MiniTest, as the name suggests, is a small and fast unit testing framework. Shipped with Ruby 1.9, MiniTest supports a complete suite of testing capabilities such as TDD, BDD, mocking, and benchmarking.

This quick reference aims to demonstrate MiniTest's main concepts and provide real world examples to get you acquainted quickly. Let's start with MiniTest::Spec.

MiniTest::Spec

Provides RSpec-like matchers and contexts right out of the box.

require 'minitest/autorun'

describe Hipster, "Demonstration of MiniTest" do
  before do
    @hipster = Hipster.new
  end

  after do
    @hipster.destroy!
  end

  subject do
    Array.new.tap do |attributes|
      attributes << "silly hats"
      attributes << "skinny jeans"
    end
  end

  let(:list) { Array.new }

  describe "when asked about the font" do
    it "should be helvetica" do
      @hipster.preferred_font.must_equal "helvetica"
    end
  end

  describe "when asked about mainstream" do
    it "won't be mainstream" do
      @hipster.mainstream?.wont_equal true
    end
  end
end

Matchers (must | wont)

In most cases you can switch between must for positive expectations and wont for negative expectations.

Assertion Examples
must_be list.size.must_be :==, 0
must_be_close_to subject.size.must_be_close_to 1,1
must_be_empty list.must_be_empty
must_be_instance_of list.must_be_instance_of Array
must_be_kind_of list.must_be_kind_of Enumerable
must_be_nil list.first.must_be_nil
must_be_same_as subject.must_be_same_as subject
must_be_silent proc { "no stdout or stderr" }.must_be_silent
must_be_within_epsilon subject.size.must_be_within_epsilon 1,1
must_equal subject.size.must_equal 2
must_include subject.must_include "skinny jeans"
must_match subject.first.must_match /silly/
must_output proc { print "#{subject.size}!" }.must_output "2!"
must_respond_to subject.must_respond_to :count
must_raise proc { subject.foo }.must_raise NoMethodError
must_send subject.must_send [subject, :values_at, 0]
must_throw proc { throw :done if subject.any? }.must_throw :done

MiniTest::Unit::TestCase

Provides a rich set of assertions to make your tests clean and readable.

require 'minitest/autorun'

class TestHipster < MiniTest::Unit::TestCase
  def setup
    @hipster = Hipster.new
    @list    = Array.new
    @subject = ["silly hats", "skinny jeans"]
  end

  def teardown
    @hipster.destroy!
  end

  def test_for_helvetica_font
    assert_equal "helvetica!", @hipster.preferred_font
  end

  def test_not_mainstream
    refute @hipster.mainstream?
  end
end

Assertions (assert | refute)

Toggle between assert for positive assertions and refute for negative assertions.

Assertion Example
assert assert @subject.any?, "empty subjects"
assert_block assert_block { @subject.any? }
assert_empty assert_empty @list
assert_equal assert_equal 2, @subject.size
assert_in_delta assert_in_delta @subject.size, 1,1
assert_in_epsilon assert_in_epsilon @subject.size, 1, 1
assert_includes assert_includes @subject, "skinny jeans"
assert_instance_of assert_instance_of Array, @list
assert_kind_of assert_kind_of Enumerable, @list
assert_match assert_match @subject.first, /silly/
assert_nil assert_nil @list.first
assert_operator assert_operator @list.size, :== , 0
assert_output assert_output("Size: 2") { print "Size: #{@subject.size}"}
assert_raises assert_raises(NoMethodError) { @subject.foo }
assert_respond_to assert_respond_to @subject, :count
assert_same assert_same @subject, @subject, "It's the same object silly"
assert_send assert_send [@subject, :values_at, 0]
assert_silent assert_silent { "no stdout or stderr" }
assert_throws assert_throws(:error,'is empty') {throw :error if @subject.any?}

MiniTest::Mock

A simple and clean mock system. There two essential methods at our disposal: expect and verify.

require 'minitest/autorun'

class Twipster
  def initialize(twitter)
    # A Ruby wrapper for the Twitter API
    @twitter = twitter
  end

  def submit(tweet)
    @twitter.update("#{tweet} #lolhipster")
  end
end

