Rails 教程:RSpec 测试解耦 [英] Rails Tutorial: RSpec test decoupling

查看:60
本文介绍了Rails 教程:RSpec 测试解耦的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 Michael 中进行第 8.5 章的练习 2Hartl 的 Ruby on Rails 教程.练习如下:

I'm trying to do Exercise 2 of Chapter 8.5 in Michael Hartl's Ruby on Rails Tutorial. The exercise is as follows:

按照第 8.3.3 节中的示例,通过用户和身份验证请求规范(即当前在 spec/requests 目录中的文件)并在 spec/support/utilities.rb 中定义实用程序函数以将测试与实施.额外的功劳:将支持代码组织到单独的文件和模块中,并通过在规范帮助文件中正确包含模块来使一切正常工作.

Following the example in Section 8.3.3, go through the user and authentication request specs (i.e., the files currently in the spec/requests directory) and define utility functions in spec/support/utilities.rb to decouple the tests from the implementation. Extra credit: Organize the support code into separate files and modules, and get everything to work by including the modules properly in the spec helper file.

示例 8.3.3:utility.rb

Example 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 块中,并且工作正常.

The defined valid_signin(user) function is used in the following block of authentication_pages_spec.rb and works fine.

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):

So with this example I set about to create my own named 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 中像这样使用这个块:

I'm using this block in user_pages_spec.rb like this:

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 报告这些错误:

It doesn't work. Spork/Guard reports these errors:

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:prepare 以确保测试数据库(使用 postgresql)是有序的.

The errors seem to suggest the user.name in my valid_signup(user) function in utilities.rb isn't defined, but I don't see any reason why. I've restarted Guard several times, and did a rake db:test:prepare to make sure the testing db (using postgresql) was in order.

为了完整性,这是我的 factories.rb:

Here's my factories.rb for completeness:

FactoryGirl.define do
    factory :user do
        name    "Example User"
        email   "user@example.com"
        password    "foobar"
        password_confirmation   "foobar"
    end
end

在我继续尝试分离更多测试套件之前,我非常想解决这个错误,更重要的是,了解它的原因.

Before I continue to try and decouple more of the testing suite I'd very much like to solve this error and, more importantly, understand the reason for it.

编辑

我已经尝试了你的提示,并在 user_pages_spec.rb 中编辑了函数如下:

I've tried your tips, and edited the function in user_pages_spec.rb as follows:

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_signupuser 变量不再由 FactoryGirl 填充:

Since I removed let(:user){FactoryGirl.create(:user)} from the function I guessed there was no longer a user created in the function so I needed to define valid_signup(user) as such as the user variable for valid_signup was no longer being filled by 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

这不起作用,并给了我以下错误:

This didn't work and gave me the following errors:

Failures:

1) 使用有效信息注册 UserPages 应创建一个用户失败/错误:在 { valid_signup(user) } 之前名称错误:未定义的局部变量或方法 user' for #<RSpec::Core::ExampleGroup::Nested_5::Nested_3::Nested_2:0x007fdafc5088c0># ./spec/requests/user_pages_spec.rb:42:inblock (4 levels) in '

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:inblock (4 levels) in '

2) 保存用户后使用有效信息注册 UserPages失败/错误:它 { should have_selector('title', text: user.name) }无方法错误:未定义的方法 name' for nil:NilClass# ./spec/requests/user_pages_spec.rb:52:inblock (5 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:inblock (5 levels) in '

我还尝试使用 valid_signup(user) 运行测试,就像我以前使用的方式(使用 user.name, user.email, user.password, user.password_confirmation,它也不起作用,有错误:

I also tried running the test with valid_signup(user) the way I used to have it before (with user.name, user.email, user.password, user.password_confirmation, which didn't work either, with errors:

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 的函数中没有变量:

Next I tried running it without passing variables in user_pages_spec.rb: before { valid_signup() } and without a variable in the function in 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

返回:

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 是一种创建变量的方法,但我不知道它实际上对我的测试数据库做了什么.

Still no closer to the answer. I might be overlooking something simple. No clue what though. I got what I first did wrong though: I just thought FactoryGirl was a way to create variables, and I didn't know it actually did something to my test database.

推荐答案

我将尝试解释您的原始测试中发生了什么(我发现它比编辑后的版本更容易修复):

I will try to explain what is going on in your original test (which I find easier to fix than the edited version):

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

More info on FactoryGirl usage: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories

这篇关于Rails 教程:RSpec 测试解耦的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