Mocking Method Execution Times and Sequence - c#

I am using Moq paired with an Interface of methods. I need to test that the methods in this interface are executed in a certain sequence as well as a certain number of times for each one.
Interface
public interface IInterface
{
void MethodOne(string foo);
void MethodTwo(string foo);
}
Method
// MyClass stuff ...
public async Task Run()
{
MethodOne("foo");
MethodTwo("foo");
}
// ...
Tests
I've written this test to verify that the methods are only executed a certain amount of times (once):
[TestMethod]
public async Task Test()
{
var mock = new Mock<IInterface>();
var mockSequence = new MockSequence();
var obj = new MyClass();
await obj.Run();
mock.Verify(i=> i.MethodOne("foo"), Times.Once());
mock.Verify(i=> i.MethodTwo("foo"), Times.Once());
}
This works fine...
I've tried these tests for determining a certain sequence is properly met, but the test seems to always pass.
[TestMethod]
public async Task Test()
{
var mock = new Mock<IInterface>();
var mockSequence = new MockSequence();
var obj = new MyClass();
await obj.Run();
mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo"));
}
Should pass, and does...
[TestMethod]
public async Task Test()
{
var mock = new Mock<IInterface>();
var mockSequence = new MockSequence();
var obj = new MyClass();
await obj.Run();
mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo")); // swapped order here
mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
}
Should not pass, but does...
What do I need to do differently to verify proper sequence is met?
How do I combine the two so that I can test number of execution times AND proper sequence?

Your example tests for InSequence seem to be in the wrong order. You should be performing the Setup, before you invoke the Run method. I've assumed this is a typo and that your actual tests do the Setup, then invoke the object in the correct order. It's also not clear how your mock of IInterface gets to MyClass. In my final example, I've assumed it's actually injected...
Moq's InSequence support only seems to work if you're using strict mocks. If you create your Mock like this:
var mock = new Mock<IInterface>(MockBehavior.Strict);
Then this will work:
mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo"));
while this fails:
mock.InSequence(mockSequence).Setup(i => i.MethodTwo("foo")); // swapped order here
mock.InSequence(mockSequence).Setup(i => i.MethodOne("foo"));
Note, the error that comes is about the method missing a corresponding setup... rather than it being called out of sequence, which isn't necessarily the easiest to decode if you're not expecting it.
The alternate approach, if you don't want to / can't use strict mocks is to implement your own sequence checking using callbacks. Something like this:
int sequence = 1;
mock.Setup(i => i.MethodOne("foo"))
.Callback(()=>Assert.AreEqual(1, sequence++, "Method called out of sequence"));
mock.Setup(i => i.MethodTwo("foo"))
.Callback(() => Assert.AreEqual(2, sequence++, "Method called out of sequence"));
var obj = new MyClass(mock.Object);
await obj.Run();

This may be further off topic than you want to go but NSubstitute is a great mocking library that handles this very well.
In NSubstitute it's just:
var mock = Substitute.For<IInterface>();
var obj = new MyClass();
await obj.Run();
Received.InOrder(() => {
mock.MethodOne("foo");
mock.MethodTwo("foo");
});

Related

Returning default mock results from actual objects' instances

