-3

Suppose there are 2 layers below the layer being tested:

  1. Technical Logic Layer: calls the DAO layer.
  2. DAO layer: calls the database

(The layer being tested can call the Technical Logic Layer but not the DAO layer.)

If mocks have to be used, when is it better to mock the Technical Logic Layer vs the DAO layer? Let's suppose one has to be mocked because no other solution is allowed.

I'm looking for the pros and cons of each choice and why one is considered better. Be as comprehensive as possible.

2
  • I've seen arguments that only external calls like database calls should be mocked and that mocks should be avoided because they cause coupling. But if we have to mock at some point, it's not obvious to me where to do it. Either way the test has to know about implementation: if the technical logic layer is mocked then we can't change the layer being tested without changing the test. If we mock the database we can change the technical logic layer but only if it still calls the same DAO method. Mocking the database makes the test catch more bugs because it's closer to production however.
    – user7340
    Commented Apr 11, 2020 at 14:03
  • 1
    see What is the problem with "Pros and Cons"?
    – gnat
    Commented Apr 11, 2020 at 19:18

2 Answers 2

4

I'm looking for the pros and cons of each choice and why one is considered better.

Testing closer to the boundary is better because... covering more of your code is good, in that it improves the chances that mistakes you introduce are caught by your tests. Tests are, in a sense, little experiments we do to try to predict what will happen when we put this code into production -- the more "real" code we use, the more likely we are to get an accurate prediction.

Testing further from the boundary is better because... adding more unstable elements to a test increases the chance that the test will falsely report that an intended change somewhere far away is a fault. Your "technical logic layer" probably changes behavior if the behaviors of the underlying dependencies change, which means a lot of extra test maintenance work to do in the face of deliberate changes. See Parnas 1971. In addition, you've got this spooky action-at-a-distance thing going on -- why is the test aware of implementation details of a module that isn't even the subject of the test?

My unit tests are usually a consequence of TDD; in greenfield work, the details of the boundary are clear sooner then the internals. So my early tests tend to be very big, with mocks near the edges of the system, and then as the details of the design become more clear the mocks get closer to the test subject.

This is NOT the only answer, even for TDD. Growing Object Oriented Software (Pryce and Freeman) use a variant of "programming by wishful thinking" which tends to mock its way toward the boundary, rather than designing in from the boundary.

1
  • Thanks. This was as clear of a description as I've seen with a lot of searching.
    – user7340
    Commented Apr 12, 2020 at 6:40
0

It all depends on which layer you are testing, which would also be called the System Under Test. Generally you mock the next layer down. Assuming the "technical logic layer" (more often called the Business Layer) makes calls to the "data access layer," then mock the data access layer when testing the business layer.

You don't need to know the INSERT in the database works when testing the business logic. Your test should ensure the business logic works. This means limiting the reasons for the test to fail. Isolating external service calls eliminates a myriad ways your test can fail. If the test fails, it should be due to a flaw in the business logic or a change in requirements, not because a DBA decided to take the database down for maintenance, or because a web service was upgraded to an incompatible version.

Ensuring the INSERT to the database works is still valuable, but that is another test where you don't mock anything, because you are testing the integration between the business logic and data layers.

This doesn't mean you don't need to mock logic under your control that resides in a different layer. Simply because the code is in your codebase doesn't mean a mock isn't a valuable addition to your test. Objects in those other layers have their own complexity, which needs testing. Ideally those other objects should not cause your test to fail. Again, this is about limiting the number of reasons for your test to fail. It is a balancing act. Objects that are easily created before the test don't need to be mocked.

So to answer your question, yes.

Mock the database. Mock the higher layers. The lower layers. Until you need to ensure these different components coordinate properly, then you don't want to mock them.

Object/function mocking should stop where you have determined test failures will most likely occur due to a defect in the logic being tested.

2
  • Could you elaborate on the reasons for doing it this way? I do see what you're suggesting more often, but plenty of people advocate for mocking only calls to external services.
    – user7340
    Commented Apr 11, 2020 at 15:17
  • @user7340: updated with more info. Commented Apr 11, 2020 at 18:12

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.