Monthly Archives: December 2010

Getting to Know the Ruby Standard Library – MiniTest::Mock

This article has been republished on Monkey and Crow.

Recently we looked at MiniTest, this time around we’re going to dive into MiniTest::Mock, a tiny library that will let you test systems that would otherwise be very difficult to test. We will take a look at what MiniTest::Mock provides, and then how it works.

A MiniTest::Mock Example

If you’re not familiar with Mock objects in general, wikipedia has a nice article on them. Let’s imagine that we want to write a script that deletes any email messages that are more than a week old:

  class MailPurge
    def initialize(imap)
      @imap = imap
    end
  
    def purge(date)
      # IMAP wants dates in the format: 8-Aug-2002
      formatted_date = date.strftime('%d-%b-%Y')
    
      @imap.authenticate('LOGIN', 'user', 'password')
      @imap.select('INBOX')

      message_ids = @imap.search(["BEFORE #{formatted_date}"])
      @imap.store(message_ids, "+FLAGS", [:Deleted])
    end
  end

We want to make sure that MailPurge only deletes the messages the imap server says are old enough. Testing this will be problematic for a number of reasons. Our script is going to be slow if it has to communicate with the server, and it has the permanent side effect of deleting your email. Luckily we can drop a mock object in to replace the imap server. We need to make a list of all the interactions our code has with the imap server so that we can fake that part of the server. We can see our script will call authenticate, select, search, and store, so our mock should expect each call, and have a reasonable response.

  def test_purging_mail
    date = Date.new(2010,1,1)
    formatted_date = '01-Jan-2010'
    ids = [4,5,6]
    
    mock = MiniTest::Mock.new
    
    # mock expects:
    #            method      return  arguments
    #-------------------------------------------------------------
    mock.expect(:authenticate,  nil, ['LOGIN', 'user', 'password'])
    mock.expect(:select,        nil, ['INBOX'])
    mock.expect(:search,        ids, [["BEFORE #{formatted_date}"]])
    mock.expect(:store,         nil, [ids, "+FLAGS", [:Deleted]])
    
    mp = MailPurge.new(mock)
    mp.purge(date)
    
    assert mock.verify
  end

We call MiniTest::Mock.new to create the mock object. Next we set up the mock’s expectations. Each expectation has a return value and an optional set of arguments it expects to receive. You can download this file and try it out (don’t worry it won’t actually delete your email). The MailPurge calls our fake imap server, and in fact does delete the message ids the server sends back in response to the @imap.search. Finally, we call verify which asserts that MailPurge made all the calls we expected.

How it Works

Lets dive into the source, if you have Qwandry you can open it with qw minitest. Looking at mock.rb you will see that MiniTest::Mock is actually quite short. First let’s look at initialize.

def initialize
  @expected_calls = {}
  @actual_calls = Hash.new {|h,k| h[k] = [] }
end

We can see that Mock will keep track of which calls were expected, and which ones were actually called. There is a neat trick in here with the Hash.new {|h,k| h[k] = [] }. If a block is passed into Hash.new, it will get called any time there is a hash miss. In this case any time you fetch a key that isn’t in the hash yet, an array will be placed in that key’s spot, this comes in handy later.

Next lets look at how expect works:

def expect(name, retval, args=[])
  n, r, a = name, retval, args # for the closure below
  @expected_calls[name] = { :retval => retval, :args => args }
  self.class.__send__(:define_method, name) { |*x|
    raise ArgumentError unless @expected_calls[n][:args].size == x.size
    @actual_calls[n] << { :retval => r, :args => x }
    retval
  }
  self
end

This looks dense, but if you take a moment, it’s straightforward. As we saw in the example above, expect takes the name of the method to expect, a value it should return, and the arguments it should see. Those parameters get recorded into the hash of @expected_calls. Next comes the tricky bit, MiniTest::Mock defines a new method on this instance that verifies the correct number of arguments were passed. The generated method also records that it’s been called in @actual_calls. Since @actual_calls was defined to return an array for a missing key, it can just append to whatever the hash returns. So expect dynamically builds up your mock object.