Consider a scenario when you need to write integration tests for the REST API controllers. I use MSTest for this. The logic is following:
Create new database, run init SQL migrations
Create test HTTP Client and make calls against endpoints
Dispose of test DB.
Use-case Most of the endpoints do validation, whether SQL command (INSERT, UPDATE, DELETE) returned a success result (boolean true) or not. To replicate this scenario I either need to know exact SQL failure conditions (hard), or, the easier option is to be able to mock repositories. Sure this is no longer an complete integration test, but at least I am able to test REST business logic that way and HTTP response status codes. Additional benefit is testing DB transaction rollback functionality.
Question Therefore, I would like to know - it it possible to create a mock, that would return all values of actual object instance by default, unless setup() override is provided?
Example/working code On test initialization, we create an instance of a HTTP test client and a mocked repository:
_usersRepo = new UsersRepository(); // actual instance of repository
var application = new WebApplicationFactory<Startup>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
_usersRepoMock = new Mock<IUsersRepository>();
// setting mock just for 1 of the methods manually
_usersRepoMock
.Setup(p => p.GetByIdAsync(It.IsAny<long>()))
.Returns(async (long r) => await _usersRepo.GetByIdAsync(r));
services.AddSingleton<IUsersRepository>(_usersRepoMock.Object);
});
});
_client = application.CreateClient(new WebApplicationFactoryClientOptions());
See that I have manually configured a mock to return the results from an actual repository for a single method.
Then, in a test class, whenever I need something to fail I do the following:
[TestMethod]
public async Task Db_throws_exception()
{
_usersRepoMock.Setup(p => p.GetByIdAsync(It.IsAny<long>())).Throws(new System.Exception("test"));
//.. rest of the code
}
This solution works as expected, HOWEVER, the problem is that in order for this approach to work I need to manually proxy all mock object requests to actual instances. I have around 4-6 methods in each repo and about 8 different repos (more in the future) and I would like to avoid doing it. Is it possible?
Here is what I want to avoid doing:
_usersRepoMock = new Mock<IUsersRepository>();
_usersRepoMock.Setup(p => p.GetByIdAsync(It.IsAny<long>())).Returns(async (long r) => await _usersRepo.GetByIdAsync(r));
_usersRepoMock.Setup(p => p.Create(It.IsAny<long>())).Returns(async (long r) => await _usersRepo.Create(r));
_usersRepoMock.Setup(p => p.Update(It.IsAny<long>())).Returns(async (long r) => await _usersRepo.Update(r));
_repo2Mock = new Mock<IRepo2>();
_repo2Mock.Setup(p => p.Method1(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method1(r));
_repo2Mock.Setup(p => p.Method2(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method2(r));
_repo2Mock.Setup(p => p.Method3(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method3(r));
_repo2Mock.Setup(p => p.Method4(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method4(r));
_repo2Mock.Setup(p => p.Method5(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method5(r));
_repo3Mock = new Mock<IRepo3>();
_repo3Mock.Setup(p => p.Method1(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method1(r));
_repo3Mock.Setup(p => p.Method2(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method2(r));
_repo3Mock.Setup(p => p.Method3(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method3(r));
_repo3Mock.Setup(p => p.Method4(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method4(r));
_repo3Mock.Setup(p => p.Method5(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method5(r));
There are some similar questions on stackoverflow, but I couldn't find the same use-case as mine nor an acceptable answer to the problem.. Here is one example: Default behavior for mock inherited from object instance
p.s. I have probably spent more time writing this question than it was to write the code for mocks :)
I think you can achieve what you want using one of the overloads of the Mock constructor.
First, create an implementation of IUsersRepository in your test project and make sure all its members are virtual. Implement all members using the default behavior.
class TestUsersRepository : IUsersRepository
{
public virtual async Task<User> GetByIdAsync(long id)
{
// (...)
}
// (...)
}
Then in your test, create a Mock using your test repository.
You should now be able to override the members that you want using the Setup method.
[TestMethod]
public async Task Db_throws_exception()
{
var mockRepo = new Mock<TestUsersRepository>(() => new TestUsersRepository());
mockRepo .Setup(p => p.GetByIdAsync(It.IsAny<long>())).Throws(new System.Exception("test"));
//.. rest of the code
}

Redis StringSetAsync returns false in Unit Tests using Moq

I am currently writing unit tests for a RedisProvider Class for my Solution. I have a method in my class that sets a key in the Redis Cache:
public async Task<bool> StringSetAsync(string key, string value)
{
var cache = multiplexer.GetDatabase();
//Some other stuff
result = await cache.StringSetAsync(key, value);
return result;
}
In my unit tests I have the following setup:
var mockMultiplexer = new Mock<IConnectionMultiplexer>();
mockMultiplexer.Setup(_ => _.IsConnected).Returns(true);
mockMultiplexer.Setup(_ => _.Configuration).Returns(MockedData.CacheConnectionString);
var mockDatabase = new Mock<IDatabase>();
mockMultiplexer
.Setup(_ => _.GetDatabase(It.IsAny<int>(), It.IsAny<object>()))
.Returns(() => mockDatabase.Object);
RedisCacheProvider mockedCacheProvider = new RedisCacheProvider(new Mock<ILogger<RedisCacheProvider>>
().Object, mockMultiplexer.Object);
var result = await mockedCacheProvider.StringSetAsync(It.IsAny<string>(), It.IsAny<string>());
When I run the above code for my unit test, the result that I receive is always false. Is there a way to get a true result? What is wrong over here?
I realized that this should be mocked by the services using the class rather than validating this on the class itself.
Here you go:
mockedCacheProvider.Setup(h => h.StringSetAsync(
It.IsAny<string>(),
It.IsAny<string>())).ReturnsAsync(() => true);

Moq Test - Mocked Service Call Always Returns Null

I am new to Moq and Mocks/Unit Testing in general. After watching a video on Mocking which uses Moq, I thought I had enough understanding to start setting up a few simple tests for a project I am working on. However, no amount of fiddling with the code helps my first test pass.
Here is the code I have:
Interface Being Tested
public interface IDataInterface
{
Task<IList<T>> GetData<T>(string url);
// Other stuff
}
Implementation of Interface Method GetData<T>
public async Task<IList<T>> GetData<T>(string url)
{
try
{
var json = await client.GetAsync(url).Result.Content.ReadAsStringAsync();
var data = (JObject)JsonConvert.DeserializeObject(json);
JArray arr = (JArray)data["resource"];
return arr.ToObject<IList<T>>();
}
catch (InvalidCastException icEx)
{
throw new InvalidCastException("An error occurred when retrieving the data", icEx);
}
// Other catches
}
Service Calling Implemented Interface GetData<T>
public async Task<IList<MyObj>> GetActiveObjs()
{
var data = await _myImplementedInterface.GetData<MyObj>(ActiveUrl);
return data;
}
My Test
[Fact]
public async void MyImplementedInterface_GetActive_ReturnsDataOrEmptyList()
{
var _activeUrl = "http://some.url";
using (var mock = AutoMock.GetLoose())
{
mock.Mock<IDataInterface>()
.Setup(x => x.GetData<MyObj>(_activeUrl))
.Returns(Task.FromResult(_SomeStaticDataRepresentation)));
var svc = mock.Create<MyService>();
var expected = _SomeStaticDataRepresentation;
var actual = await svc.GetActiveObjs();
Assert.True(actual != null);
// Other assertions that never matter because of the null value of _actual_ variable
}
}
Early on, I had issues because the project uses Autofac and Moq, but I resolved those specific issues. However, I cannot seem to get past the null value coming back from the service call. When running the project, the method returns data, just as expected, so I am flummoxed as to where the issue lies. Reviewing various posts and the Moq Quickstart did not provide what I needed to solve this myself, so my hope is there is someone here who can tell me what I am doing wrong. I apologize already as I am certain it is a newbie mistake.
Addressing the implementation first. Avoid mixing async-await and blocking calls like Wait() and .Result which can result in deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
public async Task<IList<T>> GetData<T>(string url) {
try {
var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
var data = (JObject)JsonConvert.DeserializeObject(json);
JArray arr = (JArray)data["resource"];
return arr.ToObject<IList<T>>();
} catch (InvalidCastException icEx) {
throw new InvalidCastException("An error occurred when retrieving the data", icEx);
}
// Other catches
}
For the subject method under test, if nothing needs to be awaited then there is no need to make the function async, just return the Task
public Task<IList<MyObj>> GetActiveObjs() {
return _myImplementedInterface.GetData<MyObj>(ActiveUrl);
}
Now for the test, since already using tasks, then the test should be async and the subject awaited as well.
[Fact]
public async Task MyImplementedInterface_GetActive_ReturnsDataOrEmptyList() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
IList<MyObj> expected = _SomeStaticDataRepresentation;
mock.Mock<IDataInterface>()
.Setup(x => x.GetData<MyObj>(It.IsAny<string>()))
.ReturnAsync(expected);
var svc = mock.Create<MyService>();
//Act
var actual = await svc.GetActiveObjs();
//Assert
Assert.True(actual != null);
Assert.True(actual == expected);
// ...
}
}
Assuming, based on what was shown it is uncertain what the active URL would have been, it could be ignored in the test using It.IsAny<string>().

How to Mock IMongoDatabase

I'm using Moq for mocking my objects in an ASP.NET Core project.
I want to mock the following IsConnection() method:
public Client(IMongoClient client)
{
_client = client;
}
public async Task<bool> IsConectionOk()
{
var pingCommand = new BsonDocument("ping", 1);
var mongoDb = _client.GetDatabase("Name");
var commandResult = await mongoDb.RunCommandAsync<BsonDocument>(pingCommand);
return commandResult != null;
}
As you see, there's only one injection, IMongoClient, so I need to mock this one. Now, I need to mock IMongoDatabase as well since the _client.GetDatabase returns me an IMongoDatabase which runs RunCommandAsync
this is my unit test:
[Fact]
public async Task IsConnectionOk_xxx_RunPing1Command()
{
var dbMock = new Mock<IMongoDatabase>();
var resultCommand = new BsonDocument("ok", 1);
dbMock.Setup(stub => stub.RunCommandAsync<BsonDocument>(It.IsAny<BsonDocument>(), It.IsAny<ReadPreference>(), It.IsAny<CancellationToken>())).ReturnsAsync(resultCommand);
var mongoClientMock = new Mock<IMongoClient>();
mongoClientMock.Setup(stub => stub.GetDatabase(It.IsAny<string>(), It.IsAny<MongoDatabaseSettings>())).Returns(dbMock.Object);
var client = new Client(mongoClientMock.Object);
var pingCommand = new BsonDocument("ping", 1);
//act
await client.IsConectionOk();
//assert
dbMock.Verify(mock => mock.RunCommandAsync<BsonDocument>( It.Is<BsonDocument>(x => x.Equals(pingCommand)) , It.IsAny<ReadPreference>() ,It.IsAny<CancellationToken>()));
}
You can see that I mocked a IMongoDatabase so my mongoClientMock can return it when the code is executing. When code is running, I have checked that mongoClientMock.GetDatabase() is returning a MongoDatabase (good until there), the problem is that when MongoDatabaseMock calls RunCommandAsync is not returning what I set up, it just returns null. I don't know what I could be missing here, any thoughts?
Things are a little tricky here.
Some background first.
According to documentation, IMongoDatabase.RunCommandAsync<TResult> is defined as
Task<TResult> RunCommandAsync<TResult>(
Command<TResult> command,
ReadPreference readPreference = null,
CancellationToken cancellationToken = null
)
Note the Command<TResult>, while in your code you pass a BsonDocument.
Luckily BsonDocument has an implicit conversion operator from BsonDocument to Command<TResult>
When a setup does not get what was configured it tends to return null. So you need to make sure that it is setup properly so that it performs the expected behavior.
[TestClass]
public class UnitTest1 {
[TestMethod]
public async Task _IsConnectionOk_xxx_RunPing1Command() {
var dbMock = new Mock<IMongoDatabase>();
var resultCommand = new BsonDocument("ok", 1);
dbMock
.Setup(stub => stub.RunCommandAsync<BsonDocument>(It.IsAny<Command<BsonDocument>>(), It.IsAny<ReadPreference>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(resultCommand)
.Verifiable();
var mongoClientMock = new Mock<IMongoClient>();
mongoClientMock
.Setup(stub => stub.GetDatabase(It.IsAny<string>(), It.IsAny<MongoDatabaseSettings>()))
.Returns(dbMock.Object);
var client = new Client(mongoClientMock.Object);
var pingCommand = new BsonDocument("ping", 1);
//act
var actual = await client.IsConectionOk();
//assert
Assert.IsTrue(actual);
dbMock.Verify();
}
}
Just found my problem, take a look at the next line:
dbMock.Setup(stub => stub.RunCommandAsync<BsonDocument>(It.IsAny<BsonDocument>(), It.IsAny<ReadPreference>(), It.IsAny<CancellationToken>())).ReturnsAsync(resultCommand);
turns out that RunCommandAsync first parameter is a Command<TResult> so the fix I needed was:
dbMock.Setup(stub => stub.RunCommandAsync<BsonDocument>(It.IsAny<Command<BsonDocument>>(), It.IsAny<ReadPreference>(), It.IsAny<CancellationToken>())).ReturnsAsync(anyResultCommand);
And problem solved!

Moq / Unit Test Unable to Catch Exception unless stepping through code

I have a Unit Test, that is expecting an Exception as the return parameter.
If I run the code through the Debugger, and inspect various elements it works.
If I just run all th tests, it doesn't.
The issue has to be something to do with the fact that the method is Async, as if I remove the various Async elements it works as expexted. (I guess its a timing issue)
my unit test is;
[TestMethod]
[ExpectedException(typeof(System.AggregateException))]
public void Service_GetDoc_ThrowsExceptionIfNull()
{
var nullRepository = new Mock<CCLDomainLogic.Repositories.DocRepository>();
IDD emptyDoc = null;
nullRepository
.Setup<Task<CCLDomainLogic.DomainModels.Doc>>
(x => x.GetDocAsync(It.IsAny<int>()))
.Returns(() => Task<CCLDomainLogic.DomainModels.Doc>.Factory.StartNew(() => emptyDoc));
DocService s = new DocService(nullRepository.Object);
var foo = s.GetDocAsync(1).Result;
}
And the code (abbreviated is)
public async Task<Doc> GetDocAsync(int id)
{
Stopwatch timespan = Stopwatch.StartNew();
try
{
...
{
var t = await Task.Run(() => _repository.GetDocAsync(id));
...
}
timespan.Stop();
if (t == null)
{
throw new ArgumentNullException("DocService.GetDocAsync returned Null Document");
}
}
catch (Exception e)
{
throw new Exception("Error in DocService.GetDocAsync", e);
}
}
So how do I rework this test to catch the excpetion when running async.
And as a bonus question, can I modify my unit test so I can check for specific exceptions, not the Aggregate Exception?
The reason that your test runs async code that executes after the assert has already run (or not).
Using the debugger only give the async code more time to workl.
In order to test your code all you have to do is chnage the return type of your MTest to Task and add await to thje relevant call:
[TestMethod]
[ExpectedException(typeof(System.AggregateException))]
public async Task Service_GetDoc_ThrowsExceptionIfNull()
{
var nullRepository = new Mock<CCLDomainLogic.Repositories.DocRepository>();
IDD emptyDoc = null;
nullRepository
.Setup<Task<CCLDomainLogic.DomainModels.Doc>>(x => x.GetDocAsync(It.IsAny<int>()))
.Returns(() => Task<CCLDomainLogic.DomainModels.Doc>.Factory.StartNew(() => emptyDoc));
DocService s = new DocService(nullRepository.Object);
var foo = await s.GetDocAsync(1).Result;
}

Categories

Resources