Easels App with Test Driven Development: Part 3

Free Ruby on Rails Tutorial

Dive into our detailed Ruby on Rails tutorial that breaks down the process of including task management features in the Easels app, including tests to ensure users can mark their tasks as either complete or incomplete.

This exercise is excerpted from Noble Desktop’s past web development training materials. Noble Desktop now teaches JavaScript and the MERN Stack in our Full Stack Development Certificate. To learn current skills in web development, check out our coding bootcamps in NYC and live online.

Topics Covered in This Ruby on Rails Tutorial:

Test #4: Ensuring the User Can Mark Their To-do Complete, Test #5: Ensuring the User Can Mark Their To-do Incomplete

Exercise Overview

Now that the user can go to the Easels homepage, sign in, save their tasks to the database, and only see their own list of to-dos, we need to add the actual task management features. The purpose of the Easels app is to provide a simple way for a user to mark to-dos in their list as either complete or incomplete. We’ll add these features one-by-one, starting with a test that ensures the user can tell the app that their task is done.

  1. If you completed the previous exercises, you can skip the following sidebar. We recommend you finish the previous exercises (13A–13B) before starting this one. If you haven’t finished them, do the following sidebar.

    If You Did Not Do the Previous Exercises (13A–13B)

    1. Close any files you may have open.
    2. Open the Finder and navigate to Class Files > yourname-Rails Class
    3. Open Terminal.
    4. Type cd and a single space (do NOT press Return yet).
    5. Drag the yourname-Rails Class folder from the Finder to the Terminal window and press ENTER.
    6. Run rm -rf easels to delete your copy of the easels site.
    7. Run Git clone https://bitbucket.org/Noble Desktop/easels.Git to copy the easels Git repository.
    8. Type cd easels to enter the new directory.
    9. Type Git checkout 13B to bring the site up to the end of the previous exercise.
    10. Run bundle to install any necessary gems.
    11. Run yarn install—check-files to install JavaScript dependencies.