The final part of Mock makes sure that it did everything you expected:

def verify
  @expected_calls.each_key do |name|
    expected = @expected_calls[name]
    msg = "expected #{name}, #{expected.inspect}"
    raise MockExpectationError, msg unless
      @actual_calls.has_key? name and @actual_calls[name].include?(expected)
  end
  true
end

We can see here that verify will check each of the @expected_calls and make sure that it was actually called. If any of the expected methods aren’t called, it will raise an exception and your test will fail. Now you can build mock objects and make sure that your code is interacting the way you expect it to.

You should be aware though that MiniTest::Mock does not have many of the features that much larger libraries such as mocha do. For instance it does not let you set up expectations on existing objects, and requires you to specify all the arguments which can be cumbersome.

So we have dived into another piece of ruby’s standard library and found some more useful functionality. Hopefully along the way you have lerned some uses for mocking, and a neat trick with ruby’s Hash.

Advertisements

4 Comments

Filed under ruby, stdlib, testing

Getting Help Inside IRB

Here’s a quick tip, ruby’s ri utility will look up documentation about a method. For instance you can type ri String#split to see the documentation for String’s instance method split. If you have an irb session open you can tell irb to shell out using back ticks like this:


  ruby-1.9.1-p378 > puts `ri String#split`

You can also make a little helper method like this:


  def ri(signature)
    puts `ri #{signature}`
  end

Leave a comment

Filed under Uncategorized

Getting to Know the Ruby Standard Library – Shellwords

This article has been republished on Monkey and Crow.

Previously we answered a few questions about Minitest, and learned a little about exit hooks and introspection in ruby. Now lets look at an often overlooked library, Shellwords. Shellwords lets you break up a string the same way the Bourne shell does. So again, we will try to answer a few questions:

  1. What does Shellwords do?
  2. How does Shellwords break up the input?

Overview

Before diving into the code, lets look at some examples of what Shellwords does. Lets open up irb and try a few things out.

require 'shellwords'
Shellwords.split "search for 'some word'"
#=> ["search", "for", "some word"] 
Shellwords.split "search for 'some \"word\"'"
#=> ["search", "for", "some \"word\""]

As you can see, Shellwords splits up the input while respecting quoting. It is fairly strict though:

Shellwords.split "georgia o'keefe"
#=> ArgumentError: Unmatched double quote: "georgia o'keefe"
Shellwords.split "artist is \"georgia o'keefe\""
["artist", "is", "georgia o'keefe"]

So how could you use it? It’s good for tokenizing tags if you want to allow them to be more than one word. It could also come in handy if you wanted to make a mini language for scripting things:

instructions <<- END
  Activate Timmy
  Say "Hello world!"
  Wave
end

instructions.lines.each do |line|
  tokens = Shellwords.split(line)
  ...
end

It wouldn’t be the first little language made in ruby.

Shellwords

Now we have a rough idea of what it does, how does it work? To start with, take a look at the source (qw shellwords if you have Qwandry installed). We immediately come across:

