首页 > 解决方案 > 使用 RSpec double 进行 Rails 测试:ActiveRecord::AssociationTypeMismatch:

问题描述

我是 RSpec 的新手(对 RoR 也不是很有经验!)

我正在开发一个有课程模型的网络应用程序。要创建新课程,我需要使用 Teacher 和 Student 对象。

我想在 Course 模型上创建一个测试,为此我尝试使用 instance_double 来模拟教师和学生。这是我的代码

require 'rails_helper'

RSpec.describe Course, type: :model do
  it 'can be created' do
    # creating the teacher
    john = instance_double(Teacher)
    allow(john).to receive(:save).and_return(true)
    allow(john).to receive(:id).and_return(1)

    # creating the student
    francois = instance_double(Student)
    allow(francois).to receive(:save).and_return(true)
    allow(francois).to receive(:id).and_return(1)

    # creating the course
    testCourse = Course.new(teacher: john, student: francois, class_language: "EN")

    # testing the course
    expect(testCourse).to be_valid
  end
end

我从 RSpec 得到下一个失败

 1) Course can be created
     Failure/Error: testCourse = Course.new(teacher: john, student: francois, class_language: "EN")
     
     ActiveRecord::AssociationTypeMismatch:
       Teacher(#70253947615480) expected, got #<Double Teacher(id: integer, email: string, encrypted_password: string, first_name: string, last_name: string, about: text, reset_password_token: string, reset_password_sent_at: datetime, remember_created_at: datetime, created_at: datetime, updated_at: datetime)> which is an instance of RSpec::Mocks::Double(#70253946301600)
     # ./spec/models/course_spec.rb:16:in `block (2 levels) in <top (required)>'

Finished in 0.02549 seconds (files took 2.87 seconds to load)
1 example, 1 failure

我的理解是 ActiveRecord 错误是由于试图将 instance_double 教师和用户与期望“真正的”教师和学生关联的课程相关联而引起的。

我一直在尝试许多不同的方法,伪造 ID(请参阅我的代码),或者自己调用模型(请参阅此处的文章

require 'student'
require 'teacher'

我看到了关于类似问题的 Stackoverflow 帖子,所以我使用了不推荐使用的语法,但它仍然不起作用。

任何想法?谢谢!

标签: ruby-on-railsrubytestingactiverecordrspec

解决方案


我真的不鼓励你expect(...).to be_valid在你的规格中使用。Rails Tutorial Book 推广的这种可怕的地毯式轰炸方法几乎没有描述验证的行为,实际上只是测试测试设置本身。IE 你真的只是在测试你的模拟,而不是你的实际应用程序!

RSpec.describe Course, type: :model do
  let(:course){ Course.new }

  it 'requires a teacher' do
    course.valid?
    expect(course.errors.details[:teacher]).to include { error: :blank }
    course.teacher_id = 1 # doesn't actually have to exist
    course.valid?
    expect(course.errors.details[:teacher]).to_not include { error: :blank }
  end
end

这写起来有点乏味,也可以使用 shoulda-matchers gem 来完成。

RSpec.describe Course, type: :model do
  it { is_expected.to validate_presence_of(:teacher) }
end

推荐阅读