如何使用 rspec 和 factorygirl 测试 invalid_attribute

How to test invalid_attribute using rspec and factorygirl

我正在学习如何在 rails 上进行测试 tutorial

教程的一部分显示了如何编写 invalid_attribute 测试:

require 'rails_helper'

RSpec.describe ContactsController, type: :controller do

  describe "POST #create" do
    context "with valid attributes" do
      it "create new contact" do
        post :create, contact: attributes_for(:contact)
        expect(Contact.count).to eq(1)
      end
    end

    context "with invalid attributes" do
      it "does not create new contact" do
        post :create, contact: attributes_for(:invalid_contact)
        expect(Contact.count).to eq(0)
      end
    end
  end
end

我不明白 :contact:invalid_contact 指向哪里。

:contact是否指向Contactclass?好像是FactoryGirl's gh.如果是这样,那我怎么创建:invalid_contact因为没有:invalid_contactclass?

我已经尝试了post :create, contact: attributes_for(:contact, :full_name => nil),但还是失败了。

spec/factories/contacts.rb:

FactoryGirl.define do
  factory :contact do
    full_name     { Faker::Name.name }
    email         { Faker::Internet.email }
    phone_number  { Faker::PhoneNumber.phone_number }
    address       { Faker::Address.street_address }
  end
end

第一次测试,with valid attributes 通过。在模型上,有存在验证 validates_presence_of :full_name, :email, :phone_number, :address。我要添加什么才能通过 "with invalid attributes" 测试?

工厂将使用同名的class。所以你的 :contact 工厂将使用 Contact class。您可以通过指定要使用的 class 来为无效联系人创建新工厂。

factory :invalid_contact, class: Contact do
  full_name nil
end

也可以使用特征来避免拥有两个不同的工厂。

FactoryGirl.define do
  factory :contact do
    full_name     { Faker::Name.name }
    email         { Faker::Internet.email }
    phone_number  { Faker::PhoneNumber.phone_number }
    address       { Faker::Address.street_address }

    trait :invalid do
      full_name nil
    end
  end
end

然后与attributes_for(:contact, :invalid)

一起使用

您 link 的教程说:

Following the spec above, write a spec that uses invalid attributes to create a new contact. This spec should check that the contact is not created.

因此您需要弄清楚如何使用 :contact 的示例来测试 :invalid_contact

您只需在规范中添加 let

Use let to define a memoized helper method. The value will be cached across multiple calls in the same example but not across examples.

Source: https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/helper-methods/let-and-let

那么您的控制器规格将如下所示:

...
let(:invalid_contact) { create(:contact, name: nil) }

context "with invalid attributes" do
  it "does not create new contact" do
    post :create, contact: attributes_for(invalid_contact)
    expect(Contact.count).to eq(0)
  end
end
...

这样 #post 动作 params 是从 invalid_contact

中提取的

或者正如@fanta 在评论中建议的那样,您可以向您的工厂添加一个 trait。我更喜欢我的方法,因为其他人看你的代码就会知道为什么 invalid_contact 应该无效而不看 :contacts 工厂