2

We have a Unit Test Case where we want to assert that after a login operation, the SUT is able to call a method on one of it's dependencies. This dependency is a NavigationManager and the expectation is that it calls NavigationManager.NavigateTo(string).

Up to this point, we are mocking the NavigationManager to isolate the SUT. This is our test method:

var calledNavigatedToMethod = false;
nmMock.Setup(nm => nm.NavigateTo(NavigableScreens.MyNextScreen)).Callback(() => calledNavigatedToMethod = true);

SUT.Login();

Assert.IsTrue(calledNavigatedToMethod);

As you can see, NavigableScreens.MyNextScreen is a constant, which make us think this is not a dependency, ergo it shouldn't be injected/mocked. We are merely testing that when SUT.Login() is called, its dependency calls a method with said constant.

Our confusion is that, by the time we were writing the unit test, NavigableScreens.MyNextScreen wasn't yet defined, which made the test dependable on it's existence, and made us think that for that reason it should be injected/mocked.

A proposed argument is that since it is a dependency of the SUT, it MUST exist, which means using this constant in the Test Case is perfectly fine, as it is the exact same thing as calling NavigationManager.NavigateToMyNextScreen() in the way that if the method doesn't exist by the time the test is written, we'd have the same issue, so it MUST exist.

The question would be, is the use of this constant in the test case wrong, making it a non-unit test?

2 Answers 2

4

If the current requirements are that after login, you must always navigate to the screen indicated by the constant NavigableScreens.MyNextScreen, then that most likely isn't a dependency that should be injected. It is just a value that you pass on to another function anyway.

However, NavigationManager looks to be a class with a fair bit of functionality in it. That is something that should be injected as a dependency into the SUT (unless your SUT is NavigationManager itself).
Given that, your unit-test is fine.


When looking at where to use mocks/stubs/fakes in your unit-tests, there are some considerations to keep in mind (besides needing DI for other purposes than testability):

  1. Is the code that isn't strictly part of the SUT stable and well-tested enough that I am very unlikely to uncover problems with it? If not, then it should be mocked.

  2. Are the operations performed by the extra code complex or time-consuming enough that they might affect how fast my unit-tests execute? If they are, you should mock those operations.

  3. Are the operation performed by the extra code related to persistence? If so, they really should be mocked. When databases or other persistence mechanisms come into the picture, it becomes really hard to keep your tests independent of each other. You really want that complexity only when you are actually testing such mechanisms.

2

One perspective is that you always need a justification to mock, rather than a justification to use the real dependency.

Mocked tests are, simply put, worse than real tests;

  • they test less
  • require more maintenance
  • are harder to write
  • produce both false positives and false negatives
  • have results that are more difficult to interpret
  • run slower (may vary depending on environment, but they generally do everything in a highly dynamic way that beats most optimisers)

Sometimes those trade-offs are worth it; the alternatives are slower, less reliable, or flat-out impossible. But, clearly, none of those apply in the case of a simple string constant. So here you can safely say there is no justification for mocking that dependency rather than using the real thing.

The corollary is the fact that you are even considering that as an option in that cut-and-dried case reveals that you are very likely overusing mocks. Try going through your test suite and think

What would happen if I replaced this usage of a mock with the real thing?

Would it perhaps run faster, detect more issues, give a better description of failures?

Would it be more refactorable, shorter, more reliable, provide better design guidance?

If you find a lot of cases where the other way of writing the test is better, in the above sense, then that's a strong argument they should be written that way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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