使用 factory_girl 设置用于模型测试的对象

setting up objects for model testing with factory_girl

我想测试下面的示波器,但不知道如何正确设置测试对象。我什至不知道我是否应该在工厂中创建那些与某些关联或使用 let/local vars.

task.rb

belongs_to :assigner, class_name: "User"
belongs_to :executor, class_name: "User"

scope :completed, -> { where.not(completed_at: nil) }
scope :uncompleted, -> { where(completed_at: nil) }
scope :alltasks, -> (u) { where('executor_id = ? OR assigner_id = ?', u.id, u.id) }
scope :between, -> (assigner_id, executor_id) do
  where("(tasks.assigner_id = ? AND tasks.executor_id = ?) OR (tasks.assigner_id = ? AND tasks.executor_id = ?)", assigner_id, executor_id, executor_id, assigner_id)
end

工厂

factory :task do
  content { Faker::Lorem.sentence }
  deadline { Faker::Time.between(DateTime.now + 2, DateTime.now + 3) }
  association :executor, factory: :user
  association :assigner, factory: :user #I guess this is not right
end

factory :user do
  sequence(:email) { |n| "example#{n}@gmail.com" }
  password 'example0000'
  password_confirmation 'example0000'
  new_chat_notification { Faker::Number.between(0, 10) }
  new_other_notification { Faker::Number.between(0, 10) } 
end

task_spec.rb

describe Task do

  .....

  describe "scopes" do

    #I DON'T KNOW HOW TO SETUP THE TEST OBJECTS PROPERLY
    let(:uncompleted_task) { create(:task) }
    let(:completed_task) { create(:task, completed_at: DateTime.now - 2) }
    let(:user) { create(:user) 

    let(:task_1) { create(:task, executor_id: user.id, assigner_id: (user.id + 1)) }
    let(:task_2) { create(:task, assigner_id: user.id, executor_id: (user.id + 1) }
    let(:task_3) { create(:task, assigner_id: user.id, executor_id: (user.id + 2) }

    it "completed" do
      expect(completed_task).to eq(Task.completed.first)
    end

    it "uncompleted" do
      expect(uncompleted_task).to eq(Task.uncompleted.last)
    end

    it "alltasks" do

    end

    it "between" do
    end
  end
end

首先,我不喜欢在工厂里建立协会,原因如下:

  1. 如果一个工厂里有关联,那么每次一个对象created/built,关联的对象都会同时被创建。这意味着即使您只是 'build' 一个对象,也会触发许多数据库访问。您使用的关联越多,运行 测试所需的时间就越多。
  2. 你应该在真正需要的时候在测试中设置关联。如果您在工厂中设置关联,则意味着测试中隐藏了 given-when-then 过程的 'given' 部分。当测试失败时,更难找到问题所在。

要测试示波器,您应该遵循一些最佳实践:

  1. 让你的作用域尽可能简单,复杂的作用域更难测试。例如,如果需要,您应该将 between 范围分解为较小的范围。
  2. 使用最小对象来测试范围,因为我们需要在数据库中创建记录来测试范围。数据库访问会减慢测试速度,我们应该尽可能减少对象创建。对于大多数情况,两个对象(正面和负面)应该足以测试一个简单的范围。

对了,大家可能会误解let的用法,用let定义的对象只有在测试用例中调用该对象时才会创建。在您的原始规范中,没有创建任何任务或用户,因为调用了 none 个任务或用户。做你想做的事,你应该使用 before 块。

对于您的示例,我将在下面创建工厂和规范:

工厂

factory :task do
  content { Faker::Lorem.sentence }
  deadline { Faker::Time.between(DateTime.now + 2, DateTime.now + 3) }
end

factory :user do
  sequence(:email) { |n| "example#{n}@gmail.com" }
  sequence(:password) { |n| "password#{n}" }
  password_confirmation { password }
  new_chat_notification { Faker::Number.between(0, 10) }
  new_other_notification { Faker::Number.between(0, 10) }
end

规格

describe Task do
  # ...
  describe "scopes" do
    let(:executor) { create(:user) }
    let(:assigner) { create(:user) }

    describe "completeness related" do
      before(:example) do
        @uncompleted_task = create(:task, executor: executor, assigner: assigner)
        @completed_task = create(:task, executor: executor, assigner: assigner, completed_at: DateTime.now - 2))
      end

      it "completed" do
        expect(Task.completed).to include(@completed_task)
        expect(Task.completed).not_to include(@uncompleted_task)
      end

      it "uncompleted" do
        expect(Task.uncompleted).to include(@uncompleted_task)
        expect(Task.uncompleted).not_to include(@completed_task)
      end
    end

    it "alltasks" do
      me = create(:user)
      owned_task = create(:task, executor: me, assigner: assigner)
      assigned_task = create(:task, executor: executor, assigner: me)
      not_my_task = create(:task, executor: executor, assigner: assigner)
      expect(Task.alltasks(me)).to include(owned_task, assigned_task)
      expect(Task.alltasks(me)).not_to include(not_my_task)
    end

    it "between" do
      # ... (there should be as last 5 tasks to create)...
    end
  end
  # ...
end

"Rails 4 Test Prescriptions - Build a Healthy Codebase by Noel Rappin"这本书里有更多关于如何写测试的细节和点击率,有兴趣的可以看看。