-1

I have

public class SomeDomainService
{
    public SomeDomainService(IDependency1 someDependency1, IDependency2 someDependency2)
    {
        // ...
    }

    public void TestableMethod(int parameter)
    {
        var result1 = this.someDependency1.Method1(parameter);
        this.someDependency2.Method2(result1);
    }
}

I am creating a unit test to test SomeDomainService. The mock of someDependency1 will be setup to return result1 and the unit test will Verify (think Moq) that someDependency1.Method1(parameter) and that someDependency1.Method2(result1) were invoked.

Convince me my unit test is not just a duplication of the business logic. Or I am testing the wrong things? If I am wrong, what's the unit test for SomeDomainService supposed to test?

  • It should test what you care about it doing, not how it does it. If you care that someDependency1.Method1(parameter) is invoked, it should assert that happened. If you only care that the effect someDependency2.Method2(result1) occurs, you should assert that effect is happening. – bitsoflogic Oct 5 '18 at 20:49
4

Convince me my unit test is not just a duplication of the business logic. Or I am testing the wrong things? If I am wrong, what's the unit test for SomeDomainService supposed to test?

The materials you want to review

Sort summary: the method you are testing here is invoked for its effects on objects other than itself. That use case is a bit off the sweet spot for unit testing (which really loves functions, safe queries on stateful objects, and unsafe commands paired with safe queries on stateful objects).

Commands dispatched to other objects are... twitchy... in tests. The tend to be much more sensitive to implementation details than the other sorts. These kinds of tests tend to be shorter lived than the query supported sort.

The useful object pattern may help mitigate some of the risk. The fact that you are trying to measure something (did this method get called) may be a code smell - trying to tell you that there is a telemetry requirement that you haven't captured yet. If you decide that's right, you replace the mock with a useful object (production code) that includes the telemtry that you need, and your unit test turns back into the more common pair-a-command-and-a-query sort.

Of course, within the tests of the useful-object, you'll be back in the same boat you are now -- a raw call to some other component that you're probably going to mock out. There's no magic, we've just shuffled the side effect (and the mock) further away from the code that you are testing.

The basic discipline here being that as you get closer to the "boundary", the implementations should approach being "so simple that they obviously have no deficiencies", at which point you stop bothering with "unit/developer" tests, and instead squeeze out that last bit of confidence using integration/system/acceptance testing.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.