views:

498

answers:

2

I'm a little confused at what's going on here. I'm looking at the Puzzle example from Atomic Object showing how to test a Model-View-Presenter pattern Puzzle.zip

The View has a private event. The view also has a Subscribe(delegate) function that adds the delegate to the event. The Presenter is passed in an IView and an IModel. During construction, it subscribes to the view and hooks it up to a function on the model.

For unit testing the Presenter, the View class is mocked using NMock. So it is just a dumb class, and the Subscribe() function doesn't actually do anything. Of course, to test the presenter, you have to mock up the view and model, then trigger the event in the view and ensure the model function was called. The example code works just fine - however, I don't understand how it works!!

Some excerpts:

    private DynamicMock modelMock;
    private IPuzzleModel model;
    private DynamicMock viewMock;
    private IPuzzleView view;
    private SavedTypeOf moveRequestConstraint;

    [SetUp]
    public void SetUp()
    {
        modelMock = new DynamicMock(typeof(IPuzzleModel));
        modelMock.Strict = true;
        model = modelMock.MockInstance as IPuzzleModel;

        // Setup the view
        viewMock = new DynamicMock(typeof(IPuzzleView));
        viewMock.Strict = true;
        view = viewMock.MockInstance as IPuzzleView;

        moveRequestConstraint = new SavedTypeOf(typeof(PointDelegate));
        viewMock.Expect("SubscribeMoveRequest", moveRequestConstraint);

        // create the presenter
        new PuzzlePresenter(model, view);
    }

    [Test]
    public void test_MoveRequest_fromView()
    {
        Point point = new Point(1, 2);
        modelMock.Expect("MoveRequest", point);
        PointDelegate trigger = moveRequestConstraint.GetInstance as PointDelegate;
        trigger(point);
    }

Somehow, the "trigger(point)" call is actually connected to the view, and is causing the private event in the view to trigger. I can't figure out how this is working - I don't see where it is connected to the view instance. What am I missing?

Update: I am trying to use NMock 2. It appears that the moveRequestConstraint variable receives the value that is passed into SubscribeMoveRequest() in the TestSetup function. However, that is NMock 1 syntax - and NMock 2 does not appear to support that syntax. How would I do it with NMock 2?

+1  A: 

Are you executing any domain code in your test? You're probably not testing anything except that the constructor of the presenter does not throw an exception.

By the way, I strongly recommend you to use RhinoMocks. It would look like this:

private IPuzzleModel model;
private IPuzzleView view;
private PointDelegate pointDelegate;
private Point point;

[SetUp]
public void SetUp()
{
    model = MockRepository.CreateMock<IPuzzleModel>();
    view = MockRepository.CreateMock<IPuzzleView>();

    // get the delegate passed to the mock when it is called
    // This is one of the more complex things you do with mocks.
    view.Stub(x => x.Subscribe(Arg<PontDelegate>().Is.Anything)
      .WhenCalled(call => pointDelegate = (PointDelegate)call.Arguments[0];);

    point = new Point(1, 2);
}

[Test]
public void test_MoveRequest_fromView()
{
    PuzzlePresenter presenter = new PuzzlePresenter(model, view);

    // make sure the Delegate method was called and the delegate
    // is available
    Assert.IsNotNull(pointDelegate);

    // fire the delegate.
    pointDelegate(point);

    // check if the model was called.
    model.AssertWasCalled(x => x.MoveRequest(point));
}
Stefan Steinegger
A: 

I ran into the same issue trying to make Presenter First example work with NMock2.

After a little digging, I found a post in NMock2 forum on SourceForge.

[Test]
public void test_MoveRequest_fromView()
{
    Mockery mockery = new Mockery();
    IPuzzleView view = mockery.NewMock<IPuzzleView>();
    IPuzzleModel model = mockery.NewMock<IPuzzleModel>();

    CollectAction collect = new CollectAction(0);
    Expect.Once.On(view).Method("SubscribeMoveRequest").Will(collect);
    Expect.Once.On(model).Method("MoveRequest");

    new PuzzlePresenter(model, view);
    Point point = new Point(1, 2);
    PointDelegate del = collect.Parameter as PointDelegate;
    del(point);
    mockery.VerifyAllExpectationsHaveBeenMet();
}

Try the above code - haven't tried it but it should work. It doesn't read well like the rest of NMock2 but then the original test code in NMock didn't either.

UPDATE:

And it appears that the latest NMock2 (2.0.3411.37113) also supports a generic version of CollectAction, so you could also do:

PointDelegate savedPointDelegate = null;
CollectAction<PointDelegate> collect = new CollectAction<PointDelegate>(0,
    delegate(PointDelegate del) { savedPointDelegate = del; });
...
savedPointDelegate(point);

Below is my attempt at readability improvement but it's not that much of an improvement:

Expect.Once.On(view).Method("SubscribeMoveRequest").Will(
    Collect.Argument<PointDelegate>(0, delegate(PointDelegate del) { savedPointDelegate = del; }));

public class Collect
{
    public static CollectAction<T> Argument<T>(int index, CollectAction<T>.Collect collectDelegate)
    {
        CollectAction<T> collect = new CollectAction<T>(index, collectDelegate);
        return collect;
    }
}
Jiho Han