Cesare Ferrari

July 25, 2022

Testing Hotwire

pexels-pixabay-416322.webp
pexels-pixabay-416322.webp 110 KB

Let’s see how to test views that have Turbo frames updated with Hotwire.

I am now generating a model in my Rails application. The model name is Document and it represents a document. Ultimately, the document will have an actual uploaded file, but for now, the Document model will only have a name and a description.

rails g model Document name description:text
      invoke  active_record
      create    db/migrate/20220725172353_create_documents.rb
      create    app/models/document.rb
      invoke    test_unit
      create      test/models/document_test.rb
      create      test/fixtures/documents.yml

After creating the database table with rails db:migrate I can add a system test before creating the controller actions and views.

I will generate the test with a Rails generator:

rails generate system_test Document
      invoke  test_unit
      create    test/system/documents_test.rb

This will create a new skeleton system test similar to this:

# test/system/documents_test.rb

require "application_system_test_case"

class DocumentsTest < ApplicationSystemTestCase
  # test "visiting the index" do
  #   visit documents_url
  #
  #   assert_selector "h1", text: "Document"
  # end
end

Testing document index and show views

Before creating a document, I want to make sure I can view the list of all my documents (the index page), and the detailed view of one single document (the show page).

For this purpose, I create the first test, which clicks on a link in the document index page and views the show page which shows the document details. In the show page I expect to see an h1 tag with the document name.

This test, and the ones that follow, will use Rails fixtures to set up the objects to test.

The fixtures file was auto generated by Rails when we created this test, and has content similar to this:

# test/fixtures/documents.yml

one:
  name: Document name
  description: This is the first document.

two:
  name: MyString
  description: MyText

This lets us reference to the first fixture with the following code in the setup section of the test file:

# test/system/documents_test.rb

class DocumentsTest < ApplicationSystemTestCase
  setup do
    @document = documents(:one)
  end
end

Here’s the full test:

# test/system/documents_test.rb

class DocumentsTest < ApplicationSystemTestCase
  setup do
    @document = documents(:one)
  end

  test "showing a document" do
    visit documents_path
    click_link @document.name

    assert_selector "h1", text: @document.name
  end
end

If I run the test now, with rails test:system, I get an error, because we don’t yet have a route to the documents index page:

Error:
DocumentsTest#test_showing_a_document:
NameError: undefined local variable or method `documents_path' 

I can add the routes for index and show with this syntax:

# config/routes.rb

  resources :documents, only: [:index, :show]

Re-running the test shows that we are missing the whole DocumentsController:

Error:
DocumentsTest#test_showing_a_document:
ActionController::RoutingError: uninitialized constant DocumentsController

After adding the controller file and the index action in it:

# app/controllers/documents_controller.rb

class DocumentsController < ApplicationController
  def index
  end
end

The test now shows I am missing the view template:

Error:
DocumentsTest#test_showing_a_document:
ActionController::MissingExactTemplate: DocumentsController#index is missing a template for request formats: text/html

Adding the view template:

mkdir app/views/documents
touch app/views/documents/index.html.erb

Finally, we are getting somewhere. We can see the page, but it’s empty, and there is no link to the first document:

Error:
DocumentsTest#test_showing_a_document:
Capybara::ElementNotFound: Unable to find link "Document name"

Adding the link to the document in the view and the documents collection in the controller:

# app/controllers/documents_controller.rb

class DocumentsController < ApplicationController
  def index
    @documents = Document.all
  end
end# app/views/documents/index.html.erb


<% @documents.each do |document| %>
  <div><%= link_to document.name, document_path(document) %></div>
<% end %>

If I run the test at this point, it tells me I am missing the show action.

DocumentsTest#test_showing_a_document:
AbstractController::ActionNotFound: The action 'show' could not be found for DocumentsController

I am then adding the action to the controller, and the corresponding view:

# app/controllers/documents_controller.rb

class DocumentsController < ApplicationController
  ...

  def show
    @document = Document.find(params[:id])
  end
end# app/views/documents/show.html.erb

<h1><%= @document.name %></h1>

And with these changes, the test passes.

Great! I have added a model, views and a controller with the index and show actions, but I still have to test creating a document using Turbo Drive.

This will be the topic of my next post.

Photo by Pixabay from Pexels


Check out my site at www.ferrariwebdevelopment.com for more blog posts and insights.