1

我正在创建一个 Rails 应用程序(这方面相当新),并且我有一个不使用脚手架生成的客户端模型。我已经运行了“rake db:migrate”,并且正在使用“rake test:units”对模型进行单元测试,但是我在终端中得到了以下运行时错误的变体,几乎用于我所有的工厂测试。

test: Creating seven clients should show that all factories are properly created. (ClientTest):
NoMethodError: undefined method `destroy' for nil:NilClass
    /Users/myUserName/Desktop/app_name/test/unit/client_test.rb:131:in `block (2 levels) in <class:ClientTest>'

我一直无法弄清楚错误是什么。我知道它无法识别客户端类,因此找不到 nil 的“销毁”方法。但是,我不确定如何解决它。

下面是我的模型代码,位于 app_name/app/models/client.rb

class Client < ActiveRecord::Base
  # Callbacks
  before_save :reformat_phone

  # Relationships
  has_many :assignments
  has_many :counselors, :through => :assignments
  has_many :interventions, :through => :assignments

  # Validations
  validates_presence_of :first_name, :last_name, :gender, :address, :city, :state, :zip, :phone, :active
  validates_inclusion_of :gender, :in => %w[male female], :message => "is not an option"
  validates_inclusion_of :marital_status, :in => %w[single married separated divorced], :message => "is not an option"
  validates_inclusion_of :state, :in => %w[PA OH WV], :message => "is not an option"
  validates_format_of :zip, :with => /^\d{5}$/, :message => "should be five digits long"
  validates_format_of :phone, :with => /^\(?\d{3}\)?[-. ]?\d{3}[-.]?\d{4}$/, :message => "should be 10 digits (area code needed) and delimited with dashes only"

  # Scopes
  scope :active, where('active = ?', true)
  scope :inactive, where('active = ?', false)
  scope :alphabetical, order('last_name, first_name')

  scope :receiving_gov_assistance, where('gov_assistance = ?', true)
  scope :not_receiving_gov_assistance, where('gov_assistance = ?', false)

  scope :male, where('gender = ?', 'male')
  scope :female, where('gender = ?', 'female')

  scope :by_marital_status, lambda { |status| where("marital_status = ?", status) }
  scope :by_ethnicity, lambda { |race| where("ethnicity = ?", race) }

  scope :employed, where('is_employed = ?', true)
  scope :unemployed, where('is_employed = ?', false)

  scope :veteran, where('is_veteran = ?', true)

  scope :assigned, where('current_assignment != ?', nil)
  scope :unassigned, where('current_assignment = ?', nil) 

# Other methods
  def name
    "#{last_name}, #{first_name}"
  end

  def proper_name
    "#{first_name} #{last_name}"
  end

  def current_assignment
    curr_assignment = self.assignments.select{|a| a.end_date.nil?}
    # alternative method for finding current assignment is to use scope 'current' in assignments:
    # curr_assignment = self.assignments.current    # will also return an array of current assignments
    return nil if curr_assignment.empty?
    curr_assignment.first   # return as a single object, not an array
  end

  # Misc Constants
  GENDER_LIST = [['Male', 'male'],['Female', 'female']]
  STATES_LIST = [['Ohio', 'OH'],['Pennsylvania', 'PA'],['West Virginia', 'WV']]

  # Callback code
  # -----------------------------
  private
  def reformat_phone
    phone = self.phone.to_s  # change to string in case input as all numbers 
    phone.gsub!(/[^0-9]/,"") # strip all non-digits
    self.phone = phone       # reset self.phone to new string
  end
end

这是我编写的测试,位于 app_name/test/unit/client_test.rb

require 'test_helper'

