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());
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.
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.
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.
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.
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++);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With