按照第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(用户)函数在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 '
这些错误似乎表明在utilities.rb中的valid_signup(user)函数中的user.name没有定义,但我没有看到任何原因.我已经多次重启Guard了,并做了一个rake db:test:准备确保测试db(使用postgresql)是有序的.
这是我的factory.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),因为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
user' for #<RSpec::Core::ExampleGroup::Nested_5::Nested_3::Nested_2:0x007fdafc5088c0> # ./spec/requests/user_pages_spec.rb:42:inname' for nil:NilClass # ./spec/requests/user_pages_spec.rb:52:inname' for nil:NilClass # ./spec/requests/user_pages_spec.rb:52:in
block (5 levels) in '
我也尝试使用valid_signup(用户)以前的方式运行测试(使用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中传递变量:在{valid_signup()}之前并且在utilities.rb中的函数中没有变量:
def valid_signup() fill_in "Name",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 # # ./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 '
仍然没有更接近答案.我可能会忽略一些简单的事情.不知道是什么.我得到了我第一次做错的事情:我只是认为FactoryGirl是一种创建变量的方法,我不知道它实际上对我的测试数据库做了些什么.
解决方法
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: 'Welcome') } it { should have_link('Sign out') } end end
有关FactoryGirl用法的更多信息:https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories