Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq mocked call returns null if using setup

I am writing tests for a C# application, using Moq. My test initialiser has the following code:

UnityContainer unityContainer = new UnityContainer();

_serviceMock = new Mock<IService>();
_serviceMock.Setup(mock => mock.GetSearchInfoAsync(It.IsAny<CancellationToken>(), It.IsAny<IEnumerable<string>>(), It.IsAny<identifierType>(), It.IsAny<bool>())).Callback(() => _count++);
unityContainer.RegisterInstance(typeof(IService), _serviceMock.Object, new ContainerControlledLifetimeManager());

I want to test that a call is made only once. I am trying it like this:

int _count = 0;

[TestMethod]
public void Properties_Test()
{
    _serviceMock.Verify(mock => mock.GetSearchInfoAsync(It.IsAny<CancellationToken>(), It.IsAny<IEnumerable<string>>(), It.IsAny<identifierType>(), It.IsAny<bool>()), Times.Exactly(1), "Invocation was performed " + _count + " times but was expected only once!");
}

This is the method where it actually gets called:

private void Search(string queryValue, identifierType identifierType)
{
    CancellationToken cancellationToken;

    lock (_syncLock)
    {
        _cancellationTokenSource.Cancel();
        _cancellationTokenSource = new CancellationTokenSource();
        cancellationToken = _cancellationTokenSource.Token;
    }

    IService Service = ServiceLocator.Current.GetInstance<IService>();

    Service.GetSearchInfoAsync(cancellationToken, new[] {queryValue}, identifierType)
        .ContinueWith(
            task =>
            {
                // Do stuff
            }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
}

The problem is that if I use this line as detailed above,

_serviceMock.Setup(mock => mock.GetSearchInfoAsync(It.IsAny<CancellationToken>(), It.IsAny<IEnumerable<string>>(), It.IsAny<identifierType>(), It.IsAny<bool>())).Callback(() => _count++);

this returns null and generates a NullPointerException:

Service.GetSearchInfoAsync(cancellationToken, new[] {queryValue}, identifierType)

However, if I comment out that line, the tests run fine (albeit not counting the number of calls).

What am I doing wrong? This is my first time using Moq for this and as far as I can tell I've implemented the count functionality correctly.

EDIT: Following Chris Sinclair's suggestion, I've changed the initialiser to this, which fixed the issue:

UnityContainer unityContainer = new UnityContainer();

_serviceMock = new Mock<IService>();
Task<IEnumerable<ISearchResult>> task = new Task<IEnumerable<ISearchResult>>(Enumerable.Empty<ISearchResult>);
_serviceMock.Setup(mock => mock.GetSearchInfoAsync(It.IsAny<CancellationToken>(), It.IsAny<IEnumerable<string>>(), It.IsAny<identifierType>(), It.IsAny<bool>())).Returns(task).Callback(() => _count++);
unityContainer.RegisterInstance(typeof(IService), _serviceMock.Object, new ContainerControlledLifetimeManager());
like image 464
Vlad Schnakovszki Avatar asked Dec 10 '15 13:12

Vlad Schnakovszki


People also ask

Why is mock method returning null?

If a method return type is a custom class, a mock returns null because there is no empty value for a custom class. RETURN_MOCKS will try to return mocks if possible instead of null . Since final class cannot be mocked, null is still returned in that case.

What can be mocked with Moq?

Using Moq, you can mock out dependencies and make sure that you are testing the code in isolation. Moq is a mock object framework for . NET that greatly simplifies the creation of mock objects for unit testing.

How does Moq mock work?

Mock objects allow you to mimic the behavior of classes and interfaces, letting the code in the test interact with them as if they were real. This isolates the code you're testing, ensuring that it works on its own and that no other code will make the tests fail.

Can you mock a class with Moq?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces. However, there are a few limitations you should be aware of. The classes to be mocked can't be static or sealed, and the method being mocked should be marked as virtual.


1 Answers

When you "Setup" the method, you set a callback but you don't provide a return value. As such, when the mocked method is called, it will return the default value for the return type (in this case, a Task<> type will result in a null return value). As such, when your Search method calls your mocked GetSearchInfoAsync method, it receives a null reference which naturally fails when it later attempts to invoke .ContinueWith on it.

Try adding a .Returns() which feeds a dummy Task<> to your mocked method:

_serviceMock.Setup(mock => mock.GetSearchInfoAsync(It.IsAny<CancellationToken>(), It.IsAny<IEnumerable<string>>(), It.IsAny<identifierType>(), It.IsAny<bool>()))
    .Returns(new Task<IEnumerable<ISearchResult>>(Enumerable.Empty<ISearchResult>))
    .Callback(() => _count++);
like image 175
Chris Sinclair Avatar answered Oct 19 '22 21:10

Chris Sinclair