3

在 M. Hartl's Rails 教程的第 6.3.4 节末尾,我遇到了用户模型的 RSpec 测试问题。我完全迷路了,请帮忙。

除了我需要帮助的测试之外,所有测试都通过了:

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

失败:

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

失败/错误:它 { 应该 == 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...”> 得到:#用户 ID:无,名称:“示例用户”,电子邮件:“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) 您所做的任何修改都将在退出 irb(main):001:0> 时回滚

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

=> #

irb(main):002:0> user.save
(0.3ms) SAVEPOINT active_record_1 User Exists (0.3ms) SELECT 1 AS one 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", Wed , 2013 年 8 月 21 日 21:45:13 UTC +00:00], ["email", "user@example.com"], ["name", "Example User"], ["password_digest", "$2a$10 $6tJCohmi7t3OShf/55S5Se98JWvGhJfC1wNAZsc8B6WPP1Zgee0wu"],["updated_at",2013 年 8 月 21 日星期三 21:45:13 UTC +00:00]]

(0.2ms)RELEASE SAVEPOINT active_record_1 => true

irb(main):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(main):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

不确定 gem 是否重要,这是我的:

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'

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

4

1 回答 1

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块很棒,但您应该谨慎使用它们。如果你发现你在 a 中包装了一个it块,describe那么摆脱它describe

保持简单:尽量不要预先加载测试。当您进行前端加载时,您很可能正在编写符合数据的测试。

http://betterspecs.org/上还有很多其他很棒的技巧。绝不将此视为法律,但它确实提供了一些很好的信息,可以为编写规范奠定非常好的基础。

最后,祝你好运,享受编写规范的乐趣!

于 2013-08-25T02:10:14.297 回答