Skip to content
This repository has been archived by the owner on May 8, 2018. It is now read-only.

Writing Specifications with MiniTest::Spec

moonglum edited this page Oct 17, 2011 · 3 revisions

We are using MiniTest::Spec, Fabricator and Capybara to write the Specifications for the dreamforge project. We write Model Specs and Integration Specs. Every feature first needs to be specified via MiniTest before it gets implemented. Every Bug needs to be formulated as a Regression Test.

Model Specs & Fabricators

For every model we create a spec file in test/models/ named MODELNAME_spec.rb and a fabricator in test/fabricators/ named MODELNAME_fabricator.rb. In the fabricator create the model with all fields filled in. To invalidate your model you can remove attributes from your fabricator. You can find all information on Fabricator here.

Integration Specs

For each page the user might visit we create an integration test in test/integration named CONTROLLERNAME_integration_spec.rb. We use Capybara, so there are some additional matchers available.

Adding Assertions to Minitest

To add a expectation, go to the test/support directory and add the corresponding expectation to custom_capybara_expectations.rb if it is Integration-Test specific or custom_minitest_expectations.rb if it is of general usage.

General Expectations

Are added as follows to the MiniTest::Assertions module:

def assert_valid(obj, msg = nil)
  msg = message(msg) { "Expected #{mu_pp(obj)} to be valid" }
  assert obj.valid?, msg
end

And then add the expectation to the module MiniTest::Expectations via the infect_an_assertion method like this:

infect_an_assertion :assert_valid, :must_be_valid

Integration Test specific Expectations

Are added as follows to the Capybara::Sessions class:

def has_flash_message?(message)
  within '#flash' do
    has_content? message
  end
end

And then add the expectation as a matcher as follows:

CapybaraMiniTestSpec::Matcher.new(:has_flash_message?)

Available Expectations

Only positive expectations listed – for negative expectations switch must to wont.

Expectations from MiniTest::Spec

Based on the list from RubyInside. Thanks, Peter!

  • obj.must_be(operator, expected): for example, 10.must_be :< , 11
  • obj.must_be_close_to: the equivalent of assert_in_delta
  • obj.must_be_empty: Fails unless obj.empty?
  • obj.must_be_instance_of(klass): Fails unless obj.class == klass
  • obj.must_be_kind_of(klass): Fails unless obj is of class klass or klass is one of its superclasses
  • obj.must_be_nil
  • obj.must_be_same_as: tests for true object equality
  • lambda {}.must_be_silent
  • obj.must_be_within_delta
  • obj.must_be_within_epsilon
  • obj.must_equal(other): Does a ==/eql? comparison between two objects.
  • obj.must_include(other)
  • obj.must_match(regex)
  • lambda {}.must_output(stdout, [stderr..]): The block should have certain output on stdout or stderr. Set stdout to nil just to check stderr.
  • lambda {}.must_raise(exception)
  • obj.must_respond_to(message)
  • obj.must_throw(sym)

Custom General Expectations

  • must_be_valid
  • must_be_invalid

Integration-Test specific Expectations from Capybara

  • page.must_have_selector('table tr')
  • page.must_have_selector(:xpath, '//table/tr')
  • page.must_have_no_selector(:content)
  • page.must_have_xpath('//table/tr')
  • page.must_have_css('table tr.foo')
  • page.must_have_content('foo')

Custom Integration-Test specific Expectations

  • must_have_flash_message
  • must_have_link_to(path)

Actions provided by Capybara

  • visit(path): Goes to the specified page, always uses GET.
  • current_path: Returns the current path
  • click_link(id-or-link-text)
  • click_button(id-or-button-text)
  • click_on(id-or-text-of-link-or-button
  • fill_in('First Name', :with => 'John')
  • fill_in('Password', :with => 'Seekrit')
  • fill_in('Description', :with => 'Really Long Text...')
  • choose('A Radio Button')
  • check('A Checkbox')
  • uncheck('A Checkbox')
  • attach_file('Image', '/path/to/image.jpg')
  • select('Option', :from => 'Select Box')
  • find_field('First Name').value
  • find_link('Hello').visible?
  • find_button('Send').click
  • find(:xpath, "//table/tr").click
  • find("#overlay").find("h1").click
  • all('a').each { |a| a[:href] }
  • within(selector, &block): Scopes the block to the given selector
  • within_fieldset(selector, &block): Scopes the block to the given selector
  • within_table(selector, &block): Scopes the block to the given selector
  • save_and_open_page: Take a snapshot of the page as it currently is and take a look at it

Custom Action

  • sign_in: Goes to the sign-in page, logs in as a normal user. Returns the logged-in user
  • sign_in(user): Goes to the sign-in page, logs in as user