Rendering a List of Posts via BDD in Rails
In this guide we are going to render records on our `Posts` page. And as usual, we'll start by building a test case in RSpec.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

In this guide we are going to render records on our Posts page. And as usual, we'll start by building a test case. Open the features/post_spec.rb file and add another test to our index block.

# spec/features/post_spec.rb

    it 'has a list of posts' do
      post1 = Post.create(date: Date.today, rationale: "Post1")
      post2 = Post.create(date: Date.today, rationale: "Post2")
      visit posts_path
      expect(page).to have_content(/Post1|Post2/)
    end

In this code, we are checking to see if there are a list of posts. We are creating two posts, visiting the path and then we expect the page to have content from both the post variables. If you're thinking there's a lot of duplicate code here, you're right. We'll come back later and refactor the code. Traditionally you don't want to refactor code right away since it's hard to detect a failure. For example, if you perform a refactor, then create a test and it fails, we would have to waste time figuring out if the problem was from our implementation or the refactor we performed.

If you run rspec you'll see that we have two failures.

large

One failure is related to the the post logic. The second failure is because a user is not logged in. Let's add in a call to our login test helper method to mimic a user login.

# spec/features/post_spec.rb

describe 'navigate' do
  before do
    user = User.create(email: "test@test.com", password: "asdfasdf", password_confirmation: "asdfasdf", first_name: "Jon", last_name: "Snow")
    login_as(user, :scope => :user)
  end

  # ... rest of the file not shown for brevity sake

If you run rspec, it will now only throw a single failure. To build out the initial feature, open the posts_controller.rb file. In the index method, add this line of code:

# app/controllers/posts_controller.rb

def index
  @posts = Post.all
end

This code will run a database query and gather all the available records into the variable @posts. As a result, the records will be available to the index action and associated template. Next, open the index.html.erb page and update the code to look like this:

<!-- app/views/posts/index.html.erb -->

<%=  @posts.inspect %>

If you run this, all of our tests will be green. Now, start the rails server and refresh the browser. You'll see all the records in their raw form (if you've never used inspect before it prints out the raw object value and all of its metadata).

large

With that it working, let's get start refactoring the code.

First, let's eliminate the duplicate code in the post_spec.rb test and place it in a before block.

# spec/features/post_spec.rb

describe 'index' do
  before do
    visit posts_path
  end

  it 'can be reached successfully' do
    expect(page.status_code).to eq(200)
  end

  it 'has a title of Posts' do
    expect(page).to have_content(/Posts/)
  end

  it 'has a list of posts' do
    post1 = Post.create(date: Date.today, rationale: "Post1")
    post2 = Post.create(date: Date.today, rationale: "Post2")
    visit posts_path
    expect(page).to have_content(/Post1|Post2/)
  end
end

If you notice, we are not moving our call to visit posts_path in "has a list of posts" because we need to visit the page AFTER the posts are created. If we visit the page before the posts are created it won't find any posts.

Next, let's work on our index.html.erb file. Right now we're printing out the raw object values to the screen and they're all running together. Let's polish this up a bit and have the records shown on their own lines. We'll iterate over each post and display the values like this:

<!-- overtime-app/app/views/posts/index.html.erb -->

<h1>Posts</h1>

<% @posts.each do |post| %>
  <%= post.inspect %>
  <hr>
<% end %>

Run rspec and everything should work. Open the browser, and you'll see:

large

Now, each post is on a different line. However, we need to put it in a table form because that's what the client requested for the page.

Let's implement a Bootstrap table to clean this UI up. In order to make the table dynamic, inside the <tbody> tag, we'll iterate over each post and display the three fields, namely: date, rationale and the user_id. For user_id, we are only going to display the last name for now. We'll later add the functionality to display the full name.

<!-- overtime-app/app/views/posts/index.html.erb -->

<h1>Posts</h1>

<table class="table table-striped table-hover">
  <thead>
    <tr>
      <th>
        #
      </th>
      <th>
        Date
      </th>
      <th>
        User
      </th>
      <th>
        Rationale
      </th>
    </tr>
  </thead>
  <tbody>
    <% @posts.each do |post| %>
      <tr>
        <td>
          <%= post.id %>
        </td>
        <td>
          <%= post.date %>
        </td>
        <td>
          <%= post.user.last_name %>
        </td>
        <td>
          <%= post.rationale %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

Now, if we test this out it will fail because our seed data doesn't have users. So, go to seeds.rb and add the user field.

# db/seeds.rb

@user = User.create(email: "test@test.com", password: "asdfasdf", password_confirmation: "asdfasdf", first_name: "Jon", last_name: "Snow")

puts "1 User created"

100.times do |post|
  Post.create!(date: Date.today, rationale: "#{post} rationale content", user_id: @user.id)
end

puts "100 Posts have been created"

Next, run bundle exec rake db:setup to clean up our sample data and start the rails server. Hit refresh on your browser and this is how it will look:

large

This looks fine, but there's one more thing we need to do. If we go to the console real quick and update the rationale of the first record with a long name,

large

You'll see that the entire output on the browser gets skewed because the rationale is too long.

To fix this, we are going to use a method called truncate. Go to index.html.erb and change the rationale code, like this:

<!-- overtime-app/app/views/posts/index.html.erb -->

<%= truncate(post.rationale) %>

That's it. We're good to go with this core functionality.

Resources