describe Twipster, "Make every tweet a hipster tweet." do
  before do
    @twitter  = MiniTest::Mock.new
    @twipster = Twipster.new(@twitter)
  end

  it "should append a #lolhipster hashtag and update Twitter with our status" do
    tweet = "Skyrim? Too mainstream."
    @twitter.expect :update, true, ["#{tweet} #lolhipster"]
    @twipster.submit(tweet)
    assert @twitter.verify # verifies tweet and hashtag was passed to `@twitter.update`
  end
end

Resources

  1. MiniTest on Github
  2. MiniTest Rdoc
  3. Using MiniTest::Spec with Rails
  4. Ruby Inside: A MiniTest::Spec Tutorial: Elegant Spec-Style Testing That Comes With Ruby

I hope you found this quick guide valuable. Please let me know if you'd like to see anything else included and feel free to ask questions or give feedback in the comments section.

Callbacks are a great technique for achieving simplicity and flexibility. Simply put, a callback is a block of code passed as an argument to a method. In Ruby, code blocks are everywhere and Ruby makes it trivial to pass a block of code to methods. For example:

def foo(bar, &block)
  callback = block
  callback.call(bar)
end

foo(5) {|x| x * x} # => 25

But what do we do when a method needs two blocks of code or more? Consider the classic case where we want a method to execute a block of code if an action succeeds or call different code if an action fails.

In this article, I will demonstrate how we can pass multiple blocks to a method and with some metaprogramming, we can achieve a dynamic callback mechanism with just a few lines of code.

Let's add a method called callback to the Proc class:

class Proc
  def callback(callable, *args)
    self === Class.new do
      method_name = callable.to_sym
      define_method(method_name) { |&block| block.nil? ? true : block.call(*args) }
      define_method("#{method_name}?") { true }
      def method_missing(method_name, *args, &block) false; end
    end.new
  end
end

That's it! The above Proc#callback method simply yields an anonymous class with methods defined to handle our callbacks. This allows for the capability of creating and storing dynamic callbacks, which can later be looked up and executed as needed.

Notice anything unusual? We're using the === operand to invoke the block. Proc#=== is an alias for Proc.call. Anything on the right side of === acts as the proc's parameter. Normally, this is to allow a proc object to be a target of a when clause in case statements, but we're using it as a super simple way of invoking our anonymous class.

Let’s try it with something useful. Let’s say we’re writing something which needs to happen in an all-or-nothing, atomic fashion. Either the whole thing works, or none of it does. A simple case is tweeting:

def tweet(message, &block)
  Twitter.update(message)
  block.callback :success
rescue => e
  block.callback :failure, e.message
end

The tweet method accepts a message string and &block parameters. We call callback on the block and give it a name. Any name will work :success, :error, :fail!, whatever. In addition, we can pass arguments to the blocks (more on that later). Now we can provide a status if the tweet was successful or not:

tweet "Ruby methods with multiple blocks. #lolruby" do |on|
  on.success do
    puts "Tweet successful!"
  end
  on.failure do |status|
    puts "Error: #{status}"
  end
end

The advantage here is that we define our own mini DSL. We don't need to worry about passing too many or unexpected blocks. We could have easily said where.success or on.error or update.fail!. Also note the on.failure block includes a status parameter - this contains the exception message captured in the tweet method above. So if Twitter was down for whatever reason, the on.failure block would be invoked and printed 'Error: Twitter is down or being upgraded'.

Bonus: In addition to wrapping code in blocks, our Proc#callback method defines boolean style methods. So we could have call the tweet method like this if we wanted to:

tweet "Ruby methods with multiple blocks. #lolruby" do |update|
  puts "Tweet successful!" if update.success?
  puts "Sorry, something went wrong." if update.failure?
end

Put the Proc#callback method in a utility library and your code will look neat and tidy.

As always, I welcome your thoughts and feedback. Let me know what you think of the techniques shown here, or share your own favorite code block tricks.

I watch a lot of tests run in a given day. So I figured why not make it more fun. Inspired by minitest's pride, and um cats? I came up with a Nyan Cat inspired RSpec formatter.

Update: After last week's launch, Nyan Cat received a great response from the Ruby world. Over the weekend, I released version 0.0.2. It includes a few bug fixes and some really cool enhancements. Most notably, Nyan Cat now spans multiple lines. In addition, it displays running totals of passing, pending, and failed specs. Thanks to everyone who contributed! Checkout the new screencast below.

