Question

I'm not sure if static vs. dynamic mock is the terminology used to describe this comparison, but I got this terminology from types of mocking static vs dynamic and Hand-rolled mocks made easy. To summarize, a dynamic mock is a proxy generated on the fly, whereas a static mock is pre-implemented. For a Python example given this abstract Repository, Value domain object and example DatabaseRepository implemention:

class Value:
    ...

class Repository(ABC):
    @abstractmethod
    def read_value(value_id: str) -> Value:
        pass

class DatabaseRepository(Repository):
    def __init__(self):
        self.database_client = ...

    def read_value(value_id: str) -> Value:
        # Use `self.database_client` and `value_id` to fetch a parsable `Value`.
        ...

A static mock would look like:

from abc import ABC, abstractmethod

class MockRepository(Repository):
    def read_value(_: str) -> Value:
        return Value(...)  # Ignores `value_id` and returns predefined `Value` for mocking purpose.

static_mock = MockRepository()

A dynamic mock would look like:

from unittest.mock import Mock

dynamic_mock = Mock(spec=Repository)
dynamic_mock.read_value.return_value = Value(...)

In the code bases I've worked with, I've rarely seen the static mock. Yet the static mock seems to come with numerous benefits, a major one being compile-time safely for constructing the mock object in a statically typed language (or in Python, safety that can enforced by a type checker like mypy).

The only benefit to dynamic mocks seems to be the conciseness (also mentioned by the two links), and while this is a valid benefit, is there anything I'm missing in regards to what a dynamic mock can do exclusively or more naturally compared to a static mock? Does design of a particular language (like statically typed vs. dynamically typed) tilt this comparison scale?

Was it helpful?

Solution

What you have here is a stub. Stubs are used to inject values into other objects. These values can be used to take the execution down different logical paths in the function under test. Return true, return false, throw exception.

Creating stubs dynamically (considered configurable stubs) just keeps the code base clean and reduces the need to manage large amounts of helper classes. Hard-coded stubs are custom built for one test, so you can imagine how that would go with a code base with 1000 of tests. Soon as the test suite gets bigger the management of hard coded stubs gets problematic.

Mocks are different. They remember how they were used and that can be verified as part of the test; i.e. the SaveToDatabase() method was called three times.

There is a fantastic book called xUnit Test Patterns that contains everything you need to know about this topic. It's really good and shouldn't be too difficult to find.

Licensed under: CC-BY-SA with attribution
scroll top