Skip to main content

Getting started with Ruby on Rails 3.2 and MiniTest - a Tutorial

For fun, I thought I would start a new Ruby on Rails project and use MiniTest instead of Test::Unit. Why? Well MiniTest is Ruby 1.9s testing framework dejour, and I suspect we will see more and more new projects adopt it. It has a built in mocking framework and RSpec like contextual syntax. You can probably get away with fewer gems in your Gemfile because of that.

Getting started is always the hardest part - let's jump in with a new rails project

rails new tddforme --skip-test-unit

Standard stuff. MiniTest sits nicely next to Test::Unit, so you can leave it in if you prefer. I've left it out just to keep things neat and tidy for now.

Now we update the old Gemfile:

group :development, :test do
  gem "minitest"
end

and of course, bundle it all up.....from the command line:

$ bundle

Note that if you start experiencing strange errors when we get in to the generators later on, make sure you read about rails not finding a JavaScript runtime. Fire up your rails server and check everything is hunky dory if you fancy.

A Model

For the purposes of testing our test framework, we are going to put together a small model. Let's call it DevelopmentMethodology and give it a couple of fields - perhaps a name and a description.

$ rails g model DevelopmentMethodology name:string description:string

Set up the test structure

Now we have a model and a migration. I like to write my first tests now. Yes I know, crazy, before I've even migrated......anyway, because I created my project with no test-unit, I don't even have a test directory, so I'll create it now along with a few other subdirectories that will mimic the usual testing structure

$ mkdir test
$ mkdir test/fixtures
$ mkdir test/unit

Now, inside the test directory, I'll set up a basic test_helper file:

$ touch test/test_helper.rb

The code for my test_helper is almost boilerplate - I'm putting the Rails Environment into "test" and loading up the Rails and then simply including the minitest framework.

ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require "minitest/autorun"

If you kept test-unit in your project, you'll have a test_helper already that will look similar to this, but won't yet bring in minitest. In this case you can choose to replace the "require" statement in that test_helper.rb file or you can create a new minitest_helper.rb file with the code.

Petit pause

Let's take a breath - go get a cup of coffee. We've covered a lot of ground. So far, the basic steps we have gone through are:

  • Create a new Rails project, skipping the test-unit framework
  • Update our Gemfile to include the minitest
  • Bundle
  • Create test directory structure
  • Create test_helper.rb

Writing a test

At last, we can write our first test! Create a new file in the test/unit directory - call it whatever you want, I've chosen to name mine "development_methodology_test.rb" and a basic outline goes like this:

require 'test_helper'

class TestDevelopmentMethodology < MiniTest::Unit::TestCase
  def test_our_test_framework_can_fail
    assert false
  end
end

First, we include our test_helper.rb. Then we define a class to test our DevelopmentMethodology model - it inherits from on of the classes in MiniTest. Easy peasy. Then, we create a method called test_our_test_framework_can_fail in which we simply assert false. Why would I bother to do this? Well, this little piece of code allows me to verify that I've set up MiniTest correctly - I haven't yet bothered to write any tests against my own application yet. However, when I run the test, I get valuable output (assuming everything has been set up correctly) and I can see that MiniTest is running and is producing a failing test:

[]$ ruby -Itest test/unit=development_methodology_test.rb
# Running tests:

F

Finished tests in 0.000601s, 1665.0543 tests/s, 1665.0543 assertions/s.

  1) Failure:
test_our_test_framework_can_fail(TestDevelopmentMethodology) [test/unit/development_methodology_test.rb:5]:
Failed assertion, no message given.

1 tests, 1 assertions, 1 failures, 0 errors, 0 skips

I'll swiftly rewrite my test now that I know everything is working. I'm going to put a condition that a Development Methodology must contain a name.

require 'test_helper'

class TestDevelopmentMethodology < MiniTest::Unit::TestCase
  def test_development_methodology_has_name
    development_methodology = DevelopmentMethodology.new
    assert !development_methodology.save
  end
end

And run it

[]$ ruby -Itest test/unit=development_methodology_test.rb

# Running tests:

E

Finished tests in 0.032036s, 31.2147 tests/s, 0.0000 assertions/s.

  1) Error:
test_development_methodology_has_name(TestDevelopmentMethodology):
ActiveRecord::StatementInvalid: Could not find table 'development_methodologies'

It errors! But of course it does - we haven't run a migration yet. Let's do that and at the same time prepare our test database

