require 'spec_helper'
describe UsersController do
let(:user){ double(User, id: 2, name: "Jimbo", email: 'jimbo@email.com', password: 'passwordhuzzah', password_confirmation: 'passwordhuzzah') }
before do
mock_model("User")
end
describe 'PATCH #update' do
User.should_receive(:find).with(user.id.to_s).and_return user
user.should_receive(:update_attributes).with({ "email" => user.email, "name" => user.name, "password" => user.password, "password_confirmation" => user.password_confirmation })#.and_return true
patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
flash[:error].should == "could not update user"
response.status.should == 200
end
end
代码库:
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
redirect_to @user, flash: { success: 'succesfully updated user' }
else
flash.now[:error] = "could not update user"
render 'edit'
end
end
虽然上面的规范通过(并且只用了0.05秒!)我正确地做了吗?随着模拟高于请求,以及下面的“正常”预期呢?看起来有点笨拙。它不仅难以阅读,而且如果一个期望失败,所有这些都会失败。
是什么让我觉得我做错了是我得到的一个奇怪的错误。请参阅describe块的第二行,我说用户(user
)的实例应该使用更新的属性触发update_attributes?请注意我已注释掉的and_return true
方法。当它被链接时,运行上面的规范会让我感到厌烦:
1) UsersController should_receive
Failure/Error: patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
NoMethodError:
undefined method `model_name' for RSpec::Mocks::Mock:Class
虽然这是我的头脑,但我认为这是因为用户'实例'实际上不是一个实例,只是一个哈希。我想因为我使用了mock_model
并且双重继承了那个模拟模型,所以所有的active-record-y东西都会被设置。
无论如何,我应该如何正确地编写这个规范? 我不想使用FactoryGirl,我想保留所有内容,以便我的规格快速准确地报告错误。
答案 0 :(得分:1)
是的,在这种控制器测试的情况下,您的基本流程是“正常的”,除了您的最内层代码需要在it
块内(并且可能并且只是没有正确转录)。
但是,您没有使用mock_model
来电,因为您没有对结果做任何事情。此方法不会对您传递字符串名称的类进行任何根本性更改,它只是创建一个模拟ActiveRecord实例的模拟对象,在您的情况下,您实际上会丢弃该实例。与所有RSpec双打一样,第一个参数只是给它一个可用于错误消息等的名称。
是的,您返回true
时收到错误的原因是redirect_to
期望@user
成为ActiveRecord实例,而它不仅仅是Hash
如你所说的那样,它没有所需的model_name
方法。
有很多方法可以重写这一点,特别是如果你想同时支持成功和失败的情况,但是最小化变化的一种方法是(未经过全面测试):
describe UsersController do
let(:user){ mock_model(User, id: 2, name: "Jimbo", email: 'jimbo@email.com', password: 'passwordhuzzah', password_confirmation: 'passwordhuzzah') }
describe 'PATCH #update' do
it "should fail in this case" do
User.should_receive(:find).with(user.id.to_s).and_return user
user.should_receive(:update_attributes).with({ "email" => user.email, "name" => user.name, "password" => user.password, "password_confirmation" => user.password_confirmation })#.and_return true
patch :update, id: user.id, user: { email: user.email, name: user.name, password: user.password, password_confirmation: user.password_confirmation }
flash[:error].should == "could not update user"
response.status.should == 200
end
end
end