Skip to main content

Define a single factory to generate the same data in multiple formats

Project description

multi_factory

Define a single factory to generate the same data in multiple formats:

  • Base (original data as defined on the factory)
  • JSON (original data converted into a Python dict that is JSON serialisable)
  • Domain (JSON data that is passed through a marshmallow schema that validates it and converts it into a domain object like a @dataclass)

Installation

multi_factory can be installed using pip (requires Python >=3.10):

pip install multi-factory

Quick start

Imagine you have the following code to represent a User in your application:

from typing import Any
from enum import Enum
from uuid import UUID
from datetime import datetime
from dataclasses import dataclass
from marshmallow import Schema, fields, post_load


class Gender(Enum):
    MALE = 1
    FEMALE = 2
    OTHER = 3


class UserSchema(Schema):
    id = fields.UUID()
    first_name = fields.String()
    last_name = fields.String()
    age = fields.Integer()
    birthday = fields.DateTime()
    gender = fields.Enum(Gender)

    @post_load
    def to_domain(self, incoming_data: dict[str, Any], **kwargs: Any) -> User:
        return User(**incoming_data)


@dataclass
class User:
    id: UUID
    first_name: str
    last_name: str
    age: int
    birthday: datetime
    gender: Gender

The above code will be used in a POST /users HTTP API endpoint, where the request body will contain a JSON representation of the User class that will need to be validated and de-serialized by the UserSchema class. The UserSchema class will also pass the validated data into the User class so it is easier to use and pass around your application.

To be able to test this POST /users endpoint, you will need to define factories to generate data for this User class in multiple formats for use in tests.

You write the following code to define multiple independent factories to achieve this:

import factory
from uuid import uuid4


class UserDictFactory(factory.Factory):
    class Meta:
        model = dict

    id = uuid4()
    first_name = "Bob"
    last_name = "Dylan"
    age = 21
    birthday = datetime(year=2000, month=1, day=1, hour=0)
    gender = Gender.MALE


class UserJSONFactory(factory.Factory):
    class Meta:
        model = dict

    id = str(uuid4())
    first_name = "Bob"
    last_name = "Dylan"
    age = 21
    birthday = datetime(year=2000, month=1, day=1, hour=0).isoformat()
    gender = Gender.MALE.name


class UserDomainFactory(factory.Factory):
    class Meta:
        model = User

    id = uuid4()
    first_name = "Bob"
    last_name = "Dylan"
    age = 21
    birthday = datetime(year=2000, month=1, day=1, hour=0)
    gender = Gender.MALE

Having to write 3 factories here adds more work that what should be necessary. It also increases the risk of these 3 factories getting out of sync if a change is made to one of them and not the other 2.

With multi-factory, we can do the following instead:

from multi_factory import JSONToDomainFactory


class UserFactory(JSONToDomainFactory[User, UserSchema]):
    id = uuid4()
    first_name = "Bob"
    last_name = "Dylan"
    age = 21
    birthday = datetime(year=2000, month=1, day=1, hour=0)
    gender = Gender.MALE

This UserFactory combines the 3 factories above into a single factory. This means you only need to define the test data once, which requires less maintenance that having the same data defined in multiple factories.

When you invoke this UserFactory you will have access to the 3 different formats for the same data:

>>> result = UserFactory()
>>> result
JSONToDomainFactoryResult(
    base={
        'id': UUID('96a43fc4-069a-4882-a388-24033299496f'), 
        'first_name': 'Bob', 
        'last_name': 'Dylan', 
        'age': 21, 
        'birthday': datetime.datetime(2000, 1, 1, 0, 0), 
        'gender': <Gender.MALE: 1>
    }, 
    json={
        'id': '96a43fc4-069a-4882-a388-24033299496f', 
        'first_name': 'Bob', 
        'last_name': 'Dylan', 
        'age': 21, 
        'birthday': '2000-01-01T00:00:00', 
        'gender': 'MALE'
    }, 
    domain=User(
        id=UUID('96a43fc4-069a-4882-a388-24033299496f'), 
        first_name='Bob', 
        last_name='Dylan', 
        age=21, 
        birthday=datetime.datetime(2000, 1, 1, 0, 0), 
        gender=<Gender.MALE: 1>
    )
)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multi_factory-0.2.1.tar.gz (9.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

multi_factory-0.2.1-py3-none-any.whl (10.9 kB view details)

Uploaded Python 3

File details

Details for the file multi_factory-0.2.1.tar.gz.

File metadata

  • Download URL: multi_factory-0.2.1.tar.gz
  • Upload date:
  • Size: 9.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.12.0

File hashes

Hashes for multi_factory-0.2.1.tar.gz
Algorithm Hash digest
SHA256 081cc2e185cd8d6146e848e9c17e8e28ee6787f9c522d20a6ace93f852b66910
MD5 ab4f726ab31421d4688f795ccba413b1
BLAKE2b-256 517217db7f17f47d5ce0bef362ba7f2531302e4dc1ab1d3156ad0169a4d4680a

See more details on using hashes here.

File details

Details for the file multi_factory-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: multi_factory-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 10.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.12.0

File hashes

Hashes for multi_factory-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 80b04b9dd1080056d32a72b9d9b9fb8f26a1f656469dffa6baaa780df07071ab
MD5 7d1f4c760ba66fe3d28ee2a39415f08b
BLAKE2b-256 27a20d927c939b76cae79ce0fcf01797f2ae5e16d0d830d972533d11d34cfca1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page