使用 rspec 测试依赖模型
Testing dependent models with rspec
我对 ruby 和 rspec 比较陌生,想知道如何解决以下情况或者处理类似情况的首选方法是什么。为了简单起见,假设我有以下模型:
class Products
has_one :image
has_one :category
def create_document
{
title: title,
image: image.url unless image.nil?,
category: category.name unless category.nil?,
}
end
end
class Images
belongs_to :product
end
class Categories
belongs_to :product
end
我要测试四种情况:
describe Product do
# create all four products
fixtures :products, :images, :categories
it 'returns title' do
# create only product
end
it 'returns title and category' do
# create category
end
it 'returns title, image and category' do
# create image
end
it 'returns title and image' do
# remove category
end
end
我的问题是所有固定装置都在我的测试开始执行之前创建。这意味着我可以创建 4 种产品(我知道如何解决)并分别测试它们中的每一种,或者创建一种产品并在我进行测试时创建图像和类别(这对我来说仍然是一个难题而且我不不知道如何解决)。处理这样的测试最好的方法是什么?
这是我的spec_helper.rb
ENV['RAILS_ENV'] = 'test'
require_relative '../config/environment.rb'
require 'rspec/rails'
require 'rack/utils'
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
RSpec.configure do |config|
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
config.expect_with :rspec do |c|
c.syntax = [:should, :expect]
end
config.before(:each) do
Rails.cache.clear rescue nil
Time.zone = 'UTC'
end
# define the factories
require 'factory_girl'
# configure fixture options
config.fixture_path = "#{Rails.root}/spec/fixtures/"
config.use_transactional_fixtures = true
# Build the fixtures
require_relative 'support/fixture_builder'
end
这是我的 fixture_builder.rb
require 'fixture_builder'
FixtureBuilder.configure do |fbuilder|
fbuilder.files_to_check += Dir["spec/factories/**/*.rb", "spec/support/fixture_builder.rb"]
fbuilder.factory do
load(Rails.root.join('db/seeds.rb'))
product_one = fbuilder.name(:product_one, FactoryGirl.create(:product, title: 'product 1')).first
fbuilder.name(:image_one, FactoryGirl.create(:image, product_id: product_one.id, url: 'product 1 image url'))
fbuilder.name(:category_one, FactoryGirl.create(:category, product_id: product_one.id, name: 'product 1 category'))
end
end
还有我的工厂
FactoryGirl.define do
factory :product, class: Product do
end
end
FactoryGirl.define do
factory :image, class: Image do
end
end
FactoryGirl.define do
factory :category, class: Category do
end
end
FactoryGirl 用作固定装置的替代品。因此,您不使用 FactoryGirl 的 fixtures 方法,而是它有自己的 DSL:
FactoryGirl.create(:product) # Saves a product to the database
FactoryGirl.build_stubbed(:product) # Builds a product and makes it look look like it has been persisted
FactoryGirl.attributes_for(:product)
# etc
您可以通过在规格中包含 FactoryGirl::Syntax::Methods
来设置快捷方式:
RSpec.configure do |config|
# ..
config.include FactoryGirl::Syntax::Methods
# ..
end
然后您可以使用 rspecs let
和 let!
方法在您的规范中使用工厂。
describe "Let methods and factories" do
let(:product) { create(:product) } # initialized when it is used
let!(:foo) { create(:foo) } # initialized before any specs are run
describe "let" do
expect(product).to be_a Product
end
end
database_cleaner gem 对于规范之间的清理非常宝贵,可以避免任何测试顺序问题或误报。
在您的情况下,您可能希望直接为每个测试用例使用工厂。
describe Product do
# It is a good practice to put examples in a describe block
# which tells which method we are testing
describe "#create_document" do
it 'returns title' do
product = build_stubbed(:product, title: build_stubbed(:title))
expect(product.create_document()[:title]).to be_a Title
end
it 'returns title and category' do
product = build_stubbed(:product, category: build_stubbed(:category))
expect(product.create_document()[:category]).to be_a Category
end
#..
end
end
我对 ruby 和 rspec 比较陌生,想知道如何解决以下情况或者处理类似情况的首选方法是什么。为了简单起见,假设我有以下模型:
class Products
has_one :image
has_one :category
def create_document
{
title: title,
image: image.url unless image.nil?,
category: category.name unless category.nil?,
}
end
end
class Images
belongs_to :product
end
class Categories
belongs_to :product
end
我要测试四种情况:
describe Product do
# create all four products
fixtures :products, :images, :categories
it 'returns title' do
# create only product
end
it 'returns title and category' do
# create category
end
it 'returns title, image and category' do
# create image
end
it 'returns title and image' do
# remove category
end
end
我的问题是所有固定装置都在我的测试开始执行之前创建。这意味着我可以创建 4 种产品(我知道如何解决)并分别测试它们中的每一种,或者创建一种产品并在我进行测试时创建图像和类别(这对我来说仍然是一个难题而且我不不知道如何解决)。处理这样的测试最好的方法是什么?
这是我的spec_helper.rb
ENV['RAILS_ENV'] = 'test'
require_relative '../config/environment.rb'
require 'rspec/rails'
require 'rack/utils'
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
RSpec.configure do |config|
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
config.expect_with :rspec do |c|
c.syntax = [:should, :expect]
end
config.before(:each) do
Rails.cache.clear rescue nil
Time.zone = 'UTC'
end
# define the factories
require 'factory_girl'
# configure fixture options
config.fixture_path = "#{Rails.root}/spec/fixtures/"
config.use_transactional_fixtures = true
# Build the fixtures
require_relative 'support/fixture_builder'
end
这是我的 fixture_builder.rb
require 'fixture_builder'
FixtureBuilder.configure do |fbuilder|
fbuilder.files_to_check += Dir["spec/factories/**/*.rb", "spec/support/fixture_builder.rb"]
fbuilder.factory do
load(Rails.root.join('db/seeds.rb'))
product_one = fbuilder.name(:product_one, FactoryGirl.create(:product, title: 'product 1')).first
fbuilder.name(:image_one, FactoryGirl.create(:image, product_id: product_one.id, url: 'product 1 image url'))
fbuilder.name(:category_one, FactoryGirl.create(:category, product_id: product_one.id, name: 'product 1 category'))
end
end
还有我的工厂
FactoryGirl.define do
factory :product, class: Product do
end
end
FactoryGirl.define do
factory :image, class: Image do
end
end
FactoryGirl.define do
factory :category, class: Category do
end
end
FactoryGirl 用作固定装置的替代品。因此,您不使用 FactoryGirl 的 fixtures 方法,而是它有自己的 DSL:
FactoryGirl.create(:product) # Saves a product to the database
FactoryGirl.build_stubbed(:product) # Builds a product and makes it look look like it has been persisted
FactoryGirl.attributes_for(:product)
# etc
您可以通过在规格中包含 FactoryGirl::Syntax::Methods
来设置快捷方式:
RSpec.configure do |config|
# ..
config.include FactoryGirl::Syntax::Methods
# ..
end
然后您可以使用 rspecs let
和 let!
方法在您的规范中使用工厂。
describe "Let methods and factories" do
let(:product) { create(:product) } # initialized when it is used
let!(:foo) { create(:foo) } # initialized before any specs are run
describe "let" do
expect(product).to be_a Product
end
end
database_cleaner gem 对于规范之间的清理非常宝贵,可以避免任何测试顺序问题或误报。
在您的情况下,您可能希望直接为每个测试用例使用工厂。
describe Product do
# It is a good practice to put examples in a describe block
# which tells which method we are testing
describe "#create_document" do
it 'returns title' do
product = build_stubbed(:product, title: build_stubbed(:title))
expect(product.create_document()[:title]).to be_a Title
end
it 'returns title and category' do
product = build_stubbed(:product, category: build_stubbed(:category))
expect(product.create_document()[:category]).to be_a Category
end
#..
end
end