pytest-dev / pytest-factoryboy

factory_boy integration the pytest runner
MIT License
359 stars 42 forks source link

naming model factory functions #225

Open drobert opened 3 days ago

drobert commented 3 days ago

Non-standard but supported use case in FactoryBoy is using a factory function for the 'model' definition, e.g.:

def _some_factory_func(*args, **kwargs):
  # do some cleanup/advanced logic around the args, e.g.
  if "foo" in kwargs:
    def kwargs["bar"] # contrived example
  return MyModel(*args, **kwargs)

@register
@register(_name="my_model") # has no effect
class MyModelFactory(Factory):
  class Meta:
    model: _some_factory_func # this line

  prop1 = ...
  prop2 = ...

elsewhere I have register(MyModelFactory) in a conftest.py file

What pytest sees as registered fixtures include:

This does not seem to change even if I use @register(_name="my_model"); the name ultimately renames _some_factory_func.

Similarly, named_model does not succeed as it assumes the model is a type.

Request here is to support a factory function with a model name.

youtux commented 3 days ago

We use model.__name__ to determine the fixture name.

try reassigning _ some_factory_func.__name__ = “MyModel”, it may solve your use case.

I will reopen this if unresolved.

drobert commented 1 day ago

Can you expand on your solution?

In python, you cannot set .__name__ on a top-level function, or else imports would fail.

I've attempted to wrap this one layer:

def _mk_factory():
  def factory(*args, **kwargs) -> MyModel:
    # ...
    return MyModel(...)

  factory.__name__ = "MyModel"
  return factory

renamed = _mk_factory()

class MyModelFactory(Factory):
  class Meta:
    model: renamed

But this fails in pytest with TypeError: issubclass() arg 1 must be a class. I've tried something similar using types.FunctionType(_some_factory_func.__code__, _some_factory_func.__globals__, name="MyModel") to the same effect.

Regardless, I don't feel like this ticket should necessarily be 'closed' even if this thread results in a usable syntax (which it thus far has not). At the least this behavior was non-obvious, but it also feels inconsistent. There is built-in support for this for dict using named_model; it would be nice to have a similar utility with function. It also feels awkward at best that @register(_name="...") did not have any effect when using a function.