为什么rspec从类似的控制台操作得到不同的结果? (M. Hartl Ruby on Rails教程2ed - 第6.3.4节)

时间:2013-08-21 23:48:22

标签: ruby-on-rails rspec

我在M. Hartl的Rails教程6.3.4节末尾的用户模型的RSpec测试中遇到了问题。我完全失去了请帮助。

所有测试都与我需要帮助的测试分开:

我的问题:为什么rspec会从类似的控制台操作中得到不同的结果?如何解决这个问题并通过测试?

  

故障:

     

1)用户有效密码的验证方法的返回值

     

失败/错误:它{should == found_user.authenticate(@ user.password)   }          预期:用户ID:1,名称:“示例用户”,电子邮件:“user@example.com”,created_at:“2013-08-21 21:38:32”,updated_at:   “2013-08-21 21:38:32”,password_digest:   “$ 2A $ 04 $ tjcti4yEYFuJbbz9sa.Z9.F / KXJtHU3A8onU1Fhovn3x ......” >               得到:#User id:nil,name:“Example User”,email:“user@example.com”,created_at:nil,updated_at:nil,password_digest:   “$ 2A $ 04 $ 6fdtfzgCFYetjShfYKuFIObWo4Uru4xRMkhW7Ow92O.2 ...” >

     

完成0.64049秒20例,1次失败

     

失败的例子:

     

rspec ./spec/models/user_spec.rb:100#用户返回值   验证有效密码的方法

当我在rails控制台中手动测试时,authenticate方法返回完全相同的用户,并且所有字段都匹配。以下是我的控制台实验:

  

在沙箱中加载开发环境(Rails 4.0.0)Any   您所做的修改将在退出irb(主):001:0>

上回滚      

user = User.new(名称:“示例用户”,电子邮件:“user@example.com”,   密码:“foobar”,password_confirmation:“foobar”)

     

=> #

     

IRB(主):002:0>的 user.save
  (0.3ms)SAVEPOINT active_record_1用户存在(0.3ms)SELECT 1 AS一个FROM“users”WHERE   LOWER(“users”。“email”)= LOWER('user@example.com')LIMIT 1二进制数据   已在string

上插入password_digest类型      

SQL(48.2ms)   INSERT INTO“users”(“created_at”,“email”,“name”,“password_digest”,   “updated_at”)VALUES(?,?,?,?,?)[[“created_at”,2013年8月21日,星期三   21:45:13 UTC +00:00],[“email”,“user@example.com”],[“名称”,“示例   用户“],[”password_digest“,   “$ 2A $ 10 $ 6tJCohmi7t3OShf / 55S5Se98JWvGhJfC1wNAZsc8B6WPP1Zgee0wu”],   [“updated_at”,星期三,2013年8月21日21:45:13 UTC +00:00]]

     

(0.2ms)RELEASE SAVEPOINT active_record_1   =>真的

     

IRB(主):003:0> found_user = User.find_by_email(user.email)

     

用户加载(0.5ms)SELECT“users”。* FROM“users”WHERE“users”。“email”=“user@example.com”LIMIT 1   => #

     

IRB(主):004:0> user == found_user.authenticate(user.password)

     

=>真

这就是我在spec / models / user_spec.rb

中的内容
require 'spec_helper'

describe User do

  before { @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") }

  subject { @user }
.
.
.

  describe "return value of authenticate method" do
    before { @user.save }
    let(:found_user) { User.find_by_email(@user.email) }

    describe "with valid password" do
      it { should == found_user.authenticate(@user.password) }
    end

    describe "with invalid password" do
      let(:user_for_invalid_password) { found_user.authenticate("invalid") }

      it { should_not == user_for_invalid_password }
      specify { user_for_invalid_password.should be_false }
    end
  end

.
.
.
end

用户模型如下

class User < ActiveRecord::Base

  before_save { |user| user.email = email.downcase }

  validates :name, presence: true, length: { maximum: 50 }

  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }

  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
  validates :password_confirmation, presence: true

end

不确定宝石是否重要,这就是我的宝石:

source 'https://rubygems.org'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.0.0'

group :development do
  gem 'annotate', '~> 2.4.1.beta'
end

# Use sqlite3 as the database for Active Record
gem 'sqlite3'

# Use SCSS for stylesheets
gem 'sass-rails', '~> 4.0.0'

# Bootstrap css framework from Twitter
gem 'bootstrap-sass', '2.0.0'

# RSpec testing framework
gem 'rspec-rails', '2.11.0'
gem 'capybara', '2.1.0'

# 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'

# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 1.2'

group :doc do
  # bundle exec rake doc:rails generates the API under doc/api.
  gem 'sdoc', require: false
end

# Use ActiveModel has_secure_password
gem 'bcrypt-ruby', '3.0.1'

提前感谢您,如果我错过任何其他重要信息,请告诉我。

1 个答案:

答案 0 :(得分:2)

您遇到的直接问题是如何设置测试。主题被设置为对象的未保存实例化。如果您在before { subject.reload }后执行@user.save,那么就可以解决此问题。那真是个傻瓜。

真正的问题是你过于复杂你的规范。您可以在三种不同的状态下以三种不同的方式在三个不同的位置设置相同的用户。这绝不应该发生。

以下是我设置的方法:

require 'spec_helper'

describe User do    
  describe "#authenticate" do
    let(:user) { User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") }

    it "has a valid password" do
      user.authenticate("foobar").should be_true
    end

    it "has invalid password" do
      user.authenticate("invalid").should be_false
    end
  end
end

要记住几个注意事项。

创建尽可能少的对象:您创建的对象越多,不需要的东西或处于错误状态的可能性就越大,您的测试就会失败,甚至更糟糕的是您的测试很脆弱并且意外失败。

单行it很好,但限制:当您需要进行快速验证时,单行it块很棒,但您应该谨慎使用它们。如果您发现正在it中包含describe块,那么请删除describe

保持简单:尽量不要预先加载测试。当你加载前的可能性超过你正在编写符合数据的测试时。

http://betterspecs.org/还有很多其他很棒的提示。绝不是以此作为法律,但它确实有一些很好的信息,可以为编写规范奠定良好的基础。

最后,祝你好运,并喜欢写规格!