Unit testing with Ruby (part 2)

In ruby, you can do this:

Class methods mocking

Class methods mocking

Hum. Static’s bad, don’t do static, mkay? Well, not necessarily. The fact that you can test class methods like this makes them much more usable than they are in C#, where using static all over the place brings your design to the dumpster pretty quickly. Still, the use of class methods usually covers for underlying design flaws, so it’s a technical debt and you should evaulate what is convenient to do case by case.

The above snippet is powered by mocha, a mocking framework for ruby. It has a JMock-like syntax and it provides you all the usual tools.

mock()

creates a mock object. You can set expectations on a mock object with

mock_object.expects(:method_name)

and stubs with

mock_object.stubs(:method_name)

Expectations are verified at the end of the test: if you’ve set an expectation for :method_name and that method isn’t called on the mock object before the end of the test, that test will fail (no need to VerifyAllExpectations or such). Instead, stubs do not fail the test if not called.
So, in the following example:
mock_example

if we remove the object.second call from the method run only the second test fill fail.

As seen before, you can set expectations and stubs not just on mocks but on any object, even class objects. Abusing of this feature, as to say in object = Object.new; object.expects(:method) ,  is probably not wise but as shown in the first snippet this allows testing of usually not testable situations.

You can force constraints on expectations using with(param_value) and you can return values with returns(value). For example, the following code:

mock_object = mock()
mock_object.expects(:get_capital).with('sweden').returns('stockholm')

Sets an expectation on mock_object for the method get_capital, the expectation is satisfied only if get_capital is called with the string 'sweden' and, if called that way, returns the string 'stockholm'. Of course you don’t need both clauses to be present togheter, mock_object.expects(:get_capital).with('sweden') and mock_object.expects(:get_capital).returns('stockholm') work just as expected. If you don’t set a return value for any given expectation/stub it will return nil.

stub_everything() returns a mock object that stubs every possible method call. The return value will always be nil.

More on mocha will be found on its documentation page.

Advertisements

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.