1

I want to migrate from MVVM Light to Microsoft Toolkit MVVM and I have a problem with my unit tests. I don't know how properly wrap IMessengerExtensions and IMessenger to use it in unit testing.

Example of test I have in my project:

public void LoadingFinishedTest()
    {
        var messengerMock = new Mock<IMessenger>();
        messengerMock.Setup(mock => mock.Send(It.Is<IsLoadingMessage>()));

        var testedViewModelMock = new Mock<SomeViewModel>(messengerMock.Object);

        testedViewModelMock.Object.LoadingFinished();

        messengerMock.Verify(mock => mock.Send(It.Is<IsLoadingMessage>(), Times.Once);
    }

And of course if I do not wrap anything and just try to run test I get:

System.NotSupportedException : Type to mock must be an interface, a delegate, or a non-sealed, non-static class.
  • 1
    I guess you are trying to test `SomeViewModel`. In that case, you should create a concrete class and inject `messengerMock` to `SomeViewModel` ctor. – popsiporkkanaa Aug 23 '21 at 10:01

2 Answers2

0

Assuming SomeViewModel is het subject under test, an actual instance of this should be used to exercise the test case

public void LoadingFinishedTest() {
    // Arrange
    var messengerMock = new Mock<IMessenger>();
    messengerMock.Setup(mock => mock.Send(It.Is<IsLoadingMessage>()));

    var subject = new SomeViewModel(messengerMock.Object);

    // Act
    subject.Object.LoadingFinished();

    // Assert
    messengerMock.Verify(mock => mock.Send(It.Is<IsLoadingMessage>(), Times.Once);
}

The verification can also be configured during setup

For example

public void LoadingFinishedTest() {
    // Arrange
    var messengerMock = new Mock<IMessenger>();
    messengerMock
        .Setup(mock => mock.Send(It.Is<IsLoadingMessage>()))
        .Verifiable(); //<-- NOTE THIS

    var subject = new SomeViewModel(messengerMock.Object);

    // Act
    subject.Object.LoadingFinished();

    // Assert
    messengerMock.Verify(); //<-- verifying expected behavior that was setup
}
Nkosi
  • 215,613
  • 32
  • 363
  • 426
0

I also ran into problems with this. For me the issue was that IMessenger.Send<TMessage> is an extension method. Unfortunately, as I've learned, Moq cannot mock extension methods because they are static.

My solution was to create an IMessengerWrapper which can be mocked:

    // Mockable interface
    public interface IMessengerWrapper
    {
        TMessage Send<TMessage>(TMessage message)
                where TMessage : class;
    }

    // Real implementation for actual code
    public class MessengerWrapper : IMessengerWrapper
    {
        private IMessenger _messenger;

        public MessengerWrapper(IMessenger messenger)
        {
            _messenger = messenger;
        }

        public TMessage Send<TMessage>(TMessage message)
            where TMessage : class
        {
            return _messenger.Send(message);
        }
    }

Which can then be used in place of IMessenger in your unit tests, e.g.:

    public void LoadingFinishedTest()
    {
        var messengerMock = new Mock<IMessengerWrapper>();
        // Using It.IsAny here because It.Is requires a predicate
        messengerMock.Setup(mock => mock.Send(It.IsAny<IsLoadingMessage>()));

        var testedViewModelMock = new Mock<SomeViewModel>(messengerMock.Object);

        testedViewModelMock.Object.LoadingFinished();

        messengerMock.Verify(mock => mock.Send(It.IsAny<IsLoadingMessage>(), Times.Once);
    }

Sources:

How do I use Moq to mock an extension method?

Mocking Static Methods for Unit Testing