SitePrism is awesome and you should use it

I mentioned SitePrism yesterday and so I wanted to go in a little more depth. From my example yesterday, I used an inventory show page and an inventory index page. I had a find_inventory method on inventory index and a displayed? method on inventory show. I actually reconsidered the displayed? method and so I’m going to show how to use SitePrism to validate that the page has loaded in a different way.

Let’s look at the InventoriesIndex SitePrism page:

module Inventories
  class InventoriesIndex < SitePrism::Page
    set_url '/inventories'
    set_url_matcher %r{/inventories}
    section :list, "#inventories-list" do
      elements :inventories, '.inventory'
      def selector_for_inventory(inventory)
        "#inventory_#{inventory.id.to_s}_row"
      end

      def find_inventory(inventory)
        find selector_for_inventory(inventory)
      end
    end
  end
end

This makes my tests much each to read. Instead of typing

find "#inventory_#{inv.id.to_s}_row"

in my test, it instead looks like:

find_inventory(inv)

SO MUCH PRETTIER!

Let’s look at how SitePrism can help our InventoryShow page:

module Inventories
  class InventoriesShow < SitePrism::Page
    set_url '/inventories/{id}'
    set_url_matcher %r{/inventories/\d+}
    
    section :inventory_info, '#inventory-information' do
      element :name, '.inventory-name'
    end
  end
end

With just that little bit, we can validate that the page loaded what we want:

expect(page.inventory_info.name.text).to include inv.name

Overall, so much nicer than having all the css/xpath selectors all over your RSpec tests. Check it out!

Integration Tests: Cucumber vs. RSpec

I’ve recently started writing integration tests in RSpec instead of Cucumber and I have been LOVING IT. It’s taken much less time and a lot less terrible code. Here is what I need for a test using Cucumber:

features/show_inventory.feature

Background:
    And I am signed in as a user
    And an inventory "I" exists

Scenario: I can view the inventory page from the inventory list page
    Given I am on the inventories page
    When I click on the inventory row for that inventory "I"
    Then I should be on inventory "I"'s page

features/inventory_steps.rb

Given %r{^I am signed in as an? #{capture_model}$} do |model_name|
  user = create_model model_name
  login_with(user.email, user.password)
  @current_user = user
end

When %r{^I click on the inventory row for #{capture_model}$} do |model_name|
  inventory = find_model model_name
  App.inventories_index.list.find_inventory(inventory).click
end

The steps that aren’t covered manually are covered using a tool called pickle. Pretty useful, but can also be sorta frustrating. We also use SitePrism for both styles of tests and if you aren’t using it, you should reconsider.

So what does an RSpec integration test look like? If I was covering the same simple problem, here’s what I would do:

spec/features/user_inventory_spec.rb

require 'rails_helper'

RSpec.feature 'load inventory as user', :js do
  let(:user) { create :user }
  before do
    3.times do
      create :inventory
    end
  end
  
  scenario 'select inventory and view the page' do
    Sessions::SessionsNew.sign_in user.email, 'my_test_pass'
    inventory = Inventory.first
    
    Inventories::InventoriesIndex.new.tap do |page|
      page.load
      page.list.find_inventory(inventory).click
    end
    
    Inventories::InventoriesShow.new.tap do |page|
      page.displayed?
    end
  end
end

And that’s it. I just have to have the SitePrism pages for Sessions::SessionsNew, Inventories::InventoriesIndex, and Inventories::InventoriesShow designed correctly as well. But those I will use multiple times in other tests, so after the inital setup, I save a lot of time. In this case, I had already set the pages up while we were using Cucumber, so I didn’t have to do anything new to start writing tests in RSpec.

TL;DR unless you have a PM that is actually writing your Cucumber tests, just do them in RSpec. You’ll save yourself many a headache.

Test Unit + Shoulda

Do you use Thoughtbot’s shoulda matchers? If not you should. Super handy for testing Rails applications. However, I did ran into an issue that I banged my head on the wall for an hour before noticing the very simple problem.

I was adding some tests to a controller, and, in this case, they were already written but commented out, so I was just editing them and getting them to work. We use the shoulda matchers and the accompanying context blocks to organize our tests, but the tests I was editing were just using basic Test Unit syntax (test “this is what i’m testing” do). I kept getting an ArgumentError (1 for 2). However, the function I was testing only had one argument, so I couldn’t figure out why it was asking for a second. The issue ended up being that the tests were within a context block and context blocks do not work with Test Unit. Once I converted it to should “this is what I’m testing” do, it worked perfectly.

Simple mistake, but easy to miss if you are trying to fix up legacy code.