Unit testing with Ruby (part 1)

Not having the compiler on your side makes life difficult. Example:

C#

public int Multiply(int firstFactor,int secondFactor)
{
      return firstFactor*secondFactor;
}

Ruby

def multiply(first_factor, second_factor)
   return first_factor*second_factor
end

What these methods are supposed to do is pretty straightforward. What they actually do, maybe not so.  What if you were to test both methods? What\how many tests would you write to make sure these methods behave as you expect?

Let’s take a look at the C# method. It takes two parameters (both int, so value types) and returns an integer. Relying on cyclomatic complexity to determine, for a given method, how many tests must be written is often uncorrect but let’s assume that it’s okay for this case, so one test that verifies the result for a given input might be enough.

What about the Ruby method? It takes two parameters and returns something. And the two parameters might be pretty much anything. How about testing this one? There’s no IFs so we might say that one test is still fine, or you might want to check parameters for correctness, or maybe make sure that who calls your method do it the way it’s supposed to. Either options (except the first but seriously, you’re not going for that one) requires extra effort in comparison to the C# approach.

About mocking:

C#

public Controller(IView view, IModel model)
{
      //some stuff happening here
}

And Ruby

def initialize(view, model)
      #some stuff happening here
end

Mocking view and model in C# requires some degree of IView = repo.CreateMock<IView>(). This relies on the IView interface, and you’ve got nothing like that in Ruby. You just mock methods instead. Mocha syntax:

def test_refresh_always_callRefreshOnView
   view = Mock()
   view.expects(:refresh)
   controller = Controller.new(view,Stub())
   controller.refresh
end

No need of VerifyAllExpectations() or such: this fails automatically if refresh is not invoked on the view object.

A similar problem arises when using IoC containers: you’ve got no interface to resolve. More about this later.

Leave a comment