'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 initSQL
migrations - Create test
HTTP Client
and make calls againstendpoints
- 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 :)
Solution 1:[1]
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
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Batesias |