Distinguish design and implementation detail when doing TDD

118 Views Asked by At

I have been doing Unit tests for a while. I am a little confused about the boundary between design and implementation details when doing TDD.

For example, I have two interfaces, service and adapter, that process employee information (add, get, delete ...)

public interface IEmployeeService
{
   Employee GetEmployeeById(int id)
}

public interface IEmployeeAdapter
{
   private IEmployeeService _service
   Employee GetEmployeeById(int id)
}

By design, service reads data from storage such as database, file system or web service, adapter uses service to get certain information.

This design looks fine until I start writing unit test for the adapters.

The problem is I need to know whether adapter.GetEmployeeById(id) will call service.GetEmployeeById(id) (or other methods) to determine whether I need to mock services in the test method. That makes feel like I am kind of considering the implementation detail when writing unit test. Is there anything wrong?

2

There are 2 best solutions below

0
On

Unit tests are white-box tests, so you are fully aware of what goes on inside the system under test. There's nothing wrong with using that information to help determine what to mock. Yes, it's an implementation detail, and it can make your test "fragile" in the sense of needing to change it when your underlying implementation changes. But in a case like this, I would want to know that when I call adapter.foo(), it calls underlyingService.foo(), and mocks are perfect for this.

0
On

The best rule of thumb I can advice is: try to use behavior setup/verification only in cases it is a part of your contract. In case you test behavior but what you are actually interested in is state, tests tends to break lot more often as the behavior is in fact an implementation detail.

In your example, if no one cares for the exact boundary between service and an adapter, feel free to use state verification on adapter class. But if your adapter is supposed to translate specific message calls patterns to another well-defined set of messages, you might want to use behavior verification instead. In other words, if adapter.GetEmployeeById(id) needs to translate to service.GetEmployeeById(id) then it is not an implementation detail.