Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using factory.List resolves in unusable fixtures #67

Open
sobolevn opened this issue Jul 21, 2018 · 7 comments
Open

Using factory.List resolves in unusable fixtures #67

sobolevn opened this issue Jul 21, 2018 · 7 comments

Comments

@sobolevn
Copy link
Member

Setup

Here are my models:

from typing import List

from mypy_extensions import TypedDict


class WakatimeCommit(TypedDict):
    """Class to represent a single Wakatime commit instance from API. """

    hash: str
    author_email: str


class WakatimeCommitsResponse(TypedDict):
    """Class to represent Wakatime response from commits API."""

    commits: List[WakatimeCommit]

And my factories:

import factory
from pytest_factoryboy import register

@register
class WakatimeCommitFactory(factory.BaseDictFactory):
    """
    Fake factory for a single Wakatime commit entry.

    Note:
        This class is marked as protected and is not registered,
        since we do not actually need a single commit in tests.

    """

    class Meta(object):
        model = WakatimeCommit

    hash = '123abc'
    author_email = 'some@email.com'


@register
class WakatimeCommitsResponseFactory(factory.BaseDictFactory):
    """Fake factory for Wakatime `/commits` response."""

    class Meta(object):
        model = WakatimeCommitsResponse

    commits = factory.List([
        factory.SubFactory(WakatimeCommitFactory) for _ in range(10)
    ])

And finally my test:

def test_pipeline_for_opened_valid_mr(
    wakatime_commits_response,
):
    """
    Tests the whole process of adding spent time to a merge request.

    All requests are mocked, everything returns valid responses.
    """
    print(wakatime_commits_response)

    assert 1 == 2

Error

This tests fails due to unresolved fixtures:

def test_pipeline_for_opened_valid_mr(
file <string>, line 2: source code not available
file <string>, line 2: source code not available
E       fixture 'list' not found
>       available fixtures:
...
wakatime_commit, wakatime_commit__author_email, wakatime_commit__hash, wakatime_commit__total_seconds, wakatime_commit_factory, wakatime_commits_response, wakatime_commits_response__commits, wakatime_commits_response_factory
...

I guess this happens due to the fact that I am using factory.List.

Workaround

class WakatimeCommitsResponseFactory(factory.BaseDictFactory):
    ...

    @factory.post_generation
    def commits(self, create, extracted, **kwargs):
        if extracted:
            self['commits'] = extracted
        else:
            self['commits'] = []
            for _ in range(10):
                self['commits'].append(WakatimeCommitFactory.build())
@skarzi
Copy link
Contributor

skarzi commented Jun 30, 2019

I can take this issue as well as #65, because it affected me several times.
Could someone give me some directions firstly?
I am still looking for the clearest solution for that

@sobolevn
Copy link
Member Author

Awesome, @skarzi! I would appreciate a fix. Sadly, I am not familiar with the codebase.

@skarzi
Copy link
Contributor

skarzi commented Aug 1, 2020

Currently it's really hard to implement factory.declarations.List and factory.declarations.Dict support in a proper way, because of few reasons:

  • both classes are SubFactory subclasses with factory_class set to concrete, but not registered by pytest-factoryboy factories
  • factory.base.ListFactory model is set to list and factory.base.DictFactory is set to dict so by default pytest-factoryboy expects to find list and dict fixtures, we can register such fixtures but this will override builtin list and dict in some places.
  • factory.declarations.List is almost always used with params (saved in defaults attribute on instances of ParameteredAttribute subclasses), but params aren't supported by pytest-factoryboy - this is connected with following issues LazyFixture ignores Subfactory attribute assignment #40, Duplicate Subfactory not working, reusing existing object #90 - but in my opinion following code is the simplest example of this issue:
from dataclasses import dataclass 

import factory
from pytest_factoryboy import LazyFixture, register


@dataclass
class Author:
    full_name: str


@dataclass
class Book:
    author: Author
    title: str


class AuthorFactory(factory.Factory):
    full_name = factory.Sequence(lambda counter: f'Author {counter}')

    class Meta:
        model = Author


class LemonySnicketBookFactory(factory.Factory):
    author = factory.SubFactory(AuthorFactory, full_name='Lemony Snicket')
    title = factory.Sequence(lambda counter: f'A Series of Unfortunate Events {counter}')

    class Meta:
        model = Book


register(AuthorFactory)
register(LemonySnicketBookFactory, _name='lemony_snicket_book')


def test_subfactory_defaults(lemony_snicket_book):
    """Failure because ``lemony_snicket_book.author.full_name`` is ``'Author 1'``"""
    assert lemony_snicket_book.author.full_name == 'Lemony Snicket'

So with my current knowledge, I see 2 possible solutions:

  • firstly we need to add some support of params into the codebase and then add some more logic related to factory.declarations.List and factory.declarations.Dict somewhere in register function
  • add totally custom fixtures implementations for factory.declarations.List and factory.declarations.Dict for instance, generate unique fixture for every instance of these classes

I am still not so much into pytest-factoryboy and factoryboy codebase, because it's really dynamic and sometimes hard to debug code, so any responses with feedback or any advice is really welcome!

@skarzi
Copy link
Contributor

skarzi commented Dec 28, 2020

@youtux @hugovk maybe do you have any ideas/insights about this issue and the comment I have added above?

@lovetoburnswhen
Copy link

@skarzi did you ever get around to a fix?

@skarzi
Copy link
Contributor

skarzi commented Oct 27, 2021

Unfortunately not fully :(

It would be great to discuss this issue with some more experienced pytest-factoryboy developers

@ryancausey
Copy link

I just ran into this issue. Is there any idea on how to solve this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants