Thoughts on Software by Andrew Davey
 Friday, July 06, 2007
Code for the Rhino Mock DSL

I have attached the Boo code containing the Rhino Mocks DSL I'm currently working on. This is very much a work in progress release, just to get it out there. Please have a play and tell me what you think. Happy Mocking!

Dsl.boo (3.4 KB)

To use the DSL, add the following in your code to import the static methods into scope.

import Rhino.Mocks.Dsl


Friday, July 06, 2007 12:19:48 PM (GMT Standard Time, UTC+00:00)  #    Comments [2]   |  |  |  |  | 

 Wednesday, July 04, 2007
Better Syntax for Mocking

I thought some more about the syntax of my Rhino Mock DSL. It can feel unnatural putting all the mock expectation code before the call to the object being tested. I came up with this working prototype instead:

[Test]
def Get_data_objects_for_nonexistent_company_throws():
    with_mocks:
        database = mocks.CreateMock[of IDatabase]()
        userProvider = StubUserProvider("andrew", "bad corp")
        userProvider.MakeCurrent()

        execute:
            uds = UserDataService(database, userProvider)
            expect_throw FaultException[of GetDataObjectsFault]:
                uds.GetDataObjects()
            assert thrown_exception.Detail.Type.Equals(GetDataObjectsFaultType.InvalidCompany)

        assuming:
            database.GetUserID("andrew", "bad corp")
            returned 1
        assuming:
            database.GetCompanyID("bad corp")
            returned 0 # returning 0 from database implies nonexistent company.

The with_mocks method sets up the mock repository and a Store object. The execute and assumption methods then put their blocks into the Store. At the end of with_mocks I iterate through assumptions calling each to set up the Rhino Mock expectations. Following that is: mocks.ReplayAll(), call to the "execute" block, then mocks.VerifyAll().

The other clever bit in there is the expect_throw method. This runs the block inside a try...except and fails if no exception (or wrong exception type) is thrown. It puts the exception object into field that is readable using thrown_exception. This means we can then test assertions about the exception contents. I had to cheat a bit and declare thrown_exception as "duck" in Boo i.e. it is late bound. This is so we can access members on the actual object despite not really knowing about it at compile time.

I like the readability now. The outline is:

  • Initialize mocks and data objects
  • Call the object being tested
  • Assert about the result
  • State the assumptions about how dependencies are used

The key bit, I feel, is that the call to the object being tested is not buried down at the bottom of the method.

How does everyone else feel about this modified approach?


Wednesday, July 04, 2007 10:41:55 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]   |  |  |  |  | 

 Monday, July 02, 2007
Rhino Mocks DSL in Boo

I recently learnt that Boo now supports a DSL-friendly syntax. A while back I created (and almost finished!) a set of syntactic macros in Boo to make using Rhino Mocks much more natural. Creating macros can be hard work though and gets pretty hacky in places.

The new DSL-friendly syntax in Boo means I can achieve 80% of the same easy-reading code, but using only methods and blocks!

In about 10 minutes I whipped up this:

namespace ConsoleDemo

import System
import Rhino.Mocks
import Rhino.Mocks.BooDsl.MockDsl

interface IModel:
    def CalculateScore(input as int) as int
interface IView:
    def DisplayScore(score as int)
interface IPresenter:
    def Init()

class Presenter(IPresenter):
    _model as IModel
    _view as IView
    def constructor(model as IModel, view as IView):
        _model = model
        _view = view
    def Init():
        score = _model.CalculateScore(0)
        _view.DisplayScore(score)

def Test_presenter_init():
    with_mocks:
        // Create mocks of the dependencies
        model = Mocks.CreateMock of IModel()
        view = Mocks.CreateMock of IView()
        // Create the object we are testing
        presenter = Presenter(model, view)
        // Record what we expect the presenter to
        // do with its dependencies.
        record:
            expect:
                model.CalculateScore(0)
                // Set the return value for the
                // previous mock call.
                mock_return 42
            expect:
                view.DisplayScore(42)
        // Now tell the presenter to do its stuff,
        // so we can verify it behaves correctly.
        verify:
            presenter.Init()
print "testing..."
Test_presenter_init()
print "all good!"
print "Press any key to continue . . . "
Console.ReadKey(true)

The import of "Rhino.Mocks.BooDsl.MockDsl" brings some new methods into scope. with_mocks initializes a MockRepository. "Mocks" is actually a property referencing that repository, so we can call CreateMock, etc, on it. The record and verify methods make use of the Mocks.Record() and Mocks.Playback() methods. I prefer using "verify" to "playback". The "expect" method runs the code in its block, usually that invokes some mock method. It then calls "LastCall.Repeat.Once()" to set up the expectation. The "mock_return" method calls "LastCall.Return(value)" to set up the return value for the last mock call.

This is all a bit messy for now I know. I have just being throwing code down into this prototype to get a feel for what is possible. I reckon this could be really useful for people using Rhino Mocks.

If you like what you've seen or have any ideas let me know.

The DSL methods are as follows:
I am aware of more than one glaring danger in the code(!) but it does show what can be done.

namespace Rhino.Mocks.BooDsl

import System
import Rhino.Mocks

callable Block()

static class MockDsl:

    private static _instance as MockDslImpl
    static def with_mocks(b as Block):
        _instance = MockDslImpl()
        b()
        _instance = null
    static def record(b as Block):
        assert _instance != null
        _instance.Record(b)
    static def verify(b as Block):
        assert _instance != null
        _instance.Verify(b)
    static def expect(b as Block):
        assert _instance != null
        _instance.Expect(b)
    static def mock_return(value as object):
        assert _instance != null
        _instance.MockReturn(value)
    static Mocks as MockRepository:
        get:
            assert _instance != null
            return _instance.Mocks

    private class MockDslImpl:
        mocks as MockRepository
        def constructor():
            mocks = MockRepository()
        public Mocks as MockRepository:
            get:
                return mocks
        public def Record(b as Block):
            using mocks.Record():
                b()
        public def Verify(b as Block):
            using mocks.Playback():
                b()
        public def Expect(b as Block):
            b()
            LastCall.Repeat.Once()
        public def MockReturn(value as object):
            LastCall.Return(value)


Monday, July 02, 2007 11:03:52 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   |  |  |  |  |