【问题标题】:RSpec is failing when it shouldn't. Why?RSpec 在不应该的时候失败了。为什么?
【发布时间】:2014-02-09 11:03:03
【问题描述】:

我正在构建一个非常小的 Sinatra API,今天我开始为它编写测试。我正在使用 RSpec 和 FactoryGirl。

我有一个用户模型,在控制器中我检查正在注册的用户的电子邮件是否已经保存。如果保存,则返回 402,否则创建用户并返回 200。为了对此进行测试,我使用相同的参数两次对 users 路由进行 POST,第一次检查 last_response.should be_ok因为数据库是空的,所以我可以创建用户,然后我有另一个测试,POST 到users 路由与之前的参数相同,但期待last_response.should_not be_ok,我得到了 200!所以测试失败了!!

这是我的 Sinatra 路线:

post '/users' do
    user = User.where(email: params[:email]).first

    if !user
        user = User.create(email: params[:username], password_not_hashed: params[:password])
        halt 200, {user:user}.to_json
    end 

    halt 409, {error:"user already exists"}.to_json
end

这是我的测试:

describe 'Create User' do
    include Rack::Test::Methods
    params = {}
    params['password'] = "username"
    params['username'] = "username@username.com"

    it "should create a user" do 
        post '/users', params
        last_response.should be_ok
    end

    it "should fail to create a user because its duplicate user" do 
        post '/users', params
        last_response.should_not be_ok
    end
end

我失败了,因为最后一次测试的响应是 200。我用pp 打印了paramslast_response,这就是我得到的:

参数:

{"password"=>"username", "username"=>"username@username.com"}

last_response

@block=nil,
@body=["{\"user\":{\"id\":null,\"email\":\"username@username.com\"}}"],
@body_string=nil,
@chunked=false,
@errors="",
@header=
    {"Content-Type"=>"text/html;charset=utf-8",
    "Content-Length"=>"52",
    "X-XSS-Protection"=>"1;    mode=block",
    "X-Content-Type-Options"=>"nosniff",
    "X-Frame-Options"=>"SAMEORIGIN"},
    @length=52,
    @original_headers=
    {"Content-Type"=>"text/html;charset=utf-8",
        "Content-Length"=>"52",
        "X-XSS-Protection"=>"1;    mode=block",
        "X-Content-Type-Options"=>"nosniff",
        "X-Frame-Options"=>"SAMEORIGIN"},
    @status=200,

有什么想法吗?!?!快把我逼疯了!

编辑:

刚刚添加了一个活动记录验证 (validates_uniqueness_of :email),在我的测试输出中我得到以下信息:

Failure/Error: post '/users', params
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken

所以,这意味着该电子邮件已经创建了一个用户,所以我仍然不明白为什么测试失败,并且创建用户的路由总是在 if !user

这是代码覆盖率的输出:

疯了……我迷路了!

编辑:

我还有一个 after all rspec 钩子,但这不应该是重要的,因为它只在所有测试之后运行:

after :all do
   ActiveRecord::Base.subclasses.each(&:delete_all)
end

【问题讨论】:

  • 如何在第 14 行更改条件?我一直在使用 irb 中的 !user 条件,我想知道它是否始终如一地评估我们所期望的方式。我仍然不确定。使用“if user.nil”怎么样?反而?这似乎更精确一些。
  • 同样的问题...改成user.nil?而且还在进去!
  • 添加打印语句或断点时发生了什么?您是否能够在测试执行期间检查数据库的状态?
  • 我在控制器中添加了一些打印来检查“用户”是否为 nil 并且它是 nil。也许与sqlite有关? (我在测试环境中使用的数据库......我不知道还能尝试什么,已经花了太多我们的希望有人意识到发生了什么......我想知道!!哈哈
  • 根据设计,您的测试数据库不应在测试之间保留数据。如果是这样,您最终可能会在测试中遇到各种奇怪的隐藏数据依赖关系。您可以使用 rake db:prepare 任务按下测试数据库来填充您的测试数据库、调用工厂、在您的规范中创建一个 before 语句,或者在您再次调用 API 之前执行一些像我下面的建议那样的俗气来创建用户。因此,您的测试似乎工作正常,因为您没有在测试执行之前准备好您的数据库。我想知道您是如何使活动记录验证工作的。 . .

标签: rspec sinatra


【解决方案1】:

感谢您提供详细的问题——这真的很好地结合在一起。我怀疑您之前发给 /user 的帖子没有在测试之间将新用户保留在您的测试数据库中。您是否尝试在第二次调用 post 时添加打印语句以确保您的测试数据库包含用户?看起来像这样:

it "should fail to create a user because its duplicate user" do 
    puts User.where(email: params[:email]).first
    post '/users', params
    last_response.should_not be_ok
end

您可能还想安装 pry 并在测试中设置断点。你可以在这里阅读更多关于撬的信息:https://github.com/pry/pry。这将让您在代码执行时检查测试数据库的状态。安装 pry 后,您可以像这样在代码中添加交互式中断:

require 'pry'

<other test business happens>


it "should fail to create a user because its duplicate user" do 
    binding.pry
    post '/users', params
    last_response.should_not be_ok
end

如果您的测试数据库确实没有保留发布到 /user 之间的数据,您可以在第二次测试中尝试这样的操作,以便在尝试发布之前添加用户:

it "should fail to create a user because its duplicate user" do 
    User.create(email: params[:username], password_not_hashed: params[:password])
    post '/users', params
    last_response.should_not be_ok
end

【讨论】:

  • 感谢您的回答!我添加了唯一性的活动记录验证,但出现活动记录错误,所以发生了一些奇怪的事情......用户是在第一次调用中创建的,但我不知道为什么在数据库的第二次调用中找不到它...奇怪
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-30
  • 2010-10-10
相关资源
最近更新 更多