-_-_-_-_-_-_-_,------,
_-_-_-_-_-_-_-|   /\_/\
-_-_-_-_-_-_-~|__( ^ .^)
_-_-_-_-_-_-_-""  ""

Nyan Cat

Much like Nyan, Nyan Cat simply creates a rainbow trail of test results. It counts the number of examples as they execute and highlights failed and pending specs. The rainbow changes colors as it runs and if all the specs pass, Nyan Cat falls asleep. If there are any pending or failing specs, Nyan cat is concerned and can't sleep.

Here's a short demo of Nyan Cat in action.

Installing Nyan Cat is easy. Just install the gem nyan-cat-formatter and simply put the options in your .rspec file:

--format NyanCatFormatter

Checkout the code on Github and let me know how you like it. If you run into any issues, please create an issue on Github and I will be sure to get it fixed. Of course you can always fork the project and send me a pull request.

Have fun!

Gaga originated from my winning entry in Codebrawl's Key/Value Store contest. The challenge was to write the best key/value storage backend you can think of. Since Git is fast, reliable, and a great tool for storing source code, I was really interested in making an easy way to store key/values.

Built with Grit, Gaga supports SET, GET, KEYS, and DELETE operations. And since it's Git, we can easily enhance it to include other awesome Git features such as branches, diffs, reverting, etc.

Usage

@gaga = Gaga.new(:repo => '/path/to/repo')

# SET
@gaga['lady'] = "gaga"

# GET
@gaga['lady'] #=> "gaga"

# KEYS
@gaga.keys  #=> ['lady']

# DELETE
@gaga.delete('lady') #=> 'gaga'

# Remove all items from the store
@gaga.clear

That works pretty well. Now, we can harness the power of Git and enhance our data store. For example, we can get a history log for a specific key:

# LOG
@gaga.log('lady')

# Produces:
[
 {"message"=>"all clear","committer"=>{"name"=>"Matt Sears", "email"=>"matt@mattsears.com"}, "committed_date"=>"2011-09-05..."},
 {"message"=>"set 'lady' ", "committer"=>{"name"=>"Matt Sears", "email"=>"matt@mattsears.com"}, "committed_date"=>"2011-09-05..."}
 {"message"=>"delete 'lady' ", "committer"=>{"name"=>"Matt Sears", "email"=>"matt@mattsears.com"}, "committed_date"=>"2011-09-05..."}
]

This is just a start. There's still a lot things we can add. If you are interested in more detailed information, check out the repo on Github.

I've just released Stamps - A Ruby gem for creating postage labels, calculate the shipping cost of packages, standardize domestic addresses via USPS CASS certified Address Matching Software, and track shipments using the Stamps.com Web Services API.

Quick Start

First, you will need to register for a (free) developer account at Stamps.com. Once you receive your test credentials and integration id, just plug them into the configuration block:

Stamps.configure do |config|
  config.integration_id = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX'
  config.username       = 'STAMPS USERNAME'
  config.password       = 'STAMPS PASSWORD'
end

For a simple test, we can call Stamps.account to retreive information about the account. By default, Stamps will return all responses as a Hash.

Create a Stamp

First, we need to standardize the shipping address that complies with the USPS address formatting guidelines:

standardized_address = Stamps.clean_address(
  :address => {
    :full_name => 'The White House',
    :address1  => '1600 Pennsylvania Avenue, NW',
    :city      => 'Washington',
    :state     => 'DC',
    :zip_code  => '20500'
})

Now that we have a clean address we can create a new stamp. The Stamps.create! takes the sender and receiver address along with parameters on the rate:

stamp = Stamps.create!(
    :rate          => {
      :from_zip_code => '45440',
      :to_zip_code   => '20500',
      :weight_oz     => '6.5',
      :ship_date      => Date.today.strftime('%Y-%m-%d'),
      :package_type   => 'Package',
      :service_type   => 'US-FC'  # Flat-rate
    },
    :to            => standardized_address,
    :from => {
      :full_name   => 'Littlelines',
      :address1    => '50 Chestnut Street',
      :address2    => 'Suite 234',
      :city        => 'Beavervcreek',
      :state       => 'OH',
      :zip_code    => '45440'
    }
)

Hooray!

That's it! Stamps will return a url of the stamp. Print it and ship it!

stamp[:url]

If you are interested in more detailed information, check out the repo on Github.