Unit tests and system clock
It took me way to long to learn this. Your code (and their unit tests) should inject the system clock as a dependency.
An example, let’s say you have a service that writes a record to the database with the system clock.
public void save(String userName) {
long currentTimeMs = System.currentTimeMillis();
User user = User.builder()
.name(userName)
.updateTimeMs(currentTimeMs);
database.save(user);
}
How would you test this? You can inject a mock database instance and use it to verify that it got a User object. Great! You can verify the username is as expected. How do you verify that tricky business rule that updateTimeMS is the “current time”?
For too long, I simply didn’t test that. Just verify that updateTimeMS > 0, it’s good enough, right? In this case, you should inject the system clock as an explicit dependency instead.
private Clock clock;
public void save(String userName) {
long currentTimeMs = clock.getCurrentTimeMilis();
User user = User.builder()
.name(userName)
.updateTimeMs(currentTimeMs);
database.save(user);
}
In fact, Java introduced the java.time.Clock class (introduced in Java 8!) as object oriented API to replace code like System.getCurrentTimeMillis().
The concept applies across different languages but Java holds a special place in my heart.
In Python, the situation is a bit different. Python allows you to monkey patch module functions. It seems that a full
OOP idiom is not dogmatic in Python. So the alternative would be to rely on unittest.mock.patch
def save(user_name: str) -> None:
database.save(name=user_name, time=time.time())
...
@patch('time.time', return_value=123)
def test_save_user(self) -> None:
mock_db = MagicMock()
user_repository = UserRepository(database=mock_db)
user_repository.save("foo")
mock_db.save.assert_called_with(name="foo", time=123)
Not my style. But it’s Pythonic.