I meant to work on this post… oh about 7 months ago.
Way back in January (7 months ago), Jamis Buck posted an article titled, Testing your views, which gave a few tips on using Test::Unit to, as the title suggests, test your views.
While, I’m not going to rewrite everything that Jamis wrote, I’d like to show you how to test these views with RSpec. (you might take a moment to quickly read his post…)
In this example, I’m going to show you how we’re able to write specs for the following RHTML, which you’ll notice matches the code that he wrote tests for.
```ruby
<% if @user.administrator? %>
Hi <%= @user.name %>! You appear to be an administrator.
<%= link_to "Click here", admin_url, :id => "admin_link" %>
to see the admin stuff!
<% end %>
Jamis writes, *"The only really significant thing you ought to be
testing here is that the admin link only shows up for administrators. "*
So, let's do just that, but with RSpec.
I'm not sure how Jamis is handling his view tests, but we're going to
approach our view specs, much like we approach our controller specs,
with the use of mocks and stubs, because we really don't need to spec
any of our models at this level in the application.
**Tip:** Write specifications for your models... in your model specs
**not** in your controller or view specs.
The first thing that we're going to do is setup a custom spec helper,
because for something like an mocked user, will probably get reused in
other areas of the user interface. Spec helpers are essentially modules
that you can include in your RSpec descriptions (the block that starts
with `describe`) and reuse.
In this spec helper, I'm going to include two methods, to mock the User
model and stub out any of the methods that are necessary for spec'n this
view.
````ruby
```ruby
module MockUserHelper
def mock_normal_user
user = mock(User)
user.stub!(:administrator?).and_return(false) # <--- NOT an admin
user.stub!(:name).and_return('David Chelimsky')
return user
end
def mock_admin_user
user = mock(User)
user.stub!(:administrator?).and_return(true) # <--- IS an admin
user.stub!(:name).and_return('Aslak Hellesoy')
return user
end
end
```ruby
In the mock_normal_user
method, we’re constructing a mock object and
stubbing out the methods that we see are being called in the RHTML code.
In mock_admin_user
, we’re basically doing the same thing, but just
stubbing the administrator?
method to return true
for this mock
user.
By stubbing these methods, we’ll be able to send a non-ActiveRecord
object to the view and have it render without knowing the difference.
For example, the if
user.administrator?@ condition will return true or
false, depending on how we stubbed it.
For more information on mocks and stubs, read here.
Now that we have our spec helper, let’s go ahead and dive into a few specifications for the view.
```text
describe "index page" do
include MockUserHelper
it "should render an admin link for an admin user" do
assigns[:user] = mock_admin_user
render 'index'
response.should have_tag('a#admin_link')
end
it "should not render an admin link for a normal, non-admin user" do
assigns[:user] = mock_normal_user
render 'index'
response.should_not have_tag('a#admin_link')
end
end
```
Please note: This code example is only longer than the one shown by Jamis because he didn’t include how he setup all his user sessions/objects. ;-)
When these specs are run, we can see the following results.
[Pretty output courtesy of RSpec + TextMate bundle]{.small}
Great, we’ve been able to write specifications for our Rails views without a lot of pain. Stay tuned for more posts on this topic as I continue writing about how Designers and Developers can work together, in harmony. (see my last post on this topic)
For more information on adopting RSpec, please visit the RSpec project homepage.