1

Consider the following function:

def get_api_status(api_client):
    response = api_client.get('/status/')
    return response.content

and the test for it:

def test_get_api_status():
    mocked_api_client = MagicMock()  # depth 0
    mocked_response = MagicMock()  # depth 1
    mocked_response.status = 'ok'  # depth 1.1
    mocked_api_client.get = MagicMock(return_value=mocked_response)  # depth 0.1
    assert get_api_status(mocked_api_client) == 'ok'

The above example is really simple but you can see the pattern. What if the response.content would also have method get_data which would have the method get_raw_bytes? The question is: "when does code start to smell?".

My assumptions:

  1. Let's say api_client is here to reduce coupling. api_client in real life is subclassed either http_client or filesystem_client.
  2. api_client.get does always return api_response which has .content attribute.

My thoughts:

I suspect the problem is either:

  • The example is fine. It's the maximal level of complexity. If api_response.content would really have also the method get_data and the function get_api_status would use it then it should be recognised as wrong design.
  • tight coupling between get_api_status and the api_client abstract - do I need to make one more abstract??
2
  • 4
    Needing to mock multiple layers is a sign that your dependency is leaking across the codebase - other code is coupled to an interface you don't own. It suggests a Law of Demeter problem too. Your test double should ideally return something you own, a data object you can create easily without lots of mocks.
    – jonrsharpe
    Commented Sep 16, 2020 at 18:53
  • Thanks for the "Law of Demeter". I guess then it is the design problem. If I would keep making good design I wouldn't have to mock deep.
    – Qback
    Commented Sep 16, 2020 at 19:06

1 Answer 1

2

IMHO we gave to distinguish between dependencies (other code that provides behavior) and Data Transfer Objects / Value Objects .

DTOs

This code primary hold data and have at most very simple logic, if at all, and does not have Dependencies (references to code of the other type) themselves. I would not mock DTOs (like response in your example) as well as any collection. DTOs and collections are cheap to construct. And when you need to configure them some levels deep you couldn't come around that by mocking anyway.

Dependencies

This is other code that provides behavior. There should be no need to mock Dependencies beyond one level dept. Some times we need to dive one level down because the direct dependency is a factory. If you find that one level down is not enough you might think al little about your design...

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.