class ClientTest < ActiveSupport::TestCase
  # Test relationships
  should have_many(:assignments)
  should have_many(:deacons).through(:assignments)
  should have_many(:interventions).through(:assignments)

  # Test basic validations
  should validate_presence_of(:last_name)
  should validate_presence_of(:first_name)
  should validate_presence_of(:gender)
  should validate_presence_of(:address)
  should validate_presence_of(:city)
  should validate_presence_of(:state)
  should validate_presence_of(:zip)
  should validate_presence_of(:phone)
  should validate_presence_of(:active)

  # Identity-based tests
      # tests for gender
      should allow_value("male").for(:gender)
      should allow_value("female").for(:gender)

      should_not allow_value(nil).for(:gender)
      should_not allow_value(1).for(:gender)
      should_not allow_value("seahorse").for(:gender)
      should_not allow_value("I believe gender is a societal construct.").for(:gender)

      # tests for ethnicity
      should allow_value("Asian").for(:ethnicity)
      should allow_value("Black").for(:ethnicity)
      should allow_value("Hispanic").for(:ethnicity)
      should allow_value("Latino").for(:ethnicity)
      should allow_value("Native American").for(:ethnicity)
      should allow_value("White").for(:ethnicity)

      should_not allow_value(nil).for(:ethnicity)
      should_not allow_value(1).for(:ethnicity)
      should_not allow_value(true).for(:ethnicity)
      should_not allow_value(0.5).for(:ethnicity)

      # tests for marital status
      should allow_value("single").for(:marital_status)
      should allow_value("married").for(:marital_status)
      should allow_value("separated").for(:marital_status)
      should allow_value("divorced").for(:marital_status)

      should_not allow_value("White").for(:marital_status)
      should_not allow_value(nil).for(:marital_status)
      should_not allow_value(1).for(:marital_status)
      should_not allow_value(true).for(:marital_status)
      should_not allow_value("I believe marriage is a societal construct.").for(:marital_status)

  # Contact-based Tests
      # tests for address
      should allow_value("123 Example Lane").for(:address)
      should allow_value("456 Another Street").for(:address)

      should_not allow_value(true).for(:address)
      should_not allow_value(101).for(:address)
      should_not allow_value(nil).for(:address)

      # tests for zip
      should allow_value("12345").for(:zip)

      should_not allow_value("bad").for(:zip)
      should_not allow_value("1234").for(:zip)
      should_not allow_value("123456").for(:zip)
      should_not allow_value("12345-6789").for(:zip)

      # tests for state
      should allow_value("OH").for(:state)
      should allow_value("PA").for(:state)
      should allow_value("WV").for(:state)
      should_not allow_value("bad").for(:state)
      should_not allow_value("NY").for(:state)
      should_not allow_value(10).for(:state)
      should_not allow_value("CA").for(:state)

      # tests for phone
      should allow_value("4122683259").for(:phone)
      should allow_value("412-268-3259").for(:phone)
      should allow_value("412.268.3259").for(:phone)
      should allow_value("(412) 268-3259").for(:phone)
      should_not allow_value("2683259").for(:phone)
      should_not allow_value("14122683259").for(:phone)
      should_not allow_value("4122683259x224").for(:phone)
      should_not allow_value("800-EAT-FOOD").for(:phone)
      should_not allow_value("412/268/3259").for(:phone)
      should_not allow_value("412-2683-259").for(:phone)

  # Assistance-based tests
      # tests for gov_assistance
      should allow_value(true).for(:gov_assistance)
      should allow_value(false).for(:gov_assistance)

      should_not allow_value(150).for(:gov_assistance)
      should_not allow_value("Yes").for(:gov_assistance)

      # tests for is_employed
      should allow_value(true).for(:is_employed)
      should allow_value(false).for(:is_employed)

      should_not allow_value(30000).for(:is_employed)
      should_not allow_value("Movie theater usher").for(:is_employed)

      # tests for is_veteran
      should allow_value(true).for(:is_veteran)
      should allow_value(false).for(:is_veteran)

      should_not allow_value(nil).for(:is_veteran)
      should_not allow_value("Marines").for(:is_veteran)

  # Establish context
  # Testing other methods with a context
  context "Creating seven clients" do
    setup do 
      @dan = FactoryGirl.create(:client)
      @barney = FactoryGirl.create(:client, :last_name => "Saha", :first_name => "Barney", :active => false, :ethnicity => "Indian" )
      @ryan = FactoryGirl.create(:client, :last_name => "Black", :first_name => "Ryan", :phone => "412-867-5309", :ethnicity => "White", :gov_assistance => true )
      @joe = FactoryGirl.create(:client, :last_name => "Oak", :first_name => "Joseph", :ethnicity => "Asian", :is_employed => false )
      @mary = FactoryGirl.create(:client, :last_name => "Clute", :first_name => "Mary", :gender => "female", :ethnicity => "White" )
      @jon = FactoryGirl.create(:client, :last_name => "Carreon", :first_name => "Jon", :is_veteran => true )
      @meg = FactoryGirl.create(:client, :last_name => "Smith", :first_name => "Megan", :ethnicity => "White", :gender => "female", :is_employed => false)
    end

    # and provide a teardown method as well
    teardown do
      @dan.destroy
      @barney.destroy
      @ryan.destroy
      @joe.destroy
      @mary.destroy
      @jon.destroy
      @meg.destroy
    end

    # test one of each factory
    should "show that all factories are properly created" do
      assert_equal "Tabrizi", @dan.last_name
      assert @ryan.active
      assert @joe.active
      assert_equal "Mary", @mary.first_name
      assert @jon.active
      assert @meg.active
      deny @barney.active
    end

    # test the callback is working 'reformat_phone'
    should "shows that Ryan's phone is stripped of non-digits" do
      assert_equal "4128675309", @ryan.phone
    end

    # test the scope 'alphabetical'
    should "shows that there are seven clients in in alphabetical order" do
      assert_equal ["Black", "Carreon", "Clute", "Oak", "Saha", "Smith", "Tabrizi"], Client.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'active'
    should "shows that there are six active clients" do
      assert_equal 2, Client.active.size
      assert_equal ["Black", "Carreon", "Clute", "Oak", "Smith", "Tabrizi"], Client.active.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'inactive'
    should "shows that there is one inactive client" do
      assert_equal 1, Client.inactive.size
      assert_equal ["Saha"], Client.inactive.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'receiving_gov_assistance'
    should "shows that there is one client receiving government assistance" do
      assert_equal 1, Client.receiving_gov_assistance.size
      assert_equal ["Black"], Client.receiving_gov_assistance.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'not_receiving_gov_assistance'
    should "shows that there are six clients not receiving government assistance" do
      assert_equal 6, Client.not_receiving_gov_assistance.size
      assert_equal ["Carreon", "Clute", "Oak", "Saha", "Smith", "Tabrizi"], Client.not_receiving_gov_assistance.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'male'
    should "shows that there are five male clients" do
      assert_equal 6, Client.male.size
      assert_equal ["Black", "Carreon", "Oak", "Saha", "Tabrizi"], Client.male.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'female'
    should "shows that there are two female clients" do
      assert_equal 2, Client.female.size
      assert_equal ["Clute", "Smith"], Client.female.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'employed'
    should "shows that there are five employed clients" do
      assert_equal 5, Client.employed.size
      assert_equal ["Black", "Carreon", "Clute", "Saha", "Tabrizi"], Client.employed.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'unemployed'
    should "shows that there are two unemployed clients" do
      assert_equal 2, Client.unemployed.size
      assert_equal ["Oak", "Smith"], Client.unemployed.alphabetical.map{|s| s.last_name}
    end

    # test the scope 'veteran'
    should "shows that there is one employed clients" do
      assert_equal 1, Client.veteran.size
      assert_equal ["Carreon"], Client.veteran.alphabetical.map{|s| s.last_name}
    end

    # test the method 'name' #DONE
    should "shows name as last, first name" do
      assert_equal "Tabrizi, Dan", @dan.name
    end   

    # test the method 'proper_name' #DONE
    should "shows proper name as first and last name" do
      assert_equal "Dan Tabrizi", @dan.proper_name
    end 

  end
end

非常感谢您的帮助。如果有任何其他文件需要确定问题,请告诉我。抱歉,如果这很简单或太模糊,我是 Rails 开发的新手,我正在尽我所知使用术语

编辑

以下是我目前的客户工厂,在 app_name/test/factories.rb

FactoryGirl.define do
  factory :client do
    last_name "Tabrizi"
    first_name "Dan"
    gender "male"
    ethnicity "Hispanic"
    marital_status "single"
    address "123 Example Lane"
    city "Anytown"
    state "PA"
    zip "12345"
    phone { rand(10 ** 10).to_s.rjust(10,'0') }
    gov_assistance false
    is_employed true
    is_veteran false
    active true
  end
end
4

2 回答 2

0

希望您对单独的测试数据库执行测试,所以这样做是安全的:

teardown do
  Client.destroy_all
end
于 2012-07-24T14:18:51.553 回答
0

就像西蒙建议的那样,尝试评论或删除您的拆解。通常在这些情况下,teardown 会掩盖真正的错误,因为它会编译您的所有代码并尝试启动将失败的 destroy 方法,因为您的工厂将返回 nil。由于发生这种情况,它将返回记录的最后一个错误,这就是您收到该错误的原因。

于 2012-07-24T16:55:57.793 回答