- Read Tutorial
Integration Tests
So far in this course we've spent the majority of our time working on model based tests with limited integration, or end to end, specs. In this lesson we'll walk through how to implement an index action for our topics
and letting the tests drive the functionality.
Index Action
The index
action is going to show users the full list of topics. Referencing our requirements document we can see what the page will eventually look like:
With this in mind we know that the basic functionality will be relatively straightforward, we simply need to be able to render the list of topics
to the user on the index
page. Let's walk through the basic features that we need to test for:
User can navigate to the
topics
index page successfullyOn
index
page the list oftopics
are shownEach
topic
title will link to theshow
page
Let's get started and create a spec for our topic
integration tests and add in the items that we're wanting to test for:
# spec/features/topic_spec.rb require 'rails_helper' describe 'navigate' do describe 'index' do it 'can be reached successfully' do visit topics_path expect(page.status_code).to eq(200) end xit 'renders the list of topics' do end xit 'each topic links to its show page' do end end end
Running rspec spec/features/topic_spec.rb
will give us the following error AbstractController::ActionNotFound: The action 'index' could not be found for TopicsController
. Let's get this working by creating a blank index
action in the TopicsController
and then creating a new index
template file:
# app/controllers/topics_controller.rb class TopicsController < ApplicationController def index end end
And then run: touch app/views/topics/index.html.erb
Now we can run the specs again and everything is passing. Now to create the second spec, let's add in a before
action so that we have access to a Topic
and then declare what we want to test:
# spec/features/topic_spec.rb require 'rails_helper' describe 'navigate' do before do @topic = Topic.create(title: "Sports") end describe 'index' do it 'can be reached successfully' do visit topics_path expect(page.status_code).to eq(200) end it 'renders the list of topics' do Topic.create(title: "Coding") visit topics_path expect(page).to have_content(/Sports|Coding/) end xit 'each topic links to its show page' do end end end
Running the specs will now return an error since it can't find the two elements we asked it to match. Notice the syntax here leverages the Regex matcher that is provided through Capybara, I find this a very handy way of finding elements on a page. Let's implement this feature by updating the index
action:
# app/controllers/topics_controller.rb class TopicsController < ApplicationController def index @topics = Topic.all end end
And then update the view template:
<!-- app/views/topics/index.html.erb --> <%= @topics.inspect %>
Notice how I'm using the very minimal amount of implementation code to get the tests passing? Taking small steps like this follows the Red, Green, Refactor pattern of behavior driven development. I like this approach because it makes the implementation more straightforward. Many times when I run into bugs in an application it's because I'm trying to do too much implementation at a single time and it makes it harder to sort out where a bug is occurring. Now that we have that spec passing let's refactor the view a little bit, first update the index
template so it calls on the _topic
partial using the built in syntactic sugar provided by rails:
<!-- app/views/topics/index.html.erb --> <%= render @topics %>
Now create a partial in the topics
directory:
<!-- app/views/topics/_topic.html.erb --> <div> <%= topic.title %> </div>
Running the specs again will show that the tests are still passing and now the view code is following Rails best practices by moving the topic
iterator into a partial. If you've never seen this design pattern before, it's a built in Rails convention that will look for a partial with the same name as the model inside of the corresponding view directory. To simplify this process, let's see how Rails does it step-by-step:
It sees the call of
<%= render @topics %>
It looks at the
topics
view directory for a partial with the same name as the model, in this case_topic
It iterates over the
topics
and calls the partial for each of the records returned from the query
Now let's setup the final spec:
# spec/features/topic_spec.rb it 'each topic links to its show page' do visit topics_path expect(page).to have_link(@topic.title, href: topic_path(@topic)) end
As expected this will fail, to implement the feature let's update the _topic
partial:
<!-- app/views/topics/_topic.html.erb --> <div> <%= link_to topic.title, topic %> </div>
Let's test this out in the browser by running the rails server and navigating to localhost:3000/topics
.
Running the specs again and you'll see that they're all passing, very nice work, we have a functional index
page that has been tested and refactored, it's a good feeling, right? Lastly I'm going to remove the spec/helpers/topics_helper_spec.rb
file since I don't like having pending specs on features that have been implemented and we won't use this helper. With that done we're ready to start implementing the rest of the CRUD functionality for our topics
.