-4

Lets say you have a class that has the responsibility producing a set of finished data, but the method of producing that data is intentionally an implementation detail and as such should be left inaccessible to clients. How do you unit test such an API, knowing as the implementor that the data will change over time? Ideally you'd want to mock the underlying dataset, but the fact that it's an implementation detail means the dataset is not exposed and should not be. The client application should never have any reason to change the underlying data access methods, but the test still has need to mock it.

For a simple example, lets say you have two APIs. The first returns a set of integers that form a data set. The second API is a subset of that, representing integers that need to be filtered out of the first set. Your function has the responsibility of returning the final set of integers, but the fact that it makes API calls internally is an implementation detail that there is otherwise no reason to expose to the outside. Following principles of good class design, the only method that should be visible from the outside is the one function that returns the set of integers, but in order to unit test it you would need to override those internal API calls to provide consistent controlled data. I want to be able to unit test this function to make sure it return that right data, but by all rights I shouldn't expose any of the necessary components that are purely implementation details.

Is there a way to have the best of both worlds? Are there better design principles that I'm unaware of that describe how to break down a problem of this style?

Darinth
  • 127
  • 7
  • What about mocks? – πάντα ῥεῖ Mar 16 '20 at 18:40
  • How do you mock an internal implementation detail of a class? It's not visible on the outside to be overridden and I'd generally say shouldn't be. I could make it visible from the outside... but doing so is now exposing internal implementation details that should normally be hidden. – Darinth Mar 16 '20 at 18:51
  • 1
    @Darinth: if calling that external API would be really an implementation detail, then there would be no need to mock it out, you could just test the class or component without caring how it is implemented. But if you don't get "consistent controlled data" that way, then it is definitely not an implementation detail. So encapsulate the external API behind an interface which is injected into the "subject under test" - problem solved. – Doc Brown Mar 16 '20 at 18:55
  • 2
    Does this answer your question? [What's the idea behind mocking data access in unit tests](https://softwareengineering.stackexchange.com/questions/262686/whats-the-idea-behind-mocking-data-access-in-unit-tests) – gnat Mar 16 '20 at 19:21
  • @gnat No, it unfortunately does not. This is not the point of the question, I understand full well the value of mocking data access, and mocking the data access I feel is really the right solution here... but I feel like the fact the class does data access is an implementation detail that shouldn't be exposed... but in order to mock it you'd have to expose it. – Darinth Mar 16 '20 at 19:40
  • @DocBrown It feels like you're saying functionality of a class that should never be touched by an end-user should be left exposed because you want to test it and that isn't sitting right with me. Things can be implementation details and still be something a test might need access to. Tests are in the unique position of being something written by the author of a class and in need of simulating circumstances on demand that out in the real world may be rare. (such as testing how certain time-related code may behave on the last day of a month) – Darinth Mar 16 '20 at 20:12

1 Answers1

2

How do you mock an internal implementation detail of a class?

You change your design.

One of the implicit ideas in Test Driven Development is that we decouple complicated code from code that is difficult to test.

Parnas 1971 gave us guidance here; we create module boundaries around parts of our solution that are likely to change. Here, the "change" that we want is a different implementation of the data fetch depending on which environment we are running in.

So our in memory calculation would be in one module, and the I/O would be in a different module, and we would arrange the composition such that, in our tests, we could use a test double to isolate the logic (the subject of the test) from the I/O boundary and its complexities.

VoiceOfUnreason
  • 32,131
  • 2
  • 42
  • 79
  • So the solution here is to move the code that performs the data manipulation into it's own completely separate, but testable, module. The module can then still be called from within the module that makes the API calls, but the fact that it does so can still be hidden and left as an implementation detail. It *feels* like a violation of data hiding, a way to sidestep the reality that you want a way to test an implementation detail of something without really needing to test the whole thing. – Darinth Mar 16 '20 at 19:33
  • 1
    In truth, once you have the idea, that are lots of different ways to arrange the parts. But the solution you describe should work. To an extent, you are right that your tests prove that B works, but don't prove that B is the thing called by A. The heuristic I draw upon here is that we are really trying to detect accident, not malice. – VoiceOfUnreason Mar 16 '20 at 19:44
  • Proving that B works is what I want, I don't care that B is the thing called by A. It's exactly what I need, I just wish there was a better way to do it. After all, why make a completely separate module that only exists to serve as a super-simple subcomponent of another class. It makes me wish that there was an effective way to just simply say that test classes have the ability to access implementation details that shouldn't be otherwise accessible. C++ actually provides this through `friend` but so far as I'm aware is alone in that fact. – Darinth Mar 16 '20 at 19:54