FactoryGirl工厂可以将现有模型重新用作关联

时间:2016-11-21 23:00:24

标签: ruby-on-rails cucumber factory-bot ruby-on-rails-5 faker

假设我想运行测试并创建许多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工厂的所有灵活性...... 有没有更好的解决方法?

上下文正在与许多学生一起运行Cucumber测试

编辑:

我的实际黄瓜案例是根据涉及当前时间和注册用户数量(在学校注册)的数学公式测试累积奖金。我正在做一些像

这样的事情
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 in我们的DB)

1 个答案:

答案 0 :(得分:0)

在使用黄瓜而不是让工厂规则决定创建的内容时,您应该具体而准确地了解您的Givens。所以,如果你想在同一所学校创建几个学生,我会有

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. 变化数量
  4. 当您实施这些步骤时,特别是当您开始时,它很有诱惑力将所有代码放在步骤中完成工作。抵制这一点,而不是在步骤中进行调用,并使调用与步骤描述匹配。例如

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

    此步骤使用两种方法create_schoolcreate_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. 将这个复合步骤分解为更简单的步骤,以便学生和学校已经存在,例如

      鉴于有哈佛学校 还有耶鲁学校 还有一个学生弗雷德 弗雷德就读于耶鲁大学和哈佛大学。

    2. 您可以通过这种方式使您的方案更具描述性,尤其是在开发注册功能时

      1. 提取更高级别的方法

        鉴于'有一所学生就读于两所学校'   @student = create_dual_enrolled_student 端

      2. 您选择哪种方法,您将重复使用我们在本答案开头创建的简单方法。

        现在关于工厂的问题基本上是一个实现细节,关于你如何创造事物。应该可以

        1. 实施一个解决方案,需要很少或根本不了解工厂,以了解发生的事情。 (只是非常简单的工厂调用你的方法)

        2. 实施一个甚至不使用工厂的解决方案(这是我赞成的方法,但这是另一个故事)。

        3. 最后,如果您采用这种方法并且所有步骤定义都是作为简单调用实现的,那么如果您有许多类似的步骤定义就没关系,例如。

          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
          

          Phew这是一个很长的答案,我希望它有用。