4

I'm writing unit tests for a class that makes use of a general utilities module. In general I want to mock all the dependencies of the class, but mocking some of the utility functions feels weird. Some of them are simple functions for dealing with file paths, string manipulation, etc, and it seems like an implementation detail that the class under test uses this utility to implement its public API. If the code was refactored to not need that utility function, I wouldn't care and would prefer that the test not fail.

The problem is that some of the utilities functions depend on the filesystem, and obviously need to be mocked. So I'm considering either

  1. Leaving the utilities module as-is and mocking the filesystem module
  2. Or using most of the utilities module but mocking the individual functions that have dependencies on the filesystem

What have other people done in this scenario?

2
  • Also please see: When should I use mock objects?
    – user22815
    Commented Nov 30, 2015 at 20:28
  • Originally I disagreed with the answerers in the other questions, so I created my own, but actually I think the discussion about external vs internal dependencies is worth the duplicate flag. Commented Dec 1, 2015 at 5:57

2 Answers 2

5

It depends what you consider a "unit". Some people think a unit to be tested should be the tiniest part they can reduce matters to - , that is, just the function under test with everything it depends on mocked away. I think this is just taking things too far towards the ideology of some utopian dream of unit testing.

A unit should be a simple part of your program that is easy to test, without being too large that it becomes a mini-integration test. For many people this means the class, and many (including Martin Fowler) would argue that some dependent classes be included in that definition. So a utility class of file I/O wrappers would be perfectly fine to consider as part of the unit being tested.

So it's up to you to decide how granular to take your system for testing. If you think your filesystem module works fine (and has been tested itself perhaps) then simply include it and take the (very slight) risk that it might cause your function to fail testing (which isn't a bad thing as you can then fix it - which is what testing is all about). This may mean you spend far less time writing tests than you do fixing any potential bugs found while testing your function.

2
  • Well, I can't include the filesystem module as-is because I want to write tests against 'files' that don't actually exist on disk. But I see what you're saying. Thanks. Commented Dec 1, 2015 at 5:49
  • In addition to the answer. Some may say that "utilities" are "implementation details" and hence they should not be mocked since they are an integral part of the solution under test
    – Laiv
    Commented Dec 3, 2018 at 11:54
3

In general I want to mock all the dependencies of the class

I think that is where you took the wrong turn. If a class has dependencies, then those dependencies must never be mocked in the test for that class; that makes the test worthless. If that means the test for that class is not a unit test, then so be it. You can't do a stand-alone isolated test for something that is not stand-alone and isolated.

Mocking is for when an object has collaborators or outputs of some unknown type (e.g. typically the corresponding class has a field that is of an abstract or interface type).

For example, say you have:

enter image description here

You would mock (or otherwise stub out) FileBugRepository when testing BugService, because that's not a static class-level dependency, just one possible arrangement of objects at run-time. But when testing FileBugRepository, you should not mock FileUtils because it is being used directly, and is the only valid implementation.

In this case, FileUtils is a direct static dependency; in most languages the code of FileBugRepository will contain the name FileUtils. So you can't test it with something else without using some low-level magic to redefine the code you are testing to be something other than what it is.

That is of course the road to ruin, to thousands of tests with high coverage, high maintenance cost and no added value, and not having a good answer to the question 'why are you not mocking Integer and String'?

The general principle holds even if the code is written using the anti-pattern where every class has a separate interface, i.e. IFileUtils and FileUtilsImpl; then IFileUtils simply is an alias for FileUtilsImpl.

4
  • I'm not sure I understand the distinction between mocking a class-level dependency or an object-level dependency. In the second case, maybe I want to test the logic in FileBugRepository against a mocked out filesystem, which I might do by mocking FileUtils. Or maybe FileUtils is just really slow/expensive. Commented Dec 1, 2015 at 5:54
  • 1
    Wanting something logically impossible is usually unwise.
    – soru
    Commented Dec 1, 2015 at 11:03
  • What's logically impossible? Commented Dec 2, 2015 at 20:21
  • 1
    Wanting to test some logic that does something, while replacing the parts that do that thing. At least given a definition of 'test' as a thing that fails if and only if the object under test is wrong.
    – soru
    Commented Dec 3, 2015 at 16:47

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