factory_girl创建重复值

时间:2015-03-26 14:55:08

标签: ruby-on-rails rspec factory-bot faker

导轨4.1.1
rspec 3.2.1

Gemfile

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.

结束

工厂/ 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:// 122.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&gt ;, #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:// 122.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&gt ;, #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&# 34;,department_id:353,status_id:nil,staff_id:200,taken_staff_id:nil&gt ;, #http://127.0.0.1:3000 /" ,created_at:" 2015-03-26 15:14:58",updated_at:" 2015-03-26 15:14:58",department_id:nil,status_id:nil,staff_id :200,taken_staff_id:nil&gt ;, #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:nil,staff_id:200,taken_staff_id:nil>] D,[2015-03-26T11:14:58.925686#7515]调试 - :=============================== =============== D,[2015-03-26T11:14:58.934540#7515] DEBUG - :#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:nil,staff_id:200,taken_staff_id:nil> D,[2015-03-26T11:14:58.944844#7515] DEBUG - :#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:nil,staff_id:200,taken_staff_id:nil> D,[2015-03-26T11:14:58.949122#7515] DEBUG - :#http://127.0.0.1:3000 /",created_at:nil,updated_at:nil,department_id:357,status_id:nil ,staff_id:nil,taken_staff_id:nil> 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."

为什么会出现以及如何修复?

2 个答案:

答案 0 :(得分:0)

因为Faker ::句子依赖于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

运气不好,你继续得到同样的句子。 您可以做的是覆盖参数并获得更高的种子值以增加随机化。

答案 1 :(得分:0)

问题出在 factories / tickets.rb 而是像

那样设置Ticket factory
  

工厂:机票做       customer_name {Faker :: Name.name}
      customer_email Faker :: Internet.email
      主题Faker :: Lorem.sentence
      身体Faker :: Lorem.sentence

您应该使用大括号将值传递给属性:

customer_name { Faker::Name.name }
customer_email { Faker::Internet.email }
subject { Faker::Lorem.sentence }
body { Faker::Lorem.sentence