Завод FactoryGirl, который может повторно использовать существующие модели в качестве ассоциации

Предположим, я хочу запустить тест и создать много Student. Каждый ученик принадлежит к одной школе. Названия школ даны жемчужиной Faker, но их ограниченное количество, и учащиеся связаны с этими школами.

Могу ли я использовать FactoryGirl таким образом, чтобы можно было повторно использовать существующие школы? т.е. новый FactoryGirl.create(:student) назначается либо

  • новая школа еще не подделана => школа подделана с FactoryGirl.create(:school)
  • или в уже существующую фальшивую школу => Школа уже фальсифицирована и только что получена из БД

.

class Student
  belongs_to :school, class_name: 'SchoolSociety'
end

class School
  has_many :students
  field :name
end

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

FactoryGirl.define do
  factory :student, class: Student do
    association(:school, factory: :school)
  end
  factory :school, class: School do 
    name { Faker::University.name }
  end
end

Одним из решений было бы использовать School.where(name: Faker::University.name), но я бы потерял всю гибкость фабрик FactoryGirls ... Есть ли лучший способ обхода?

Контекст проводит тесты по огурцу со многими учениками.

Редактировать:

Мой реальный случай с огурцом - это тестирование джекпота, который увеличивается в соответствии с математической формулой, включающей текущее время и количество зарегистрированных пользователей (которые зарегистрировались в школе). Я делал что-то вроде

Scenario Outline: Jackpot increases with registrations and time
    Given the current date is <date>
    And <count> students have registered for the special event
    When I am on the special event page
    Then I should see "<jackpot> €"

    Examples:
    | date | count | jackpot |
    | 2016/11/24 15:00:00 |  5 | 4 9 5 , 3 0 |
    | 2016/11/30 15:00:00 |  10 | 4 9 7 , 6 0 |
    | 2016/12/10 15:00:00 |  20 | 5 0 2 , 2 0 |
    | 2016/12/10 15:00:00 |  150 | 6 5 2 , 2 0 |

Теперь эти <count> students have registered for the special event должны быть учащимися, которые принадлежат к существующей школе (что означает, что во время процесса регистрации они должны зарегистрироваться, используя школьный адрес электронной почты, домен которого существует / сопоставим с School в нашей БД)


person Cyril Duchon-Doris    schedule 21.11.2016    source источник


Ответы (1)


При использовании Cucumber вы должны быть конкретными и точными в отношении своего Гивенса, а не позволять заводским правилам решать, что создается. Итак, если вы хотите создать несколько учеников в одной школе, я бы

Given there is a school with several students

Если вы хотите иметь несколько школ с учениками

Given there are several schools with students

И если вы хотите, чтобы ученик учился в более чем одной школе

Given there is a student enrolled in two schools

Очевидно, вы можете создать здесь большое разнообразие, выполнив такие действия, как

  1. именование школ
  2. именование студентов
  3. различные количества

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

 Given 'there is a school with several students' do 
  @school = create_school
  @students = []
  several.times do 
    @students << create_student school: school
  end
end

На этом шаге используются два метода create_school и create_student. Вы бы определили их во вспомогательном модуле

module SchoolsStepHelper
  def create_school
    ...

  def create_student(school: ...)
    ...
end
World SchoolsStepHelper

Теперь вы можете точно сказать, как вы создаете своих учеников, и когда вы сталкиваетесь с чем-то новым, например, учащийся, обучающийся в двух школах, вы можете добавлять / изменять свои методы, чтобы получить эту дополнительную функциональность, например вы могли бы добавить

def enroll_student(student: school:)
  ...

так что мы можем сделать

Given 'there is a student enrolled in two schools' do
  @student = create_student
  @school_1 = create_school
  @school_2 = create_schoo
  enroll_student(student: @student, school: @school_1)
  enroll_student(student: @student, school: @school_1)
end

Однако сейчас у нас по-прежнему слишком много кода в определениях шагов, поэтому нам нужно провести рефакторинг. Здесь у нас есть несколько вариантов

  1. Разбейте этот составной шаг на более простые шаги, чтобы ученик и школы уже существовали, например

    Учитывая, что есть школа Гарварда И есть школа Йельского университета И есть ученик Фред, и Фред учится в Йеле и Гарварде.

Вы бы сделали это так, чтобы сделать свой сценарий более наглядным, особенно при разработке функции регистрации.

  1. Извлечь метод более высокого уровня

    Учитывая, что в двух школах учится ученик, выполните @student = create_dual_enrolled_student end

Какой бы подход вы ни выбрали, вы будете повторно использовать простые методы, которые мы создали в начале этого ответа.

Теперь ваш вопрос о фабриках - это в основном детали реализации, о том, как вы создаете вещи. Должно быть возможно

  1. Реализуйте решение, которое требует небольшого понимания фабрик или вообще не требует их понимания, чтобы понять, что происходит. (просто очень простые вызовы Factory в ваших методах)

  2. Реализуйте решение, которое даже не использует фабрики (я предпочитаю этот подход, но это совсем другая история).

Наконец, если вы воспользуетесь этим подходом и все ваши определения шагов реализованы как простые вызовы, тогда не имеет значения, есть ли у вас много похожих определений шагов, например.

Given there is a student
Given Fred is a student
Given there is a student Fred
Given there is a student Sue

У вас может быть определение шага для каждого из них, не создавая дублирования, потому что выполнение вызовов не является дублированием, а стоимость дополнительных шагов практически уравновешивается простотой реализации и отсутствием параметров или регулярных выражений.

Given 'there is a student' do
  create_student
end
Given 'Fred is a student' do
  create_student name: 'Fred'
end
Given there is a student Fred do
  create_student name: Fred
end
Given 'there is a student Sue' do
  create_student name: 'Sue'
end

Уф, это был длинный ответ, надеюсь, он пригодился.

person diabolist    schedule 24.11.2016
comment
Спасибо за ответ, я отредактировал свой пост, чтобы добавить свой конкретный случай. Массовая генерация пользователей для джекпота события. - person Cyril Duchon-Doris; 24.11.2016
comment
Я бы решил ваш текущий случай, написав справочный шаг «Учитывая, что есть сотни студентов». Поскольку ваш сценарий интересует только их регистрацию на специальное мероприятие, школа не имеет значения. Чтобы сделать это быстро, я, вероятно, использовал бы дамп базы данных, Factory Girl будет делать это невероятно медленно. - person diabolist; 28.11.2016
comment
Я дополнительно уточнил свой вопрос, чтобы удалить ненужные вещи, но здесь важна школа (и фактически единственная причина задать вопрос). Студенты могут зарегистрироваться на мероприятие даже только в том случае, если они зарегистрируются в школе. Но да, я полагаю, что свалка, вероятно, лучший путь сюда ... - person Cyril Duchon-Doris; 28.11.2016
comment
Тогда школа должна быть в сценарии. Сценарии не работают, если вы не выражаете их ясно. Как бы то ни было, вы можете добраться туда с небольшим количеством предыстории, например. Учитывая, что есть школа с множеством учеников. А школа foo проводит мероприятие. Когда большинство студентов регистрируются на мероприятие. Тогда джекпот должен быть огромным. - person diabolist; 29.11.2016