We’ve recently been working on increasing the integration test coverage for a client app that is very form heavy.
Eventually a lot of the forms on this application are going to be redesigned and restructured but before we get started on a big backend refactor we wanted to make sure the frontend coverage is up to scratch.
Typically, the best way to do this is to write Capybara based request specs. This works well but with big forms, it can get a bit unwieldy with a lot of copy and paste between tests. Worse, if your forms are bound to change going back over all the tests and changing CSS locators or Capybara method calls is a bit of a pain.
To work around these issues we created a gem called form_object_model. This gem lets you define the fields of your forms separately from the tests that use the forms. For example, with a form that looks something like this:
Typically, in a request spec you’d call Capybara directly to fill out the form, like this:
select("Mr", :from => "Title")
fill_in("Name", :with => "Joe Bloggs")
Instead, with the form_object_model gem, you can declare the fields of the form in an RSpec let block like this:
FormObjectModel::Form.new do |form|
form.select :title, "Title"
form.text_field :name, "Name"
Then you can fill out the form using assignment like so:
form.title = "Mr"
form.name = "Joe Bloggs"
And test them like this:
form.name.should have_value("Joe Bloggs")
The advantage of this is that if your form structure changes you only need to change the definition of the form, not every test that uses it. Plus, IMO, the syntax is a lot nicer and easier to read than a mishmash of direct Capybara calls.
This is somewhat inspired by the Site Prism gem, which is inspired by the Page Object Model from Selenium. But we’ve pared it back and made it targeted specifically towards making forms easier to test. So if you want to use a similar model for page navigation and content testing you should give Site Prism a go, but if you want a simple way to DRY up your form tests and make them a bit easier to read and maintain, give form_object_model a shot.