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:
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.
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
- 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
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.