Ruby on rails 使用factory_girl设置模型测试的对象

Ruby on rails 使用factory_girl设置模型测试的对象,ruby-on-rails,rspec,factory-bot,Ruby On Rails,Rspec,Factory Bot,我想测试下面的作用域,但不知道如何正确设置测试对象。我甚至不知道我是否应该在工厂中使用一些关联或使用let/local变量创建它们 task.rb belongs_to :assigner, class_name: "User" belongs_to :executor, class_name: "User" scope :completed, -> { where.not(completed_at: nil) } scope :uncompleted, -> { where(co

我想测试下面的作用域,但不知道如何正确设置测试对象。我甚至不知道我是否应该在工厂中使用一些关联或使用let/local变量创建它们

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
任务_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

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

  • 如果工厂中存在关联,则每次创建/构建对象时,都会同时创建关联对象。这意味着即使您只是“构建”一个对象,也会触发许多数据库访问。使用的关联越多,运行测试所需的时间就越多
  • 当您确实需要时,您应该在测试中设置关联。如果您在工厂中设置关联,这意味着给定when-then流程的“给定”部分在测试中隐藏。当测试失败时,发现哪里出了问题变得更加困难
  • 要测试范围,您应该遵循以下最佳实践:

  • 使您的作用域尽可能简单,测试复杂的作用域会更加困难。例如,如果需要,您应该将
    范围划分为小范围
  • 使用最小对象来测试范围,因为我们需要在数据库中创建记录来测试范围。数据库访问减慢了测试速度,我们应该尽可能减少对象的创建。对于大多数情况,两个对象(正和负)应该足以测试一个简单的范围
  • 顺便说一句,您可能会误解
    let
    的用法,使用let定义的对象仅在测试用例中调用时创建。在原始规范中,由于未调用任何任务或用户,因此不会创建任何任务或用户。要执行所需操作,应在
    块之前使用

    例如,我将创建以下工厂和规格:

    工厂

    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
    
    规格


    Noel Rappin的《Rails 4测试处方-构建健康的代码库》一书中有更多关于如何编写测试的细节和热门内容,如果您感兴趣,值得一读

    首先,由于以下原因,我不喜欢在工厂建立协会:

  • 如果工厂中存在关联,则每次创建/构建对象时,都会同时创建关联对象。这意味着即使您只是“构建”一个对象,也会触发许多数据库访问。使用的关联越多,运行测试所需的时间就越多
  • 当您确实需要时,您应该在测试中设置关联。如果您在工厂中设置关联,这意味着给定when-then流程的“给定”部分在测试中隐藏。当测试失败时,发现哪里出了问题变得更加困难
  • 要测试范围,您应该遵循以下最佳实践:

  • 使您的作用域尽可能简单,测试复杂的作用域会更加困难。例如,如果需要,您应该将
    范围划分为小范围
  • 使用最小对象来测试范围,因为我们需要在数据库中创建记录来测试范围。数据库访问减慢了测试速度,我们应该尽可能减少对象的创建。对于大多数情况,两个对象(正和负)应该足以测试一个简单的范围
  • 顺便说一句,您可能会误解
    let
    的用法,使用let定义的对象仅在测试用例中调用时创建。在原始规范中,由于未调用任何任务或用户,因此不会创建任何任务或用户。要执行所需操作,应在
    块之前使用

    例如,我将创建以下工厂和规格:

    工厂

    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
    
    规格


    Noel Rappin的《Rails 4测试处方-构建健康的代码库》一书中有更多关于如何编写测试的细节和热门内容,如果您感兴趣,值得一读

    你看到的问题是什么?例如,当您尝试运行此代码时,您会收到什么错误消息?使用此代码时,什么都没有,但我从
    alltasks
    中删除了此代码,因为PG用户已经存在问题。如您所见,对于
    alltasks
    between
    我必须创建一些具有给定
    用户id的任务,以检查这些任务是否只返回匹配的任务。因此,我的问题是为这些方法设置适当的用户和任务对象。正如我提到的,我甚至不知道从什么方法开始。你看到的问题是什么?例如,当您尝试运行此代码时,您会收到什么错误消息?使用此代码时,什么都没有,但我从
    alltasks
    中删除了此代码,因为PG用户已经存在问题。如您所见,对于
    alltasks
    between
    我必须创建一些具有给定
    用户id的任务,以检查这些任务是否只返回匹配的任务。因此,我的问题是为这些方法设置适当的用户和任务对象。正如我所提到的,我甚至不知道该从什么方法开始。我已经阅读了使用RSpec的Rails日常测试和Rails 4测试处方的前半部分,但没有真正了解到
    let
    的确切作用。你有一篇很好的文章,比较let+工厂+以前吗?你知道问题是所有的消息来源都有不同的说法。我不明白为什么我不能使用
    let
    。我的意思是,我在
    let
    定义中使用
    create
    ,这样一旦测试调用了特定的测试,对象就会持久化。至于您的示例,您将其用于用户而不是任务,我不明白为什么。你能更详细地阐述一下你的答案吗,或者指出一个关于
    let
    的惊人来源吗?Thx
    let
    就像一个声明,
    let(:executor){create(:user)}
    不会在数据库中创建任何用户记录,直到
    @uncompleted_task=create(:task,executor:executor,assigner:assigner)
    被执行,也就是说,
    let(:executor)
    只在使用
    executor
    时创建用户记录(调用