C # Noob - Инициирующее событие в классе с поддельными интерфейсами. Как работает этот код? - PullRequest
1 голос
/ 06 апреля 2009

Я немного смущен тем, что здесь происходит. Я смотрю на пример головоломки из Atomic Object, показывающий, как протестировать модель Model-View-Presenter Puzzle.zip

Просмотр имеет частное мероприятие. Представление также имеет функцию подписки (делегата), которая добавляет делегата к событию. Ведущий передается в IView и IModel. Во время построения он подписывается на представление и подключает его к функции на модели.

Для модульного тестирования Presenter класс View смоделирован с помощью NMock. Так что это просто тупой класс, и функция 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 получает значение, которое передается в SubscribeMoveRequest () в функции TestSetup. Однако это синтаксис NMock 1, а NMock 2 не поддерживает этот синтаксис. Как бы я сделал это с NMock 2?

Ответы [ 2 ]

1 голос
/ 15 апреля 2009

Вы выполняете какой-либо код домена в своем тесте? Вы, вероятно, ничего не тестируете, за исключением того, что конструктор презентатора не выдает исключение.

Кстати, я настоятельно рекомендую вам использовать 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));
}
0 голосов
/ 06 октября 2009

Я столкнулся с той же проблемой, пытаясь заставить Presenter First работать с NMock2.

После небольшой копки я нашел сообщение на форуме NMock2 на 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();
}

Попробуйте приведенный выше код - не пробовал, но он должен работать. Он не читается так хорошо, как остальная часть NMock2, но тогда оригинальный тестовый код в NMock тоже не читался.

UPDATE:

И похоже, что последняя версия 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;
    }
}
...