factory_girl 创建重复值
factory_girl creates duplicate value
rails 4.1.1
rspec3.2.1
宝石文件
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.1.1'
# Use mysql as the database for Active Record
gem 'mysql2', '~> 0.3.13', :platform => :ruby
gem 'jdbc-mysql', '5.1.28', :platform => :jruby
# Use SCSS for stylesheets
gem 'sass-rails', '~> 4.0.3'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .js.coffee assets and views
gem 'coffee-rails', '~> 4.0.0'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
gem 'jquery-turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', '~> 0.4.0', group: :doc
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use unicorn as the app server
# gem 'unicorn'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
# Use debugger
# gem 'debugger', group: [:development, :test]
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin]
gem 'slim-rails'
gem 'devise'
gem 'cancan'
gem 'rakismet'
gem 'carrierwave'
gem 'rmagick', :require => false
gem 'faker', '1.0.1'
gem 'elasticsearch-model'
gem 'elasticsearch-rails'
gem 'bonsai-elasticsearch-rails'
gem 'remotipart', '~> 1.2'
gem 'kaminari'
gem 'thin'
gem 'carrierwave-aws'
group :production do
gem 'rails_12factor', '0.0.2'
end
gem 'faye-rails', '~> 2.0'
group :development, :test do
gem 'rspec-rails', '~> 3.0'
gem 'factory_girl_rails'
end
group :test do
gem 'capybara'
gem 'database_cleaner'
gem 'launchy'
gem 'selenium-webdriver'
gem "shoulda-matchers", "~> 2.2.0"
end
spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment",__FILE__)
require 'rspec/rails'
require "capybara/rspec"
require 'shoulda/matchers'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
config.include MailerMacros
config.include Capybara::DSL, :type => :feature
config.use_transactional_fixtures = false
config.before :each do
if Capybara.current_driver == :selenium
DatabaseCleaner.strategy = :truncation
else
DatabaseCleaner.strategy = :transaction
end
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
# Include Factory Girl syntax to simplify calls to factories
config.include FactoryGirl::Syntax::Methods
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
结束
factories/tickets.rb
FactoryGirl.define do
factory :ticket do
customer_name { Faker::Name.name }
customer_email Faker::Internet.email
subject Faker::Lorem.sentence
body Faker::Lorem.sentence
association :department
permalink "/tickets/token/AAA-AAA-AAA-AAA-AAA"
remote_ip "127.0.0.1"
user_agent "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:34.0) Gecko/20100101 Firefox/34.0"
referrer "http://127.0.0.1:3000/"
end
factory :spam_ticket, parent: :ticket do
body "viagra-123"
end
factory :invalid_ticket, parent: :ticket do
customer_name nil
customer_email nil
end
end
tickets_controller_spec.rb
require 'spec_helper'
describe TicketsController, :type => :controller do
let(:department) { create(:department) }
let!(:tickets) do
(1..5).collect { create(:ticket, department: department) } +
[create(:ticket, department: nil), create(:ticket, department: create(:department))]
end
let(:ticket) { tickets.first }
let(:invalid_ticket) { build(:invalid_ticket) }
let(:valid_attributes) { attributes_for(:ticket) }
let(:invalid_attributes) { attributes_for(:invalid_ticket) }
let(:spam_attributes) { attributes_for(:spam_ticket) }
describe "GET #index" do
context "not signed user" do
it "list of tickets should redirect" do
get :index
expect(response).to redirect_to(new_staff_session_path)
end
end
context 'signed as member who is allowed to view tickets applied only to his department' do
login_as 'member'
it "populates an array of tickets of a particular department" do
@current_user.update(department: ticket.department)
get :index
expect(assigns(:tickets)).to eq tickets[0...-1]
end
it "renders the :index template" do
get :index
expect(response).to render_template :index
end
end
context 'signed as admin who is allowed to view all tickets' do
login_as 'admin'
it "populates an array of tickets of all departments" do
get :index
expect(assigns(:tickets)).to match_array(tickets)
end
end
end
describe "GET #new" do
before { get :new }
it "assigns a new Ticket to @ticket" do
expect(assigns(:ticket)).to be_a_new(Ticket)
end
it "renders the :new template" do
expect(response).to render_template :new
end
end
describe "GET #edit" do
before { get :edit, token: ticket.token }
it "assigns the requested contact to @ticket" do
expect(assigns(:ticket)).to eq ticket
end
it "render the #edit template" do
expect(response).to render_template :edit
end
end
describe "POST #create" do
context "with valid attributes" do
it "saves the new ticket in the database" do
expect{
post :create, ticket: valid_attributes
}.to change(Ticket, :count).by(1)
end
it "redirects to tickets#show" do
post :create, ticket: valid_attributes
expect(response).to redirect_to show_tickets_path(assigns[:ticket].token)
end
end
context "with invalid attributes" do
before {
post :create, ticket: invalid_attributes
}
it "does not save the new ticket in the database" do
t = Logger.new(STDOUT)
t.debug tickets
t.debug '=============================================='
t.debug create(:ticket)
t.debug create(:ticket)
t.debug build(:invalid_ticket)
t.debug '=============================================='
expect(Ticket.exists?(body: invalid_ticket[:body])).to be_false
end
it "renders the :new template" do
expect(response).to render_template :new
end
end
context "with spam content" do
before { post :create, ticket: spam_attributes }
it "throws out the error" do
expect(assigns(:ticket).spam?).to be_truthy
end
it "renders the :new template" do
expect(response).to render_template :new
end
end
end
describe "PATCH #update" do
context "with valid attributes" do
it "locates the requested @ticket" do
patch :update, token: ticket.token, ticket: valid_attributes
expect(assigns(:ticket)).to eq(ticket)
end
it "changes @ticket's attributes" do
patch :update, token: ticket.token, ticket: attributes_for(:ticket, customer_name: "John Johnson")
ticket.reload
expect(ticket.customer_name).to eq("John Johnson")
end
it "redirects to the updated ticket" do
patch :update, token: ticket.token, ticket: valid_attributes
expect(response).to redirect_to(show_tickets_path(ticket.token))
end
end
context "with invalid attributes" do
it "does not change ticket's attributes" do
patch :update, token: ticket.token, ticket: attributes_for(:ticket, customer_name: nil, body: "Help!")
ticket.reload
expect(ticket.body).to_not eq("Help!")
end
it "renders the :edit template" do
patch :update, token: ticket.token, ticket: attributes_for(:invalid_ticket)
expect(response).to render_template :edit
end
end
end
describe "GET #take" do
login_as 'member'
it "assigns ticket to a particular staff member" do
xhr :get, :take, id: ticket
expect(ticket.reload.taken_tickets).to eq(@current_user)
end
end
end
我得到了意外的输出:
调试 -- : [#http://127.0.0.1:3000/], created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 353, status_id: nil, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000 /", created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 353, status_id: nil, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 353, status_id: 无, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58", updated_at: "2015 -03-26 15:14:58", department_id: 353, status_id: 无, staff_id: 200, 采取_staff_id: 无>, #http:// 127.0.0.1:3000/", created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 353, status_id: nil, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000/", created_at: " 2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 无, status_id: 无, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 354, status_id: 无, staff_id: 200, taken_staff_id: 无>]
D, [2015-03-26T11:14:58.925686 #7515] 调试 -- : =============================== ===============
D、[2015-03-26T11:14:58.934540 #7515] 调试 -- : #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58" , updated_at: "2015-03-26 15:14:58", department_id: 355, status_id: 无, staff_id: 200, taken_staff_id :无>
D, [2015-03-26T11:14:58.944844 #7515] 调试 -- : #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58" , updated_at: "2015-03-26 15:14:58", department_id: 356, status_id: 无, staff_id: 200, taken_staff_id :无>
D、[2015-03-26T11:14:58.949122 #7515] 调试 -- : #http://127.0.0.1:3000/", created_at: 无, updated_at: 无, department_id: 357, status_id: 无, staff_id: 无, taken_staff_id: 无>
D, [2015-03-26T11:14:58.949449 #7515] 调试 -- : =============================== ===============
你可以看到 3 行代码:
t.debug create(:ticket)
t.debug create(:ticket)
t.debug build(:invalid_ticket)
输出相同body: "Deserunt et accusantium totam ab autem ad."
为什么会出现以及如何解决?
因为Faker::sentence依赖于rand(6)
请看以下代码段:
2.1.5 :001 > rand(6)
=> 5
2.1.5 :002 > rand(6)
=> 3
2.1.5 :003 > rand(6)
=> 3
2.1.5 :004 > rand(6)
=> 4
2.1.5 :005 > rand(6)
=> 3
2.1.5 :006 > rand(6)
=> 1
使用 6
默认值 Faker::Lorum.sentence
使用的
获得相同种子的机会非常高。
这是Faker的来源:
def sentence(word_count = 4, supplemental = false, random_words_to_add = 6)
words(word_count + rand(random_words_to_add.to_i).to_i, supplemental).join(' ').capitalize + '.'
end
你总是得到同样的句子,这只是运气不好。
您可以做的是覆盖参数并获得更高的种子值以增加随机化。
问题出在factories/tickets.rb.
而是将 Ticket 工厂设置为
factory :ticket do
customer_name { Faker::Name.name }
customer_email Faker::Internet.email
subject Faker::Lorem.sentence
body Faker::Lorem.sentence
您应该使用大括号将值传递给属性:
customer_name { Faker::Name.name }
customer_email { Faker::Internet.email }
subject { Faker::Lorem.sentence }
body { Faker::Lorem.sentence
rails 4.1.1
rspec3.2.1
宝石文件
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.1.1'
# Use mysql as the database for Active Record
gem 'mysql2', '~> 0.3.13', :platform => :ruby
gem 'jdbc-mysql', '5.1.28', :platform => :jruby
# Use SCSS for stylesheets
gem 'sass-rails', '~> 4.0.3'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .js.coffee assets and views
gem 'coffee-rails', '~> 4.0.0'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
gem 'jquery-turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', '~> 0.4.0', group: :doc
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use unicorn as the app server
# gem 'unicorn'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
# Use debugger
# gem 'debugger', group: [:development, :test]
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin]
gem 'slim-rails'
gem 'devise'
gem 'cancan'
gem 'rakismet'
gem 'carrierwave'
gem 'rmagick', :require => false
gem 'faker', '1.0.1'
gem 'elasticsearch-model'
gem 'elasticsearch-rails'
gem 'bonsai-elasticsearch-rails'
gem 'remotipart', '~> 1.2'
gem 'kaminari'
gem 'thin'
gem 'carrierwave-aws'
group :production do
gem 'rails_12factor', '0.0.2'
end
gem 'faye-rails', '~> 2.0'
group :development, :test do
gem 'rspec-rails', '~> 3.0'
gem 'factory_girl_rails'
end
group :test do
gem 'capybara'
gem 'database_cleaner'
gem 'launchy'
gem 'selenium-webdriver'
gem "shoulda-matchers", "~> 2.2.0"
end
spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment",__FILE__)
require 'rspec/rails'
require "capybara/rspec"
require 'shoulda/matchers'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
config.include MailerMacros
config.include Capybara::DSL, :type => :feature
config.use_transactional_fixtures = false
config.before :each do
if Capybara.current_driver == :selenium
DatabaseCleaner.strategy = :truncation
else
DatabaseCleaner.strategy = :transaction
end
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
# Include Factory Girl syntax to simplify calls to factories
config.include FactoryGirl::Syntax::Methods
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
结束
factories/tickets.rb
FactoryGirl.define do
factory :ticket do
customer_name { Faker::Name.name }
customer_email Faker::Internet.email
subject Faker::Lorem.sentence
body Faker::Lorem.sentence
association :department
permalink "/tickets/token/AAA-AAA-AAA-AAA-AAA"
remote_ip "127.0.0.1"
user_agent "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:34.0) Gecko/20100101 Firefox/34.0"
referrer "http://127.0.0.1:3000/"
end
factory :spam_ticket, parent: :ticket do
body "viagra-123"
end
factory :invalid_ticket, parent: :ticket do
customer_name nil
customer_email nil
end
end
tickets_controller_spec.rb
require 'spec_helper'
describe TicketsController, :type => :controller do
let(:department) { create(:department) }
let!(:tickets) do
(1..5).collect { create(:ticket, department: department) } +
[create(:ticket, department: nil), create(:ticket, department: create(:department))]
end
let(:ticket) { tickets.first }
let(:invalid_ticket) { build(:invalid_ticket) }
let(:valid_attributes) { attributes_for(:ticket) }
let(:invalid_attributes) { attributes_for(:invalid_ticket) }
let(:spam_attributes) { attributes_for(:spam_ticket) }
describe "GET #index" do
context "not signed user" do
it "list of tickets should redirect" do
get :index
expect(response).to redirect_to(new_staff_session_path)
end
end
context 'signed as member who is allowed to view tickets applied only to his department' do
login_as 'member'
it "populates an array of tickets of a particular department" do
@current_user.update(department: ticket.department)
get :index
expect(assigns(:tickets)).to eq tickets[0...-1]
end
it "renders the :index template" do
get :index
expect(response).to render_template :index
end
end
context 'signed as admin who is allowed to view all tickets' do
login_as 'admin'
it "populates an array of tickets of all departments" do
get :index
expect(assigns(:tickets)).to match_array(tickets)
end
end
end
describe "GET #new" do
before { get :new }
it "assigns a new Ticket to @ticket" do
expect(assigns(:ticket)).to be_a_new(Ticket)
end
it "renders the :new template" do
expect(response).to render_template :new
end
end
describe "GET #edit" do
before { get :edit, token: ticket.token }
it "assigns the requested contact to @ticket" do
expect(assigns(:ticket)).to eq ticket
end
it "render the #edit template" do
expect(response).to render_template :edit
end
end
describe "POST #create" do
context "with valid attributes" do
it "saves the new ticket in the database" do
expect{
post :create, ticket: valid_attributes
}.to change(Ticket, :count).by(1)
end
it "redirects to tickets#show" do
post :create, ticket: valid_attributes
expect(response).to redirect_to show_tickets_path(assigns[:ticket].token)
end
end
context "with invalid attributes" do
before {
post :create, ticket: invalid_attributes
}
it "does not save the new ticket in the database" do
t = Logger.new(STDOUT)
t.debug tickets
t.debug '=============================================='
t.debug create(:ticket)
t.debug create(:ticket)
t.debug build(:invalid_ticket)
t.debug '=============================================='
expect(Ticket.exists?(body: invalid_ticket[:body])).to be_false
end
it "renders the :new template" do
expect(response).to render_template :new
end
end
context "with spam content" do
before { post :create, ticket: spam_attributes }
it "throws out the error" do
expect(assigns(:ticket).spam?).to be_truthy
end
it "renders the :new template" do
expect(response).to render_template :new
end
end
end
describe "PATCH #update" do
context "with valid attributes" do
it "locates the requested @ticket" do
patch :update, token: ticket.token, ticket: valid_attributes
expect(assigns(:ticket)).to eq(ticket)
end
it "changes @ticket's attributes" do
patch :update, token: ticket.token, ticket: attributes_for(:ticket, customer_name: "John Johnson")
ticket.reload
expect(ticket.customer_name).to eq("John Johnson")
end
it "redirects to the updated ticket" do
patch :update, token: ticket.token, ticket: valid_attributes
expect(response).to redirect_to(show_tickets_path(ticket.token))
end
end
context "with invalid attributes" do
it "does not change ticket's attributes" do
patch :update, token: ticket.token, ticket: attributes_for(:ticket, customer_name: nil, body: "Help!")
ticket.reload
expect(ticket.body).to_not eq("Help!")
end
it "renders the :edit template" do
patch :update, token: ticket.token, ticket: attributes_for(:invalid_ticket)
expect(response).to render_template :edit
end
end
end
describe "GET #take" do
login_as 'member'
it "assigns ticket to a particular staff member" do
xhr :get, :take, id: ticket
expect(ticket.reload.taken_tickets).to eq(@current_user)
end
end
end
我得到了意外的输出:
调试 -- : [#http://127.0.0.1:3000/], created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 353, status_id: nil, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000 /", created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 353, status_id: nil, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 353, status_id: 无, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58", updated_at: "2015 -03-26 15:14:58", department_id: 353, status_id: 无, staff_id: 200, 采取_staff_id: 无>, #http:// 127.0.0.1:3000/", created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 353, status_id: nil, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000/", created_at: " 2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 无, status_id: 无, staff_id: 200, taken_staff_id: nil>, #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58", updated_at: "2015-03-26 15:14:58", department_id: 354, status_id: 无, staff_id: 200, taken_staff_id: 无>]
D, [2015-03-26T11:14:58.925686 #7515] 调试 -- : =============================== ===============
D、[2015-03-26T11:14:58.934540 #7515] 调试 -- : #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58" , updated_at: "2015-03-26 15:14:58", department_id: 355, status_id: 无, staff_id: 200, taken_staff_id :无>
D, [2015-03-26T11:14:58.944844 #7515] 调试 -- : #http://127.0.0.1:3000/", created_at: "2015-03-26 15:14:58" , updated_at: "2015-03-26 15:14:58", department_id: 356, status_id: 无, staff_id: 200, taken_staff_id :无>
D、[2015-03-26T11:14:58.949122 #7515] 调试 -- : #http://127.0.0.1:3000/", created_at: 无, updated_at: 无, department_id: 357, status_id: 无, staff_id: 无, taken_staff_id: 无>
D, [2015-03-26T11:14:58.949449 #7515] 调试 -- : =============================== ===============
你可以看到 3 行代码:
t.debug create(:ticket)
t.debug create(:ticket)
t.debug build(:invalid_ticket)
输出相同body: "Deserunt et accusantium totam ab autem ad."
为什么会出现以及如何解决?
因为Faker::sentence依赖于rand(6)
请看以下代码段:
2.1.5 :001 > rand(6)
=> 5
2.1.5 :002 > rand(6)
=> 3
2.1.5 :003 > rand(6)
=> 3
2.1.5 :004 > rand(6)
=> 4
2.1.5 :005 > rand(6)
=> 3
2.1.5 :006 > rand(6)
=> 1
使用 6
默认值 Faker::Lorum.sentence
使用的
这是Faker的来源:
def sentence(word_count = 4, supplemental = false, random_words_to_add = 6)
words(word_count + rand(random_words_to_add.to_i).to_i, supplemental).join(' ').capitalize + '.'
end
你总是得到同样的句子,这只是运气不好。 您可以做的是覆盖参数并获得更高的种子值以增加随机化。
问题出在factories/tickets.rb.
而是将 Ticket 工厂设置为
factory :ticket do
customer_name { Faker::Name.name }
customer_email Faker::Internet.email
subject Faker::Lorem.sentence
body Faker::Lorem.sentence
您应该使用大括号将值传递给属性:
customer_name { Faker::Name.name } customer_email { Faker::Internet.email } subject { Faker::Lorem.sentence } body { Faker::Lorem.sentence