我正试图在Michael Hartl的Exercise 2 of Chapter 8.5中做Ruby on Rails Tutorial。练习如下:
按照第8.3.3节中的示例,浏览用户和身份验证请求规范(即当前在spec / requests目录中的文件)并在spec / support / utilities.rb中定义实用程序函数以将测试与实施。额外的功劳:将支持代码组织到单独的文件和模块中,并通过在规范帮助文件中正确包含模块来使所有工作正常工作。
例8.3.3:utilities.rb
include ApplicationHelper
def valid_signin(user)
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
RSpec::Matchers.define :have_error_message do |message|
match do |page|
page.should have_selector('div.alert.alert-error', text: message)
end
end
定义的valid_signin(user)
函数在authentication_pages_spec.rb
的以下块中使用,并且工作正常。
describe "with valid information" do
let(:user){FactoryGirl.create(:user)}
before { valid_signin(user) }
it { should have_selector('title', text: user.name) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
it { should_not have_link('Sign in', href: signin_path) }
describe "followed by signout" do
before { click_link "Sign out" }
it { should have_link('Sign in') }
end
end
所以在这个例子中,我开始创建自己的名为valid_signup(user)
:
def valid_signup(user)
fill_in "Name", with: user.name
fill_in "Email", with: user.email
fill_in "Password", with: user.password
fill_in "Confirmation", with: user.password_confirmation
end
我在user_pages_spec.rb
中使用此块,如下所示:
describe "with valid information" do
let(:user){FactoryGirl.create(:user)}
before { valid_signup(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
let(:user) { User.find_by_email(user.email) }
it { should have_selector('title', text: user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
它不起作用。 Spork / Guard报告了这些错误:
Failures:
1) UserPages signup with valid information should create a user
Failure/Error: expect { click_button submit }.to change(User, :count).by(1)
count should have been changed by 1, but was changed by 0
# ./spec/requests/user_pages_spec.rb:46:in `block (4 levels) in '
2) UserPages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '
3) UserPages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '
4) UserPages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '
错误似乎表明valid_signup(user)
中utilities.rb
函数中的user.name未定义,但我没有看到任何理由。我已多次重启Guard,并做了rake db:test:prepare
以确保测试数据库(使用postgresql)是有序的。
这是我factories.rb
的完整性:
FactoryGirl.define do
factory :user do
name "Example User"
email "user@example.com"
password "foobar"
password_confirmation "foobar"
end
end
在我继续尝试解耦更多测试套件之前,我非常希望解决此错误,更重要的是,了解它的原因。
修改
我已尝试过您的提示,并在user_pages_spec.rb
中编辑了该功能,如下所示:
describe "with valid information" do
before { valid_signup(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
let(:user) { User.find_by_email('user@example.com') }
it { should have_selector('title', text: user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
由于我从函数中删除了let(:user){FactoryGirl.create(:user)}
,我猜测在函数中不再创建用户,所以我需要定义valid_signup(user)
,例如user
变量valid_signup
FactoryGirl不再填写:
def valid_signup(user)
fill_in "Name", with: "Example User"
fill_in "Email", with: "user@example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
这不起作用,并给了我以下错误:
Failures:
1) UserPages signup with valid information should create a user
Failure/Error: before { valid_signup(user) }
NameError:
undefined local variable or method user' for #<RSpec::Core::ExampleGroup::Nested_5::Nested_3::Nested_2:0x007fdafc5088c0>
# ./spec/requests/user_pages_spec.rb:42:in
block (4 levels) in '
2) UserPages signup with valid information after saving the user
Failure/Error: it { should have_selector('title', text: user.name) }
NoMethodError:
undefined method name' for nil:NilClass
# ./spec/requests/user_pages_spec.rb:52:in
block (5 levels) in '
我还尝试使用user' for #<RSpec::Core::ExampleGroup::Nested_5::Nested_3::Nested_2:0x007fdafc5088c0>
# ./spec/requests/user_pages_spec.rb:42:in
以前的方式运行测试(使用name' for nil:NilClass
# ./spec/requests/user_pages_spec.rb:52:in
,这也不起作用,但有错误:
valid_signup(user)
接下来我尝试运行它而不在user.name, user.email, user.password, user.password_confirmation
中传递变量:Failures:
1) UserPages signup with valid information should create a user
Failure/Error: before { valid_signup(user) }
NameError:
undefined local variable or method `user' for #
# ./spec/requests/user_pages_spec.rb:42:in `block (4 levels) in '
2) UserPages signup with valid information after saving the user
Failure/Error: it { should have_selector('title', text: user.name) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/requests/user_pages_spec.rb:52:in `block (5 levels) in '
并且在user_pages_spec.rb
中的函数中没有变量:
before { valid_signup() }
这返回:
utilities.rb
仍然没有接近答案。我可能会忽略一些简单的事情。不知道是什么。我得到了我第一次做错的事情:我只是认为FactoryGirl是一种创建变量的方法,我不知道它实际上对我的测试数据库做了些什么。
答案 0 :(得分:3)
我将尝试解释原始测试中发生了什么(我觉得比编辑版本更容易修复):
describe "with valid information" do
let(:user) {FactoryGirl.build(:user)} # FactoryGirl.create will save the instance, you should be using build instead
before { valid_signup(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
# let(:user) { User.find_by_email(user.email) } # this is not needed any more
it { should have_selector('title', text: user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
有关FactoryGirl使用的更多信息:https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories
答案 1 :(得分:2)
FactoryGirl将用户保存到数据库,然后您使用已存在于数据库中的用户访问sign_in_path并使用valid_sigin(用户)填写sign_in表单
let(:user){FactoryGirl.create(:user)}
before { valid_signin(user) }
当你这样做时:
let(:user){FactoryGirl.create(:user)}
before { valid_signup(user) }
工厂女孩将用户保存在数据库中,并使用已经拍摄的电子邮件填写表单。
编辑:
describe "with valid information" do
before { valid_signup(user) }
您没有定义变量用户,因为您删除了let(:user){FactoryGilr.create(:user)}
,并且您应该访问正确的路径,您当前的路径是“sign_in_path”并且应该是“sign_up_path”
你应该这样做:
<强> utilities.rb 强>
def valid_sign_up(user)
fill_in "Name", with: user.name
fill_in "Email", with: user.email
fill_in "Password", with: user.password
fill_in "Confirmation", with: user.password_confirmation
end
<强> user_pages_spec.rb 强>
describe "with valid information" do
let(:user){User.new(name: "my name", email: "myemail@example"...)
before do
visit sign_up
valid_sign_up(user)
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
答案 2 :(得分:2)
我遇到了同样的问题,并找到了解决方案:当你定义valid_signup时,它应该以'page'作为参数。毕竟,您正在测试页面元素,而不是用户。
<强>规格/支持/ utilities.rb 强>
def valid_signup(page)
fill_in "Name", with: "Example User"
fill_in "Email", with: "user@example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
<强>规格/请求/ user_pages_spec.rb 强>
描述“有效信息” 在{valid_signup(page)}之前
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
我希望这有帮助!
UPDATE 我现在意识到这是因为变量'page'的范围(因为它是主题)。要使用“用户”,我添加了一行
let(:user) { FactoryGirl.create(:user) }
以上
在{ sign_up(user) }
之前。这打破了后来的规范,我也尝试使用'user'作为变量,因此我将名称更改为'editeduser'。这是完整的例子:
<强> user_pages_spec.rb 强>
require 'spec_helper'
describe "UserPages" do
subject { page }
...
describe "signup page" do
before { visit signup_path }
let(:submit) { "Create my account" }
it { should have_selector('h1', text: 'Sign up') }
it { should have_selector('title', text: full_title('Sign up')) }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
describe "after submission" do
before { click_button submit }
it { should have_selector('title', text: 'Sign up') }
it { should have_content('error') }
end
end
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before { sign_up(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
let(:editeduser) { User.find_by_email('user@example.com') }
it { should have_selector('title', text: editeduser.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
end
希望这有助于某人!
答案 3 :(得分:0)
我对这个也感到好奇,并且找到了一个可能更符合Hartl预期的答案(虽然只是学习,我不是100%肯定最佳答案不是更优雅或不是)。
由于我们没有使用FactoryGirl来注册用户,而是让他们签名,我不想在我的重构中使用它。这就是我在utilities.rb中的内容:
def valid_signup
fill_in "Name", with: "Example User"
fill_in "Email", with: "user@example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
然后在user_pages_spec.rb中我替换了
describe "with valid information" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "user@example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
与
describe "with valid information" do
before { valid_signup }
我们不需要将用户保存到数据库只是为了检查一次注册,因为他们不需要通过多个页面查看来保持。此外,由于我们没有查找用户,因此我们在valid_signup方法之后不需要(用户)参数(我认为我的术语是正确的。如果我没有,请纠正我。)