Jonathan Bennett

Code w/ Jason Meetup - CRMmy Review

At last Thursday’s Code With Jason Meetup I was in the hot seat with CRMmy, my CRM side project. We spent most of our time talking through some feature tests, taking them from a single initial stream of consciousness test to a higher quality set of tests.

When I start building out a new feature, I typically start with walking through the navigation and screen I wish were there using feature tests. This ends up being very low level which is hard to work with long term, but it means the requirements are explicit. The feature I was looking at was left at this stage and look like this initially:

RSpec.feature "UsersCanManageProjects", type: :feature do
  scenario "successfully", :js do
    user = create(:user, :with_account)
    visit root_path(as: user)
    click_on "Select"
    click_on "Projects", match: :first
    click_on "Add Project"
    fill_in "Name", with: "My Project"
    click_on "Create Project"
    click_on "My Project"
    click_on "Edit"
    fill_in "Name", with: "New Project"
    click_on "Update Project"
    expect(page).to have_content "New Project"
  end
end

Some explanation was required for some parts of the test, for example the click_on "Select" is needed to select the active account and the match: :first is needed to pick a menu since there is a desktop and mobile menu. As usual, when you code needs to be explained, you need better code! We addressed that later on.

The first change I wanted to make was with the scenario name itself "successfully" is obviously bad and reflects that I hadn’t thought through exactly what I was going to be testing.

Rather than working through that through, Jason suggested breaking it out into a few separate tests. I’m not one to reach for AI in a scenario like this, but at Jason’s recommendation I did, and with a simple prompt of break up the following test into multiple scenarios, it gave me something pretty close to this:

RSpec.feature "UsersCanManageProjects", type: :feature do
  let(:user) { create(:user, :with_account) }

  before do
    visit root_path(as: user)
    click_on "Select"
  end

  scenario "navigate to projects page" do
    click_on "Projects", match: :first
    expect(page).to have_content "Projects"
  end

  scenario "add a project" do
    visit projects_path
    click_on "Add Project"
    fill_in "Name", with: "My Project"
    click_on "Create Project"

    expect(page).to have_content "My Project"
  end

  scenario "edit a project" do
    project = create :project, account: user.accounts.first

    visit projects_path
    click_on project.name
    click_on "Edit"
    fill_in "Name", with: "New Project"
    click_on "Update Project"

    expect(page).to have_content "New Project"
  end
end

The test is quite a bit longer, but much more specific. Each scenario should likely fail for more explicit reasons since they are significantly more isolated, they are more clear and understandable, and critically, I can see where I should be putting additional tests for the next thing I add.

We still hadn’t addressed some of the icky “explain your code” issues. This ended up being a pretty simple set of changes:

  1. Add CSS IDs to the menus
  2. Extract a helper method
def click_nav(label)
	within "#desktop-nav" do
		click_on label
    end
end

scenario "navigate to projects page" do
    click_nav "Projects"
    expect(page).to have_content "Projects"
end

Overall, this test went from being very low level, way to large, with several “gotchas”, into a much understandable test that provides a better foundation to continue building on, and giving a few improvements that were used to improve other tests.