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

Factory.build fixtures #56

Open
stevelacey opened this issue Jan 20, 2018 · 8 comments
Open

Factory.build fixtures #56

stevelacey opened this issue Jan 20, 2018 · 8 comments

Comments

@stevelacey
Copy link

stevelacey commented Jan 20, 2018

Might be worth adding fixtures for creating models with build instead of create

I know this exists because I've used FactoryBot in Rails and I knew to look for it, but others might not, and it's probably worth nudging people towards writing non-db tests when they can

If I've understood correctly, currently the only way to build is to use the factory fixture and call build?

def test(asset_factory):
    asset = asset_factory.build()

Automatically registering fixtures like asset_build or similar might be a way to go. Thoughts?

@stevelacey
Copy link
Author

@olegpidsadnyi any thoughts on this? A sensible defacto way of retrieving build/unsaved models would replace a bunch of stuff in the suite I'm currently working on – and I'd rather not write a fixture for each model – is there a way to do this already or would adding a pre/suffixed fixture name be the way to go here?

@stevelacey
Copy link
Author

stevelacey commented Jan 23, 2018

A few further thoughts

  • A decorator would be passable... but I'd rather make the default not saving than making people go to more /effort/ to write db-free tests
  • Automatically toggling this based on whether the db fixture (or /a/ db fixture) is included would be neat but kind of crazy, and I have no idea how you'd do that... that'd negate the need for pre/suffixing though which would be cool
  • An alternative to the above could be a nodb fixture or something but again crazy no idea how

A suffix or a decorator to adjust how the factory works for the test in question seems like the way to go but I am wondering if we can do better

@olegpidsadnyi
Copy link
Contributor

@stevelacey it is more like what kind of base factory are you using, right? What kind of ORM is it in your case. I guess there's a difference between build and create, but if you have a non-db model, what kind of difference does it make?

Could you please describe your setup?

@stevelacey
Copy link
Author

stevelacey commented Jan 23, 2018 via email

@stevelacey
Copy link
Author

@olegpidsadnyi ☝️

@witold-gren
Copy link

witold-gren commented Mar 29, 2018

This is very good idea. I try another case, byt this is not work. Normally in FactoryBoy we have strategy in class Meta. If I set BUILD_STRATEGY and usage fixture_factory and call to them, this fixture connect with my db. This is not intuitive..

register(MyModel)

def test_something(my_model_factory):
    my_model_factory()   # <- this call db
    my_model_factory.build().   # <- this not call db

@stevelacey
Copy link
Author

stevelacey commented Mar 30, 2018 via email

@timdiels
Copy link

Wouldn't the following work? Generate these fixtures:

@pytest.fixture
def derive_factory_strategy():  # or call it factory_strategy_strategy ;)
    def derive(factory_class):
        return factory_class._meta.strategy
    return derive

@pytest.fixture
def {factory_name}__strategy(derive_factory_strategy):
    strategy = derive_factory_strategy(factory_class)
    ...

@pytest.yield_fixture
def {factory_name}({factory_name}__strategy):
    # - Setting TheFactory._meta.strategy is basically all use_strategy does.
    #   I'm not using it as I have to get the _meta.strategy so that
    #   I can reset it later and there's no public function for that that I know of.
    # - You could also use the pytest builtin monkeypatch.setattr to temporarily
    #    change the strategy.
    #   (It's necessary to change it back as the factory is the actual factory
    #   class, which isn't isolated across tests)
    default_strategy = foo_factory._meta.strategy
    foo_factory._meta.strategy = {factory_name}__strategy
    yield foo_factory
    # yield_fixture catches exceptions for us, no need for try: finally:
    foo_factory._meta.strategy = default_strategy

The user can now globally override the desired strategy, e.g. to derive from presence of a db fixture the user could write:

@pytest.fixture
def derive_factory_strategy(request):
    def derive(factory_class):
        if 'db' in request.fixturenames:
            return factory.CREATE_STRATEGY
        else:
            return factory.BUILD_STRATEGY

If a user wants to override a single FooFactory's strategy, they can override the foo_factory__strategy fixture.

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