Подтвердить что ты не робот

Проверка регистрации событий с использованием Moq

Я занимаюсь разработкой приложения asp.net(classic), пытающегося реализовать шаблон MVP , используя этот пример. При попытке выполнить модульное тестирование моего докладчика и использовать следующий шаблон, psuedocode для которого выглядит так

//base view interface
public interface IView
{
    event EventHandler Init;

    event EventHandler Load;

    bool IsPostBack { get; }

    void DataBind();

    bool IsValid { get;}
}

//presenter psuedo code
public class SomePresenter
{
     public SomePresenter(ISomeDomainService service, IView someView)
     {
           ...
           //HOW DO WE TEST/VERIFY THAT THIS REGISTRATION OCCURS?
           someView.Init += OnInit;
           someView.Load += OnLoad;
     }
}
...
//consuming code that exercises the above code, that needs to be tested
var presenter = new SomePresenter(someDomainService, someView);

Как проверить, что докладчик делает то, что ожидается, то есть регистрируется для событий Init и Load? Хотя это легко сделать в примере Фила Хаака с использованием насмешек Rhino...

[Test]
public void VerifyAttachesToViewEvents()
{
    viewMock.Load += null;
    LastCall.IgnoreArguments();
    viewMock.PostSaved += null;
    LastCall.IgnoreArguments();
    mocks.ReplayAll();
    new PostEditController(viewMock, 
      this.dataServiceMock);
    mocks.VerifyAll();
}

... как мы можем сделать это с помощью MOQ?

4b9b3361

Ответ 1

Похоже, что эта функциональность недоступна в настоящее время в moq, но может появиться в будущей версии (я посмотрел в 4.0.812.4 бета, но он, похоже, не существует).

Возможно, стоит задать вопрос: "Почему SomePresenter нужно подписаться на события View Load и Init?" Предположительно, это потому, что класс SomePresenter должен отвечать на эти события. Поэтому лучше было бы использовать метод Raise на вашем Mock<IView>, чтобы поднять события Load и Init, а затем утверждать, что SomePresenter сделал правильную вещь в ответ на них.

Ответ 2

Я знаю, возможно, слишком поздно для #Dilip, но этот ответ может быть полезен тем, кто пытается сделать то же самое. Вот тестовый класс

public delegate void SubscriptionHandler<T>(string name, T handler);

public class SomePresenterTest
{
    [Test]
    public void Subscription_Test()
    {
        var someServiceMock = new Mock<ISomeDomainService>();
        var viewMock = new Mock<IView>();
        //Setup your viewMock here

        var someView = new FakeView(viewMock.Object);
        EventHandler initHandler = null;            
        someView.Subscription += (n, h) => { if ((nameof(someView.Init)).Equals(n)) initHandler=h; };

        Assert.IsNull(initHandler);

        var presenter = new SomePresenter(someServiceMock.Object, someView);

        Assert.IsNotNull(initHandler);
        Assert.AreEqual("OnInit", initHandler.Method?.Name);
    }
}

FakeView - это декоратор, реализованный следующим образом (обратите внимание на Events: Init/Load {add; remove}):

public class FakeView : IView
{
    public event SubscriptionHandler<EventHandler> Subscription;
    public event SubscriptionHandler<EventHandler> Unsubscription;
    private IView _view;
    public FakeView(IView view)
    {
        Assert.IsNotNull(view);
        _view = view;
    }

    public bool IsPostBack => _view.IsPostBack;
    public bool IsValid => _view.IsValid;

    public event EventHandler Init
    {
        add
        {
            Subscription?.Invoke(nameof(Init), value);
            _view.Init += value;
        }

        remove
        {
            Unsubscription?.Invoke(nameof(Init), value);
            _view.Init -= value;
        }
    }
    public event EventHandler Load
    {

        add
        {
            Subscription?.Invoke(nameof(Load), value);
            _view.Init += value;
        }

        remove
        {
            Unsubscription?.Invoke(nameof(Load), value);
            _view.Init -= value;
        }
    }

    public void DataBind()
    {
        _view.DataBind();
    }
}

Ответ 3

moq 4.13 представил эту функцию. Теперь можно проверить, был ли вызван add\remove. Поэтому были введены четыре новых метода:

  1. SetupAdd
  2. SetupRemove
  3. VerifyAdd
  4. VerifyRemove

Пример

var mock = new Mock<IAdder<EventArgs>>();
mock.SetupAdd(m => m.Added += (sender, args) => { });

mock.Object.Added += (sender, args) => { };
mock.Object.Added += (sender, args) => { };

mock.VerifyAdd(m => m.Added += It.IsAny<EventHandler>(), Times.Exactly(2));

NB. Обратите внимание, что для проверки необходимо добавить хотя бы одну настройку. Причина заключается в том, чтобы сохранить обратную совместимость со старой версией moq.

Ответ 4

Я потратил некоторое время на этот вопрос, и решение, которое я использую в своем проекте:

Unit test:

// Arrange
TestedObject.Setup(x => x.OnEvent1());
TestedObject.Setup(x => x.OnEvent2());

// Act
TestedObject.Object.SubscribeEvents();
TestedObject.Raise(x => x.Event1 += null);
TestedObject.Raise(x => x.Event2 += null);

// Assert
TestedObject.Verify(x => x.OnEvent1(), Times.Once());
TestedObject.Verify(x => x.OnEvent2(), Times.Once());

Протестированный метод:

this.Event1 += OnEvent1;
this.Event2 += OnEvent2;

Итак, сначала вы должны высмеять методы, которые вы назначите событиям, после вызова метода, который вы хотите протестировать, и, наконец, поднять все подписанные события. Если событие действительно подписано, вы можете проверить с помощью Moq, если вызван назначенный метод.

GLHF!