简体   繁体   中英

RSpec-rails | Password field doesn't require the appropriate attribute for the model

I have the following spec:

# spec/views/users/new.html.haml

require 'spec_helper'

describe 'users/new' do
  before { assign :user, stub_model(User).as_new_record }
  before { render }

  subject { rendered }

  it { should have_selector "form input[type=\"text\"][name=\"user[email]\"]" }
  it { should have_selector "form input[type=\"password\"][name=\"user[password]\"]" }
end

When I run it after creating a form it complains of an undefined method 'email':

undefined method `email' for #<User ...>

If I add this method to User, create the appropriate column in the users table or stub the method in the example, both examples are passed despite the password method is still undefined.

Why the second example gets passed?

Thanks.

Ruby 1.9.3;

Ruby on Rails 3.2.1;

RSpec 2.8.0;

RSpec-rails 2.8.1.

As it turned out when Rails creates a text field for an attribute of a model, the model should have that attribute. If it doesn't have it then ActionView::Template::Error will be risen when attempting to render a form. However, when Rails creates a password field the model should not have the appropriate attribute, so nothing will be risen when rendering a form.

Thereby, if I specify that some view should have a text field for an attribute of a model, the example will demand that attribute. On the contrary, when I specify that some view should have a password field for some attribute, the example won't demand driving out that attribute.

For example:

= form_for @user do |f|
  %div= f.text_field :login #=> User#login is required
  %div= f.password_field :password #=> User#password isn't requred

Tricky question. First I'll try to explain the behavior you are observing. Then I'll argue that this should not be a view spec and elaborate on the alternative I believe in. Finally I'll tell you how you can actually get it to work.

When you render form fields in a view spec, the code depends on the model having all the attributes. text_field calls User#login to fill the form value if there should be one. password_field doesn't, because you don't want your password fields pre-filled. That's how they work outside of specs and this is pretty much expected behavior. This is why you're getting the behavior.

As for view specs, this one is not very useful. Two reasons - (1) it does not add value and (2) it is fragile:

  1. The only knowledge specified in this spec is the presence of two input fields. This does not help you drive design or discover behavior. Furthermore, it does not protect you from a likely error. The only error you can prevent is accidentally removing the form. This is highly unlikely, in my opinion. It is way better to test what submitting the form should do in an integration test (say Cucumber or Steak)
  2. Due to the stub, the test is likely to break when you extend User and the view. Let's say you're adding a field - website. If you just add the column in the database and in the view, the test will fail, because it tries to access a non-existing (in the stub) column. Now you have working code, but a failing test. You'll have to go and add :website in the stub. And that is just one of the way to break it. In my experience, it often happens, that a view spec break far too easily when the code works. It is double trouble, when you depend on helpers.

I've found that they have only one value -- test-driving the interface of a non-ActiveRecord::Object. You build the view test-first with a mock. When you're done, you know what methods your model needs.

The alternative I prefer is to test those kind of things in integration. I personally use Cucumber, although Steak is also fine. It gives you better coverage and less fragility. In my observation, view specs have fallen a bit out of grace in the rspec mailing list. You can search it for more details about why to avoid them. Here's one thread that I shares some of my sentiments.

Finally, if I haven't manage to convince you to abandon this path, check out factory_girl . It provides a neat method, called build_stubbed , that gives you a stubbed model that can be used in form_for . Although I've used it a couple of times, I've always regretted it later.

Of course, YMMV.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM