How do you write testable code?

4 minintermediatetestingdesigndependency-injection

Quick Answer

Testable code is loosely coupled and dependency-injected so collaborators can be replaced with test doubles. Favor small single-responsibility classes, program to interfaces/abstractions, avoid static state and hidden dependencies (e.g., `DateTime.Now`, new-ing dependencies directly), and keep side effects at the edges. Pure functions and clear seams make code easy to test without elaborate setup.

Detailed Answer

Principles for testable code:

1. Dependency Injection:

  • Inject dependencies rather than creating them internally
  • Enables easy substitution with test doubles
// Bad - Hard to test
public class OrderService
{
    public void ProcessOrder(Order order)
    {
        var repository = new OrderRepository(); // Hard-coded dependency
        repository.Save(order);
    }
}

// Good - Easy to test
public class OrderService
{
    private readonly IOrderRepository _repository;
    
    public OrderService(IOrderRepository repository)
    {
        _repository = repository;
    }
    
    public void ProcessOrder(Order order)
    {
        _repository.Save(order);
    }
}

2. Single Responsibility Principle:

  • Each class/method should have one reason to change
  • Smaller, focused units are easier to test

3. Avoid Static Dependencies:

  • Static methods and classes are difficult to mock
  • Use interfaces and instance methods

4. Pure Functions When Possible:

  • Given the same input, always return the same output
  • No side effects
  • Easiest to test

5. Separate Logic from Infrastructure:

  • Keep business logic separate from database, file system, network calls
  • Makes logic testable without external dependencies

6. Avoid Hidden Dependencies:

  • Make all dependencies explicit in constructor
  • Don't use service locators or global state

7. Keep Methods Small:

  • Easier to understand and test
  • Single level of abstraction

8. Use Interfaces:

  • Program to interfaces, not implementations
  • Enables mocking and substitution