module Shellwords
  ...
  def shellsplit(line)
    words = []
    field = ''
    line.scan(/\G\s*(?>([^\s\\\'\"]+)|'([^\']*)'|"((?:[^\"\\]|\\.)*)"|(\\.?)|(\S))(\s|\z)?/m) do
      |word, sq, dq, esc, garbage, sep|
      raise ArgumentError, "Unmatched double quote: #{line.inspect}" if garbage
      field << (word || sq || (dq || esc).gsub(/\\(?=.)/, ''))
      if sep
        words << field
        field = ''
      end
    end
    words
  end

Without jumping into the hefty regular expression, we can see this is using String#scan to repeatedly match a pattern against the input string. From there it’s building up field and then whenever there is a separator (sep) it adds that word to the list of tokenized words it will return at the end.

Now I suggest looking at that regular expression and quavering with fear. Tremors aside, we can break it down:

\G                          # Start of match attempt
\s*                         # Some optional whitespace
(?>                         # A non matching group
  ([^\s\\\'\"]+)            # word:     Something without spaces, quotes, or escapes
  |'([^\']*)'               # sq:       Something in single quotes
  |"((?:[^\"\\]|\\.)*)"     # dq:       Something in double quotes
  |(\\.?)                   # esc:      An escaped character 
  |(\S)                     # garbage:  Anything that doesn't match the pattern
)                           #
(\s|\z)?                    # sep:      A space or the end of the line

We start with \G, which I had to look up. This apparently means continue from the last match, but in my experiments I couldn’t find an instance where it made a difference in String#scan. If anyone can give a ruby example where this makes a difference, leave me a comment. The next chunk will gobble any leading whitespace. After that comes a non matching group, this is handy if you just want to group some parts of a regexp together, for example (and it’s a poor example):

"plus 123".match(/(plus|minus)\s(\d+)/)
# => <MatchData "plus 123" 1:"plus" 2:"123">
"plus 123".match(/(?>plus|minus)\s(\d+)/)
# => <MatchData "plus 123" 1:"123">

So that first group in the shellsplit is just to keep everything inside it together. The pipe between each of these groups says that the regular expression will just pick one of them. We can see that each of these groups will be yielded to the block, while the non matching group is ignored. So each time the block is called word, sq, dq, esc, or garbage will be filled in with a value. This is largely how the different quoting and escaping rules are implemented in Shellwords.split.

It’s interesting to notice the esc pattern, it’s going to gobble up anything following a ‘\’. This doesn’t seem quite right, an experiment shows how this plays out:

Shellwords.split('I will escape\ these \\bonds')
# => ["I", "will", "escape these", "bonds"]

Is this intentional? Running this little script from bash gives us a different result:

ruby -e 'puts ARGV.inspect' I will escape\ these \\bonds
["I", "will", "escape these", "\\bonds"]

So maybe we found a bug? To be honest I could not really say. Another interesting part is the garbage capture. It should only be matched by something that isn’t whitespace and doesn’t match any of the other options. If Shellwords encounters this, it will raise an exception, so this is how Shellwords ensures valid inputs. The last part will match a space or the end of the line, and tells Shellwords where to end this chunk of input.

That wasn’t so bad now was it? While you have shellwords.rb open, perhaps you should look around. As of ruby 1.9, there is a bonus method waiting for you.

Recap

So we set off to learn about another part of the ruby standard library. Along the way we learned how it works, and saw that we could break down a rather obscure regular expression. If you were appropriately curious you may have also found Shellwords.escape, and the String#shellwords shortcut. So once again, reading the source is pretty neat.

5 Comments

Filed under ruby, stdlib

Getting to Know the Ruby Standard Library – MiniTest

This article has been republished on Monkey and Crow.

Ghetto or not, if you want to use or contribute to the ruby standard library, getting to know the source is essential. We will look at one of the libraries everyone should be acquainted with, test/unit. There are dozens of testing frameworks in ruby, but this is the one you know everyone will have available. I’ll assume you’re using ruby 1.9.x, because it’s awesome.

Overview

Test/Unit is a ruby implementation of original xUnit architecture. In general you write your tests by extending Test::Unit::TestCase, adding methods that look like test_*something*, and then you can run that file directly with `ruby my_neat_test.rb`. Since it’s best to have a purpose when learning, we’ll try to answer two questions:

  1. How does ruby know it should run the tests when you execute your file?
  2. How does test unit know which methods are tests?

Test/Unit

To get started, open up Test/Unit (`qw test` if you have Qwandry installed).

Looking at unit.rb we immediately see something odd:

  # test/unit compatibility layer using minitest.

  require 'minitest/unit'
  require 'test/unit/assertions'
  require 'test/unit/testcase'

In ruby 1.9 minitest was swapped in to replace test/unit. The code in test/unit is just here to make sure all the tests behave the same way in ruby 1.9 as they did in ruby 1.8. If you scroll down to the bottom of unit.rb, you will find this:

  MiniTest::Unit.autorun

This is going to get called any time that you require test/unit, so lets find out what it does, and I suspect we can unravel our first question.

Minitest

There isn’t much in test/unit so lets take a look at minitest since that’s what ruby 1.9.x is really using (`qw minitest`). Since we saw that autorun is called whenever we require test/unit, autorun.rb sounds like a good place to start looking. The contents of this file?

  MiniTest::Unit.autorun

Ok, well we have three more files left, so unit.rb is probably another good place to look. A quick search for autorun will yield:

  class Unit
    ...
    def self.autorun
      at_exit {
        next if $! # don't run if there was an exception
        exit_code = MiniTest::Unit.new.run(ARGV)
        exit false if exit_code &amp;&amp; exit_code != 0
      } unless @@installed_at_exit
      @@installed_at_exit = true
    end

We see that at_exit is being called. If you haven’t run across this before, so a quick peak at the docs for ruby-core shows that this block will be executed right before ruby exits. Notice that minitest calls MiniTest::Unit.new.run(ARGV) inside the block? Now you know how the tests get run. You can also see that @@installed_at_exit is in there to prevent your tests from being launched more than once. Neat stuff, one question down.

Next up, how does it know which tests to run? We saw that MiniTest::Unit.new.run(ARGV) is going to get called, so that’s a good place to start.

  def run args = []
    @verbose = args.delete('-v')

    filter = if args.first =~ /^(-n|--name)$/ then
               args.shift
               arg = args.shift
               arg =~ /\/(.*)\// ? Regexp.new($1) : arg
             else
               /./ # anything - ^test_ already filtered by #tests
             end
    ...

We can see that this code starts out by using the options from ARGV to configure MiniTest::Unit. Let’s look at filter it will either end up being the name of a test, or a regexp that matches everything. The comment looks promising too. Hopping down a few lines we see that filter gets used:

  run_test_suites filter

Looking ahead we see that TestCase.test_suites seems to be enumerable, and each suite has a set of test methods.

  def run_test_suites filter = /./
    ...
    TestCase.test_suites.each do |suite|
      suite.test_methods.grep(filter).each do |test|
        inst = suite.new test
    ...

We’ll ignore the test_suites for now and look into the test_methods code, this is probably where minitest finds all of the tests you wrote.

  def self.test_methods
    methods = public_instance_methods(true).grep(/^test/).map { |m|
      m.to_s
    }.sort
    ...

And there we go, your test inherits from TestCase and TestCase.test_methods introspects on itself looking for all the public methods that start with the word test. So now we’ve answered our questions, and if you were paying attention, we learned a few things that might be useful.

Recap

In ruby 1.9 test/unit was replaced with minitest. It turns out that minitest registers an at_exit hook, and that it introspects on its own methods to figure out what to run. Now while you were in the code, perhaps you noticed few other interesting things that might be of use later:

  • minitest defines skip which lets you skip a test and give a reason
  • There is a mocking library available in mock.rb
  • There is a tiny mocking library available in mock.rb
  • There is a small BDD testing library available in spec.rb

Now aren’t you glad you took a moment to look at the standard library? Go forth and do something great.

5 Comments

Filed under ruby, stdlib

Qwandry

When you’re happily coding away, and you run into a bug, or are curious about a library you’re using, what do you do? I like to look at the source, so I wrote open_gem.

There were two problems with this. First, open_gem doesn’t open Ruby’s standard library. Second, each language has its own packaging system, npm, cabal, cpan, and so on.

Qwandry gives you a single way to easily open all your projects and libraries. Since I use Ruby most of the time, that’s what you get by default, but telling Qwandry about other languages is easy.

Here are a few examples:

qw matrix # opens ruby's matrix library
qw activerec 3.1 # will find version 3.1 of activerecord

If you want to use Qwandry with Node.js, Perl, Python, Haskell, R, or any other language you can dream up just run the customize command:

qw --customize

Installation is easy:

gem install qwandry

The code is on GitHub as usual, so hack away (qw qw).

4 Comments

Filed under development, ruby