• Tamo240@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    5 hours ago

    I feel like there are two concepts be at confused here. ‘Mocking’ is just replacing an actual implementation with one that reports its usage, so calls or lack thereof can be asserted to occur, and tests can fail if that condition is not met. They usually allow setting side effects and return values on a per call basis also, to inject different behaviours for covering different code paths easily.

    The question is then how do I get a class like DatabaseWrapper to call into an underlying mockDB instead of the normal realDB? The answer, in static languages is dependency injection: the db object must be constructed externally to the wrapper, and passed in in such a way that any object with the same interface is acceptable.

    This allows the tests to pass in a mock with the same interface, and have the class being tested accept it. The class will then run as usual when its methods are called, but we can make assertions about how it uses its dependency. In some languages, such as python (and it seems JavaScript as well) this can be bypassed by monkey-patching the private member dynamically after the object has been constructed to be the mock instead of the real.

    Personally, I don’t think this leads to good design. Dependency injection also allows for a nice port and adapter pattern, were in the future we might replace our SQL database with a MongoDB one, and we have to rip up the application, instead of just implementing a new db class that meets the interface, and injecting that into the wrapper instead.