Don't test the structure
Tests are fitness functions that warn us about unintentional behaviors we have introduced when changing some parts of the code, breaking old behaviors.
Testing the structure of our code is a common practice when writing tests after writing code, but it can also happen when doing TDD.
Test Last
When you see the code done, it’s tempting as developers to write tests that make the code pass. Because we think we are right, we are super-optimistic with ourselves and very critics with others.
Probably from this behavior comes the wrong idea that developers should not test their own stuff.
One of the easiest ways to make our tests pass quick is to couple our tests with the internal parts of our code.
If you are trying to:
test private methods
mock all the collaborators independently of what they are doing
mock static components (when you could easily use dependency injection)
verify calls to methods of other collaborators when they are not void methods
testing the existence or absence of internal parts of your objects
Then probably your tests are very coupled to the internal parts of your system, instead of trying to add these things, change your production code to not having the need of doing this => refactor.
Test First
When writing tests first, it’s more difficult to couple them to the production code because you didn’t write the production code.
But if we don’t apply YAGNI (you are not going to need it) we could find ourselves overthinking the tests because the design is in our mind, and we are not waiting for the feedback coming from tests.
If we do small steps, we apply YAGNI and we refactor mercilessly, our tests will be low coupled to the structure of our code.
TDD Outside In
A lot of people think that for doing TDD outside in, it is needed to understand all the pieces of the puzzle before writing the test.
This is not true, I’ve explained in several posts how to use the feedback coming from tests to defer the design to the last responsible moment and not doing it upfront.
Things to trigger a change in your software design
Software design is iterative, the only case this is not going to happen is if you don’t need to add more features and there are no bugs. I haven’t seen this situation yet.
If you are doing TDD outside in, some signals that can help you to identify that your tests are coupled to the structure earlier:
Trying to find the existence or the absence of some internal components, not the existence or the absence of behaviors related to those components.
Creating test doubles too early for collaborators that are simple enough to be used directly.
Basically, all of this is telling you that you are doing too much upfront design and your tests will show it.
TDD inside out
When doing TDD inside out, it’s critical to select what units are the ones to start.
If all the units of your code are tested in isolation, you have to select very good what part has sense by themselves and which others are there just to hide details.
If your tests are coupled to each individual unit of the language, refactoring will be a nightmare. Because tests are superglue for the design.
As a rule, try to make your tests independent as much as you can from the internal parts of the service/component/xxx they are trying to test. Minimize the usage of test doubles, just use them to mark the borders between layers or to fake infrastructure.
Mocking tools are great but understand their tradeoffs is key.
Mocking libraries are a powerful tool
When I started learning about tests, I was developing in Java and JUnit.
Testing the structure of your code is a bad idea because it will disallow refactoring. It will be hard to change your mind in the future.
Instead of testing the structure, test behaviors, software should be something easy to change, this is the reason why we write tests.