Unit Testing – Does Test-Driven Development Force Following SOLID?

solidtddunit testing

I hear a lot from TDD practitioners that one of TDD's advantages is that it forces developers to follow SOLID principles (Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion). But as for me it is enough to just write some tests (unit test primarily) to understand it is important to follow SOLID (and thus create testable architecture).

Does TDD force developers to follow SOLID more actively than just writing unit tests?

Best Answer

First of all, TDD does not strictly force you to write SOLID code. You could do TDD and create one big mess if you wanted to.

Of course, knowing SOLID principles helps, because otherwise you may just end up not having a good answer to many of your problems, and hence write bad code accompanied by bad tests.

If you already know about SOLID principles, TDD will encourage you to think about them and use them actively.

That said, it doesn't necessarily cover all of the letters in SOLID, but it strongly encourages and promotes you to write at least partly SOLID code, because it makes the consequences of not doing so immediately visible and annoying.

For example:

  1. You need to write decoupled code so you can mock what you need. This supports the Dependency Inversion Principle.
  2. You need to write tests that are clear and short so you won't have to change too much in the tests (which can become a large source of code noise if done otherwise). This supports the Single Responsibility Principle.
  3. This may be argued over, but the Interface Segregation Principle allows classes to depend on lighter interfaces that make mocking easier to follow and understand, because you don't have to ask "Why weren't these 5 methods mocked as well?", or even more importantly, you don't have a lot of choice when deciding which method to mock. This is good when you don't really want to go over the whole code of the class before you test it, and just use trial and error to get a basic understanding of how it works.

Adhering to the Open/Closed principle may well help tests that are written after the code, because it usually allows you to override external service calls in test classes that derive from the classes under test. In TDD I believe this is not as required as other principles, but I may be mistaken.

Adhering to the Liskov substitution rule is great if you want to minimize the changes for your class to receive an unsupported instance that just happens to implement the same statically-typed interface, but it's not likely to happen in proper test-cases because you're generally not going to pass any class-under-test the real-world implementations of its dependencies.

Most importantly, SOLID principles were made to encourage you to write cleaner, more understandable and maintainable code, and so was TDD. So if you do TDD properly, and you pay attention to how your code and your tests look (and it's not so hard because you get immediate feedback, API and correctness wise), you can worry less about SOLID principles, in general.