1

我对这里发生的事情有点困惑。我正在查看 Atomic Object 中的 Puzzle 示例,展示如何测试 Model-View-Presenter 模式Puzzle.zip

视图有一个私人事件。该视图还有一个订阅(delegate) 函数,用于将委托添加到事件中。Presenter 在 IView 和 IModel 中传递。在构建期间,它订阅视图并将其连接到模型上的函数。

为了对 Presenter 进行单元测试,使用 NMock 模拟 View 类。所以它只是一个愚蠢的类,Subscribe() 函数实际上并没有做任何事情。当然,要测试presenter,你必须模拟视图和模型,然后在视图中触发事件并确保调用了模型函数。示例代码工作得很好 - 但是,我不明白它是如何工作的!

部分摘录:

    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);
    }

不知何故,“触发器(点)”调用实际上连接到视图,并导致视图中的私有事件触发。我不知道它是如何工作的——我看不到它与视图实例的连接位置。我错过了什么?

更新: 我正在尝试使用 NMock 2。看来 moveRequestConstraint 变量接收到在 TestSetup 函数中传递给 SubscribeMoveRequest() 的值。但是,那是 NMock 1 语法 - 而 NMock 2 似乎不支持该语法。我将如何使用 NMock 2 做到这一点?

4

2 回答 2

1

您是否在测试中执行任何域代码?除了演示者的构造函数没有抛出异常之外,您可能没有测试任何东西。

顺便说一句,我强烈推荐你使用RhinoMocks。它看起来像这样:

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));
}
于 2009-04-15T14:53:02.517 回答
0

我在尝试使 Presenter First 示例与 NMock2 一起工作时遇到了同样的问题。

经过一番挖掘,我在 SourceForge 上的 NMock2 论坛上找到了一个帖子

[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();
}

试试上面的代码 - 没有试过,但它应该可以工作。它不像 NMock2 的其余部分那样读得很好,但是 NMock 中的原始测试代码也没有。

更新:

而且似乎最新的 NMock2 (2.0.3411.37113) 也支持 CollectAction 的通用版本,所以你也可以这样做:

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

以下是我在提高可读性方面的尝试,但并没有太大的改进:

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;
    }
}
于 2009-10-06T13:35:42.717 回答