pythonfactory-boy

FactoryBoy: use a factory method on the model instead of __init__


I have a class, for which I would like to write a factory using FactoryBoy. However, this class doesn't produce its instances using the __init__ method, but rather a number of factory methods:

class MyClass:
    def __init__():
         self.field = None
         self.is_valid = False

    @staticmethod
    def from_str(arg):
        inst = MyClass()
        inst.field = arg
        inst.is_valid = True
        return inst

Is there a way to write a FactoryBoy factory such that it uses this static factory method instead of passing the fields to MyClass initialiser?


Solution

  • The way to do this is to override the _build(...) or _create(...) classmethod on your Factory subclass.

    Assuming the arg in your class is described by the text parameter of your factory:

    class MyFactory(factory.Factory):
      class Meta:
        model = MyClass
    
      @classmethod
      def _create(cls, model_class, *args, text, **kwargs):
        return model_class.from_string(text)
    
      text = factory.Sequence("some-input:{}".format)
    

    With that code, calling MyFactory() would call MyClass.from_string("some-input:0"); and calling MyFactory(text="some-data") will return the value from MyClass.from_string("some-data").

    If you have lots of classes using that same mechanism, you may even add a custom abstract factory to share that def _create declaration:

    class FromStringFactory(factory.Factory):
      class Meta:
        abstract = True
    
      @classmethod
      def _create(cls, model_class, *args, text, **kwargs):
        return model_class.from_string(text, **kwargs)
    
    class SomeClassFactory(FromStringFactory):
      class Meta:
        model = SomeClass
    
      text = factory.Faker("country")