'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:

  1. Create new database, run init SQL migrations
  2. Create test HTTP Client and make calls against endpoints
  3. 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