Test #4: Ensuring the User Can Mark a To-Do Complete

  1. Make sure Terminal is open. If you aren’t in the easels directory, type: cd easels

  2. Go to your code editor where the easels folder should be open.

  3. Generate a new feature:

    rails g rspec:feature user_marks_todo_complete
  4. In the new file, update as shown:

    require "rails_helper"
    
    RSpec.feature " User Marks Todo Complete", type: :feature do scenario "successfully" do
       end
  5. Just like in the last test, our user is going to need to go through the familiar motions of signing in, creating a new to-do, and saving it to the database. Open spec > features > user_can_see_own_todos_spec.rb so we can copy some of its code.

  6. Around lines 9–19, copy the middle three comments and the code under them (from #user signs in to click_on "Add To-Do").

  7. Back in user_marks_todo_complete_spec.rb, paste the code into the empty scenario:

    scenario "successfully" do
    
       #user signs in
       visit new_session_path
       fill_in "Email", with: "user@example.com"
       click_on "Sign In"
    
       #user creates a to-do via a form
       click_on "Add a New To-Do"
       fill_in "Title", with: "paint house"
    
       #user saves the to-do to the database
       click_on "Add To-Do"
    
    end
  8. We need to add two additional steps to make sure this new feature works. First the user needs to be able to click on a button that marks the to-do as complete. Then we want to verify that the to-do is marked as complete on the page. Below the pasted code add the following:

    click_on "Add To-Do"
    
       #user marks the to-do complete
       click_on "Complete"
    
       #verify that the to-do is marked complete
       expect(page).to have_css ".todos li.completed", text: "paint house"
    end

    NOTE: There’s a number of different ways in which we can style the tasks the user marks as done. While we set up our test so the application adds a completed class to the finished to-dos, you could style them with a strike-through, remove the text, etc. In your own apps it’s entirely up to you (and your front-end developer).

  9. Save the file and switch to Terminal.

  10. Run the new feature test by entering the following (in Sublime Text you can copy the filename by CTRL–clicking or Right–clicking on it and choosing Copy Name):

    rspec spec/features/user_marks_todo_complete_spec.rb

    As you may have expected, all the steps we pasted from the previous test are working. The test fails at the point where the user needs to click on "Complete" because Capybara can’t find it. Let’s add that button to our index view that displays the to-dos.

  11. Switch back to your code editor and open: app > views > todos > index.html.erb

  12. Inside the code that loops through the to-dos and displays them, add a conditional statement that adds an Incomplete button if the task has been marked as completed (in case the user made a mistake and did not actually complete a to-do) and otherwise shows a Complete button:

    <% @todos.each do |todo| %>
       <li>
        <%= todo.title %>
       <% if todo.completed? %>
          <%= button_to "Incomplete", root_path %>
       <% else %>
          <%= button_to "Complete", root_path %>
       <% end %>
      </li>
    <% end %>

    NOTE: In order for them to function, each button must route to somewhere else in our application. We don’t know where the buttons should lead to yet so we’re temporarily routing them both to the homepage.

  13. In our test we specified that finished tasks will have an additional class of completed. As shown in bold, add another conditional to the list item that adds that class only when appropriate:

    <li class="<%= 'completed' if todo.completed? %>">
  14. Save the file and switch to Terminal.

  15. Enter the rspec spec/features/user_marks_todo_complete_spec.rb command. (Remember that you can hit the Up Arrow key to automatically enter previous commands in Terminal.)

    Now the user can no longer click on the "Add To-Do" button that worked until we added the conditionals. The view can’t display the button because the `completed?’ method doesn’t exist. There are a few ways in which we can define this method. Let’s add a completed_at field to the Todo model by running a migration.

  16. Back in your code editor, open db > schema.rb and notice the fields we already have.

  17. In Terminal run a migration to add a completed_at field (with a timestamp value) to each to-do in our database:

    rails g migration AddCompletedToTodo completed_at:datetime
  18. Type rails db:migrate in the command line.

    NOTE: Feel free to look back at schema.rb to see that the migration added a new "completed_at" field that displays as a datetime. Now that we’ve updated the database, let’s go to the Todo model and define the completed? method there.

  19. In your code editor, open the Todo model: app > models > todo.rb

  20. As shown in bold, create a completed? method that tracks whether or not the database has a completed_at field for the specific task that it’s calling:

    class Todo < ActiveRecord::Base
       def completed?
          completed_at?
       end
    end

    NOTE: We want to know whether or not something is being returned, so we added the question marks. This matches the usage in the conditionals on the index page.

  21. Save the file and switch to Terminal.

  22. Run the test: rspec spec/features/user_marks_todo_complete_spec.rb

    Now that the to-dos are being saved, we have a problem with the "Complete" button’s routing. Because we temporarily routed it to go to the root_path, it’s trying to POST the results to the index page. We can’t do that and we don’t want to. There are many different ways to handle this. We could create a completed action in our TodosController. Let’s create a separate controller with create and destroy routes.

  23. Before we create a new controller, let’s get rid of the current error. We first need to add a route that works, so open config > routes.rb in your code editor.

  24. We want to create some additional completion routes that are associated with the Todo model, so let’s create a nested route. Add the following bold code:

    root to: "todos#index"
    resources :todos do
       resource :completion, only: [:create, :destroy]
    end
    resources :sessions

    NOTE: We only need create and destroy routes because we only need to be able to save the completion info to the database or delete it. We created a singular resource (without pluralization), because those kinds of routes do not reference an ID. Only model objects can have :id fields. We do not have a Completion model so plural resources are not a good choice here.

  25. Save the file and switch to Terminal.

  26. Type rails routes in the command line.

    The todo_completion POST route (the second one) looks like just what the doctor ordered! Notice that :todo_id is part of the route. It is a way to let Rails know which to-do to mark as completed. We will need to utilize this soon.

  27. Back in your code editor, switch to index.html.erb.

  28. In the else statement, around line 10, make the following bold edit to the Complete button so it will take us to the respective to-do’s todo_completion_path:

    <%= button_to "Complete", todo_completion_path(todo) %>
  29. Save the file and switch to Terminal.

  30. Run the test: rspec spec/features/user_marks_todo_complete_spec.rb

    Great, we know that the route works because we’re seeing a message that should be pretty familiar by now. We need to initialize a CompletionsController.

  31. Create a new controller:

    rails g controller completions
  32. Run the test: rspec spec/features/user_marks_todo_complete_spec.rb

    Another familiar error—now we need to define the 'create' method in our new CompletionsController. We know from experience that we’ll need to add a redirect inside the method. Let’s do that now.

  33. In completions_controller.rb add the following bold method and redirect:

    class CompletionsController < ApplicationController
    
       def create
          redirect_to root_path
       end
    
    end
  34. Save the file and switch to Terminal.

  35. Run the test: rspec spec/features/user_marks_todo_complete_spec.rb

    Now that the Complete button is finally functional, the test fails on the last step. It looked for the "paint house" text with the CSS that includes the.completed class but could find no matches. In order to actually mark a to-do complete, we need to add more code to our create action.

  36. Switch back to completions_controller.rb in your code editor.

  37. The first thing we need is to find the correct to-do. Add the following bold code:

    def create
       @todo = Todo.find(params[:todo_id])
       redirect_to root_path
    end

    This code searches for the to-do that was passed into the controller. It’s looking inside the Todo class, trying to find the correct one by looking for its ID. We wrote this as :todo_id because that’s how it’s passed in inside of our route.

    NOTE: If you need a refresher on the completions_path POST route we got the syntax from, go back to Terminal, type rails routes in the command line, and examine the second route’s URI Pattern.

  38. Once the database finds the correct to-do by its ID it needs to add a completed_at timestamp using the current time. Lastly it needs to save the results. Add more code to the create method as shown in bold:

    @todo = Todo.find(params[:todo_id])
    @todo.completed_at = Time.now
    @todo.save
    redirect_to root_path
  39. Save the file and switch to Terminal.

  40. Run the test: rspec spec/features/user_marks_todo_complete_spec.rb

    It passes! Now that we know we can successfully mark a to-do complete, let’s work on being able to mark them incomplete.

  41. Run all the tests using rspec to see that all four pass.

Full-Stack Web Development Certificate: Live & Hands-on, In NYC or Online, 0% Financing, 1-on-1 Mentoring, Free Retake, Job Prep. Named a Top Bootcamp by Forbes, Fortune, & Time Out. Noble Desktop. Learn More.

Test #5: Ensuring the User Can Mark a To-Do Incomplete

  1. Switch back to user_marks_todo_complete_spec.rb in your code editor.

  2. Duplicate the file, naming it: user_marks_todo_incomplete_spec.rb

    TIP: If you are using Sublime Text, you can CTRL–click or Right–click on the filename and choose Duplicate. At the bottom rename the duplicate file and hit Return.

  3. We need to make a few minor modifications. Around line 3 rename the feature:

    feature "user marks to-do in complete" do
       scenario "successfully" do
  4. Next we need the user to be able to click the Incomplete button. Our app is set up so that this button only shows once the user has clicked Complete, so we still want that step in this test. Under the click_on "Complete" code add a new step:

    #user marks the to-do complete
    click_on "Complete"
    
    #user marks the to-do incomplete
    click_on "Incomplete"
  5. Modify the last comment and its associated code as shown in bold:

    #verify that the to-do is marked in complete
    expect(page).to have_css ".todos li", text: "paint house"
    expect(page).not_ to have_css ".todos li.completed", text: "paint house"
  6. Save the file and switch to Terminal.

  7. Run the new test using the following command. (It may be easiest to bring up the previous test’s command, press the Left Arrow key until the letter c in complete is highlighted, then type in to make it read in complete):

    rspec spec/features/user_marks_todo_incomplete_spec.rb

    Our first failure is a RoutingError for the "Incomplete" button. Because we previously only updated the route for the Complete button, we are still using the temporary route to the root_path. Let’s find a replacement.

  8. Type rails routes in the command line.

    This time we want to use the todo_completion DELETE route (the third one). This is because we’re essentially deleting a completion.

  9. Back in your code editor, switch to index.html.erb.

  10. In the if statement, edit line 8 as shown in bold (replacing root_path):

    <% if todo.completed? %>
       <%= button_to "Incomplete", todo_completion_path(todo), method: :delete %>
  11. Save the file and switch to Terminal.

  12. Run the test: rspec spec/features/user_marks_todo_incomplete_spec.rb

    Again our test fails. We can’t click on the "Complete" button because no route matches the "destroy" :action and ActionView cannot render the template for it. Let’s add a destroy method to our CompletionsController. Just like create methods, destroy methods need a redirect because they do not have a corresponding view.

  13. Switch to completions_controller.rb in your code editor.

  14. The destroy method will work similarly to the create method. Copy the entire create method (around lines 3–8).

  15. Paste a copy directly below the first around line 10.

  16. As shown below, make the two bold edits to rename the method and remove the completed_at timestamp:

    def destroy
       @todo = Todo.find(params[:todo_id])
       @todo.completed_at = nil
       @todo.save!
       redirect_to root_path
    end
  17. Save the file and switch to Terminal.

  18. Run the test: rspec spec/features/user_marks_todo_incomplete_spec.rb

    It passes! Both the Complete and Incomplete buttons are now functional.

  19. Run all the tests using rspec to see that all five pass.

  20. We will continue with this app in the next exercise. To minimize confusion, leave the easels folder open in your code editor but close all the individual files that are open.

    You may have noticed that in our haste to get the incomplete test to pass we had to reuse quite a bit of code. That’s not very DRY. In the next exercise we’ll finish the Easels app by doing a bit of refactoring before we explore the app in a browser.

Noble Desktop Publishing Team

The Noble Desktop Publishing Team includes writers, editors, instructors, and industry experts who collaborate to publish up-to-date content on today's top skills and software. From career guides to software tutorials to introductory video courses, Noble aims to produce relevant learning resources for people interested in coding, design, data, marketing, and other in-demand professions.

More articles by Noble Desktop Publishing Team

How to Learn Coding

Master Coding with Hands-on Training. Learning How to Code in JavaScript, Python, and Other Popular Languages Can Pave the Way to a Job in Tech Such As Web Development, Data Science & Analytics, or Software Engineering.

Yelp Facebook LinkedIn YouTube Twitter Instagram