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

时间:2022-01-17 18:51:56

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

我对这里发生的事情感到有点困惑。我正在查看Atomic Object中的Puzzle示例,展示如何测试Model-View-Presenter模式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.

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

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!!

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

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?

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

2 个解决方案

#1


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:

顺便说一句,我强烈建议你使用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));
}

#2


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

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

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

经过一番挖掘后,我在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();
}

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.

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

UPDATE:

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

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

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

#1


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:

顺便说一句,我强烈建议你使用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));
}

#2


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

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

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

经过一番挖掘后,我在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();
}

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.

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

UPDATE:

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

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

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