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 to properly wrap IMessengerExtensions and IMessenger to use them in unit testing.

Example of a 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 simply try to run the test, I get the following error:

System.NotSupportedException: Type to mock must be an interface, a delegate, or a non-sealed, non-static class.

Could someone please explain what I may be doing wrong?

fourpastmidnight
  • 4,032
  • 1
  • 35
  • 48
  • 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
  • 235,767
  • 35
  • 427
  • 472
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