Using BDD to Create an Index View
Learn how to leverage behavioral driven development (BDD) to drive the functionality for building an index view for a Ruby on Rails application.
Guide Tasks
  • 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:

large

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:

  1. User can navigate to the topics index page successfully

  2. On index page the list of topics are shown

  3. Each topic title will link to the show 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:

  1. It sees the call of <%= render @topics %>

  2. It looks at the topics view directory for a partial with the same name as the model, in this case _topic

  3. 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.

large

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.

Resources