C#Noob - 模拟接口类中的触发事件 - 此代码如何工作?

时间:2009-04-06 05:45:29

标签: c# unit-testing mvp nmock

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

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

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

一些摘录:

    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进行操作?

2 个答案:

答案 0 :(得分: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));
}

答案 1 :(得分:0)

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

经过一番挖掘后,我在SourceForge上的NMock2论坛中找到了post

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