How to add a factory for a portal model#
FairDM’s SampleFactory and MeasurementFactory are abstract, because the base Sample and Measurement cannot be created. You add a factory for each sample or measurement type your portal defines, so tests and development data can create records of it with one call.
Before you start#
The type exists and is registered. See How to add a sample type or How to add a measurement type.
The examples use a sample type
PlantSamplewith the fieldsspecies,collected_onanddry_weight_g, and a measurement typeChlorophyllMeasurementwith areadingfield. Both are in an app calledplants. Use your own names.
Steps#
Create
plants/factories.py. Keep it outsidetests/if you want thegenerate_fake_datacommand to use it.Subclass
SampleFactory, name the model inMeta, and give a value to each field the model requires. The factory already fills inname,local_id,statusanddataset.import factory from fairdm.factories import MeasurementFactory, SampleFactory from .models import ChlorophyllMeasurement, PlantSample class PlantSampleFactory(SampleFactory): class Meta: model = PlantSample species = factory.Faker( "random_element", elements=["Quercus robur", "Fagus sylvatica", "Pinus sylvestris"], ) collected_on = factory.Faker("date_between", start_date="-2y", end_date="today") dry_weight_g = factory.Faker( "pyfloat", left_digits=2, right_digits=1, min_value=1, max_value=99 )
A field that has a default, or may be blank or null, can be left out. A required field you leave out makes
create()fail with the database’s error.Subclass
MeasurementFactoryfor a measurement type. It givessampleno default, so declare one. Build the sample in the same dataset as the measurement.class ChlorophyllMeasurementFactory(MeasurementFactory): class Meta: model = ChlorophyllMeasurement sample = factory.SubFactory( PlantSampleFactory, dataset=factory.SelfAttribute("..dataset") ) reading = factory.Faker("pydecimal", left_digits=2, right_digits=2, positive=True)
SelfAttribute("..dataset")readsdatasetfrom the measurement being built. A test that passes its owndatasetorsamplereplaces the default.Optionally, add a trait for a scenario you test often. A trait switches on with a keyword argument.
class PlantSampleFactory(SampleFactory): ... class Params: heavy = factory.Trait(dry_weight_g=95.0)
Use the factory in a test.
import pytest from plants.factories import ChlorophyllMeasurementFactory, PlantSampleFactory @pytest.mark.django_db def test_a_measurement_is_made_on_a_sample_of_its_dataset(): measurement = ChlorophyllMeasurementFactory() assert measurement.sample.dataset == measurement.dataset @pytest.mark.django_db def test_a_sample_with_descriptions(): sample = PlantSampleFactory(heavy=True, descriptions=2) assert sample.dry_weight_g == 95.0 assert sample.descriptions.count() == 2
Optionally, list the factories in the
FAIRDM_FACTORIESsetting sogenerate_fake_datacreates records of the type. The setting maps a model label to the dotted path of its factory, and is described in Settings.FAIRDM_FACTORIES = { "plants.PlantSample": "plants.factories.PlantSampleFactory", "plants.ChlorophyllMeasurement": "plants.factories.ChlorophyllMeasurementFactory", }
The result#
PlantSampleFactory() creates a saved sample in a new private dataset, and ChlorophyllMeasurementFactory() creates a measurement on a sample of its own dataset. Both take the arguments every record factory takes, such as descriptions=2 and dates=1. They are listed in the factories reference.
How to test a portal with FairDM factories shows the factories in use. The generate_fake_data command picks one of the listed factories at random for each sample or measurement it creates.