How can I create fixtures that references a few foreign instances with pytest and factory-boy?

Viewed 21

I have the function that calculate the sales of each companies in month, then I want to test this function.

so I want to create some Sales fixtures of a few companies for the test. like:

  • create 2 or 3 companies fixtures
  • create many sales fixtures for these companies
  • use created sales fixtures for my aggregation function test

How can I do this? Do some methods exist for this like FuzzyXXX?

I defined factories like below, but it generates sequential company ids.

def aggregate_companies_sales_in_month():
    result = (
        Sales.objects.all()
        .annotate(year=TrunkYear('sold_at'), month=TrunkMonth('sold_at'))
        .values("year", "month", "company")
        .annotate(sales=Sum("amount"))
        .values("year", "month", "company", "sales")
    )
    return result

and models like:

class Company(Model):
    name = CharField(max_length=64)

class Sales(Model):
    sold_at = DatetimeField()
    company = ForeignKey(Company, on_delete=CASCADE)
    amount = PositiveIntegerField()

then define factories using factory-boy

class CompanyFactory(DjangoModelFactory):
    class Meta:
        model = Company
    name = faker.company()

class SalesFactory(DjangoModelFactory):
    class Meta:
        model = Sale
    sold_at = FuzzyDate(start_date=date(2022,4,1), end_date=date(2022,12,31))
    company = Subfactory(CompanyFactory) # maybe should change here?
    amount = 100

my test code is like:

@pytest.fixture
def setup(sales_factory):
    # This creates sales for 20 companies with sequential ids. 
    sales_factory.create_batch(size=20)

@pytest.mark.django_db
def test_my_aggregation_function(setup):
    # create sales fixtures here or take it as argument?
    
    actual = aggregate_companies_sales_in_month()
    ...
1 Answers

If you want to load some predefined fixtures in your test database, you can make use of Django's call_command. The following changes should allow you to load a fixture file in your test database from within the setup function:

from django.core.management import call_command

@pytest.fixture
def setup(sales_factory):
    call_command("loaddata", "path_to_fixture.json")
Related