Как правильно связать фабрику FactoryBoy с дочерним объектом - PullRequest
0 голосов
/ 04 декабря 2018

У меня есть несколько тестов, которые работали, когда я запускал их с обычными объектами базы данных, но теперь они не работают, когда я использую фабрики FactoryBoy.Я думаю, что понимаю, почему они сломаны, но я борюсь с правильным способом установить это.

Вот мои заводы:

@register
class UserFactory(BaseFactory):
    """User factory."""

    username = Sequence(lambda n: 'user{0}'.format(n))
    email = Sequence(lambda n: 'user{0}@example.com'.format(n))
    password = PostGenerationMethodCall('set_password', 'example')
    active = True

    class Meta:
        """Factory configuration."""

        model = User

@register
class ExperimentFactory(BaseFactory):
    """Experiment Factory."""

    date = fake.date_this_decade(before_today=True, after_today=False)
    scanner = Iterator(['GE', 'Sie', 'Phi'])

    class Meta:
        """Factory configuration."""
        model = Experiment

    user = factory.SubFactory(UserFactory)

Согласно этот ответ и другие примеры. Предполагается, что FactoryBoy будет обрабатывать назначение внешнего ключа за кулисами.

Но когда я пытаюсь инициализировать мой объект ExperimentFactory в моем приборе, у меня возникает проблема.

@pytest.fixture(scope='function')
@pytest.mark.usefixtures('db')
def mocked_scan_service(db, mocker, request):
    user = UserFactory(password='myprecious')
    db.session.add(user)
    num_exp, num_scans, exp_id, scan_id, exp_uri, scan_uri = request.param

    for i in range(num_exp):
        experiment = ExperimentFactory(user_id = user.id)
        db.session.add(experiment)

    db.session.commit()

    ss = ScanService(user.id, experiment.id)
    for i in range(num_scans):
        ss._add_scan_to_database()

    ss.xc.upload_scan = mocker.MagicMock()
    ss.xc.upload_scan.return_value = ('/data/archive/subjects/000001', exp_uri, scan_uri)
    mocker.spy(ss, '_generate_xnat_identifiers')
    ss.param = request.param
    return ss

Если я не передал ExperimentFactory идентификатор пользователя, я получаю эту ошибку:

TypeError: __init__() missing 1 required positional argument: 'user_id'

Вот модель;для меня имеет смысл, что фабрике нужен аргумент user_id для инициализации:

class Experiment(SurrogatePK, Model):
    """A user's experiment, during which they are scanned."""

    __tablename__ = 'experiment'
    date = Column(db.Date(), nullable=False)
    scanner = Column(db.String(80), nullable=True)
    num_scans = Column(db.Integer(), nullable=True, default=0)
    xnat_experiment_id = Column(db.String(80), nullable=True)
    xnat_uri = Column(db.String(80), nullable=True)
    user_id = reference_col('user', nullable=False)
    scans = relationship('Scan', backref='experiment')

    def __init__(self, date, scanner, user_id, **kwargs):
        """Create instance."""
        db.Model.__init__(self, date=date, scanner=scanner, user_id=user_id, **kwargs)

    def __repr__(self):
        """Represent instance as a unique string."""
        return '<Experiment({date})>'.format(date=self.date)

Но если, как написано, я явно создаю пользователя и затем передаю идентификатор пользователя, похоже, что ExperimentFactory в конечном итоге перезаписываетвнешний ключ с подфабрикой, которую он сгенерировал.Итак, позже, когда я инициализирую объект с именем ScanService, который должен быть инициализирован с помощью user_id и и analysis_id, мои тесты не пройдены по одной из двух причин.Либо я инициализирую его с помощью user_id моего явно созданного пользователя, и мои тесты не пройдены, потому что они не находят никаких родственных экспериментов в эксперименте, которому принадлежит Experin_id, или я инициализирую его с экспериментом.user.id, и мои тесты не выполняются, потому чтоони ожидают одного пользователя в базе данных, а на самом деле их два.Эту последнюю проблему довольно легко обойти, переписав мои тесты, но это кажется неопрятным и неясным.Как я должен инициализировать ExperimentFactory, когда для экспериментальной модели требуется user_id для инициализации?

1 Ответ

0 голосов
/ 05 декабря 2018

Если у кого-то есть лучшее решение, не стесняйтесь комментировать, но вот что я понял: действительно не имеет значения, что я передаю для user_id;Я просто должен передать что-то, чтобы инициализация модели не провалилась.И одновременная передача user=user создает нужную мне ситуацию: все эксперименты принадлежат одному и тому же пользователю.Теперь все мои тесты пройдены.Вот модифицированный код прибора.все остальное осталось прежним:

@pytest.fixture(scope='function')
@pytest.mark.usefixtures('db')
def mocked_scan_service(db, mocker, request):
    num_exp, num_scans, exp_id, scan_id, exp_uri, scan_uri = request.param

    user = UserFactory(password='myprecious')

    for i in range(num_exp):
        experiment = ExperimentFactory(user_id=user.id, user=user)
        db.session.add(experiment)

    db.session.commit()

    ss = ScanService(experiment.user.id, experiment.id)
    for i in range(num_scans):
        ss._add_scan_to_database()

    ss.xc.upload_scan = mocker.MagicMock()
    ss.xc.upload_scan.return_value = ('/data/archive/subjects/000001', exp_uri, scan_uri)
    mocker.spy(ss, '_generate_xnat_identifiers')
    ss.param = request.param
    return ss
...