$ rake db:migrate
$ rake db:test:load

Run our test again and you should get a failing test

[]$ ruby -Itest test/unit=development_methodology_test.rb

# Running tests:

E

Finished tests in 0.032036s, 31.2147 tests/s, 0.0000 assertions/s.

  1) Failure:
test_development_methodology_has_name(TestDevelopmentMethodology) [test/unit/development_methodology_test.rb:5]: Failed assertion, no message given.

1 tests, 1 assertions, 1 failures, 0 errors, 0 skips

Refactor our test

The first thing I'm going to do is refactor my test - because MiniTest includes both assert and refute statements, I think my test could be more readable if I write it like this (of course, running the test again produces a failed test as above):

require 'test_helper'

class TestDevelopmentMethodology < MiniTest::Unit::TestCase
  def test_development_methodology_has_name
    development_methodology = DevelopmentMethodology.new
    refute development_methodology.save
  end
end

Make the test pass

Making this test pass is simple - we just have to validate the presence of name. Edit app/models/development_methodology.rb and add in our validates

class DevelopmentMethodology < ActiveRecord::Base
  attr_accessible :description, :name
  validates_presence_of :name
end

and now our test passes

$ ruby -Itest test/unit=development_methodology_test.rb
Run options: --seed 20660

# Running tests:

.

Finished tests in 0.139823s, 7.1519 tests/s, 7.1519 assertions/s.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

Make it purty

Now MiniTest comes with a lot built in - want your output to be in lovely colours - no problem - just run your tests like this and witness the beautiful and fabulous tests:

$ ruby -rminitest/pride -Itest test/unit=development_methodology_test.rb

Spec

A big plus point for MiniTest is the inclusion of spec by default. Watch what happens when we change our test to look like this:

require 'test_helper'

describe DevelopmentMethodology do
  it "must have a name" do
    development_methodology = DevelopmentMethodology.new
    development_methodology.save.must_equal false
  end

end

If you re-run your tests now, you get passing tests (obviously, because we've simply rewritten a passing test), but you've used some nice contextual RSpec like syntax all rolled up in MiniTest

That's good for today

I think that's enough of an introduction to using MiniTest with Rails. Hopefully, you've got some code to get you going so you can jump right in an explore MiniTest.

Comments

  1. If you use require_relative '../test_helper' you can save the -Itest and do not need to add another load-path to your app.

    Also check out minitest-reporters, it has a nice red-green 'Default' reporter, so you no longer need to read the output, it's either red or green ;)

    ReplyDelete
  2. Good tip with require_relative.....Thanks.

    I'll check out minitest-reporters

    ReplyDelete
  3. If you want a more immediate start to using MiniTest in a Rails app, check out minitest-rails.

    http://blowmage.com/2012/07/10/announcing-minitest-rails

    ReplyDelete

Post a Comment

Popular posts from this blog

Rails 3.2, MiniTest Spec and Capybara

What do you do when you love your spec testing with Capybara but you want to veer off the beaten path of Rspec and forge ahead into MiniTest waters? Follow along, and you'll have not one, but two working solutions. The setup Quickly now, let's throw together an app to test this out. I'm on rails 3.2.9. $ rails new minicap Edit the Gemfile to include a test and development block group :development, :test do gem 'capybara' gem 'database_cleaner' end Note the inclusion of database_cleaner as per the capybara documentation And bundle: $ bundle We will, of course, need something to test against, so for the sake of it, lets throw together a scaffold, migrate our database and prepare our test database all in one big lump. If you are unclear on any of this, go read the guides . $ rails g scaffold Book name:string author:string $ rake db:migrate $ rake db:test:prepare Make it minitest To make rails use minitest , we simply add a require ...

Poor person's guide to managing Ruby versions

Understanding the guts of Ruby Version Management by rolling your own I've been tinkering with a fresh install of Ubuntu 12.10, setting up a nice clean development environment. One of the first things to do, of course, is implement some sort of Ruby version management. RVM and rbenv seem to be the clear winners in this arena, though there are a lot of tools out there that do a similar job . Writing your own version management for your Rubies isn't actually all that difficult. At it's core, we need need two things: A way to segregate the executables of the various versions A way to call the versions at will Segregating versions is trivial - working with files and folders, we can put the various versions into named directories. Actually executing our different versions is not all that difficult either. One way would be to create aliases with version numbers and explicitly call those when we want to use them. The more popular way, however, is to manipulate our PATH ...