Is there a way to test combining both TestServer and moq Mock to replace methods in classes in ConfigureTestServices?

Viewed 2434

I am able to do integration tests using TestServer, and I can manually mock methods in any DI injected class by replacing in ConfigureTestServices the class by a mocking one as follows :

var webHostBuilder =
    new WebHostBuilder()
        .UseEnvironment("Testing")
        .UseContentRoot(projectDir)
        .ConfigureTestServices(s =>
        {
            s.TryAddTransient(IMyClass, MyMockMyClass);
        })
        .UseStartup<Startup>();

Where MyMockMyClass is a remplacement of MyClass, with the purpose of replacing a method (eg: Method1).

Is there an option to use a moq Mock.Of<MyClass> to quickly replace on the fly my Method1, without the need to create a new class MyMockMyClass? Something like :

var mymock = Mock.Of<IMyClass>();
Mock.Get(mymock ).Setup(m => m.Method1(It.IsAny<string>()).Returns(value: whatever);

And then somehow using this mymock with the line s.TryAddTransient(IMyClass, ... in the ConfigureTestServices code above?

2 Answers

Have the factory delegate return the mocked services when configuring the test server

var mymock = Mock.Of<IMyClass>();
Mock.Get(mymock)
    .Setup(m => m.Method1(It.IsAny<string>())
    .Returns(value: whatever);

var webHostBuilder =
    new WebHostBuilder()
        .UseEnvironment("Testing")
        .UseContentRoot(projectDir)
        .ConfigureTestServices(services => {
            services.RemoveAll<IMyClass>();//Remove previous registration(s) of this service
            services.TryAddTransient<IMyClass>(sp => mymock);
        })
        .UseStartup<Startup>();

If you need a new mocked instance per call then move the logic into the factory delegate

It's possible to build a service collection using mocks and feed that to your test server, but you should not. The test server is for doing integration tests, whereas mocks are for unit testing. You need to determine what kind of test you're doing, and choose one or the other.

Unit testing, as the name indicates, is concerned with testing one discrete unit of functionality. Mocks are a way to remove variables so you can ensure that one thing you're testing works or not.

Integration testing, on the other hand, is about testing a system top-down. You want to ensure that a given input results in a given output, using everything in the system as it exists. If you throw in mocks, then you're not testing anything, because you now don't know if it works or not simply because of the mock or the actual system is broken.

For example, let's say that you have a service that a particular action uses, which has a bug and will cause the action to throw an exception live. However, you replace it with a mock in your test server, which works fine. Your test passes, because the mock is working, but then when you push it live, everything breaks. Conversely, you could fail to mock the service correctly, and your test could fail even though the actual service is fine. Unless you test with your real system, you have no assurance of anything.

Related