DEV Community

Discussion on: My personal take on TDD

Collapse
 
dncrht profile image

It's down to the fact unit testing is "white box testing" in disguise.
People don't consider the behaviour of the class, but its implementation.

Implementation code and test should be independent, in a sense an implementation must be able to be refactored while the test stays the same. There's no other way to reach the "red green refactor" mantra if you have to change your tests when you change the implementation. This is a sign of a very brittle test suite.

It's easy to spot this form of white box testing as opposed to black box, behavioural testing: it has method expectation, mocks, etc… with the idea of "covering every line of code because 100% coverage". A test should not be the implementation in reverse, but a description in terms of inputs and outputs.

Collapse
 
grahamcox82 profile image
Graham Cox

This idea works really well when the units being tested are self contained. It falls apart when the units being tested are part of a bigger whole.

As an example here. Say I'm writing a user management system. Part of this entails the ability to load users from the database. As such, I've got:

  • User DAO
  • User Retriever ** Implementation of this that works directly in terms of the DAO ** Implementation of this that does caching
  • User Controller

Each of these should be tested to ensure that they work. But the implementation of the User DAO will directly affect the implementation of the Dao User Retriever.

An Integration test of this as a whole - from the controller to the database - makes a lot of sense to do ahead of times. You know what your API inputs and outputs are going to be, because they are part of your design. You know that when you call "GET /users/unknown" then you expect an HTTP 404, and when you call "GET /users/graham" then you expect an HTTP 200, and a JSON document in a specific format. You can write all of these first.

Unit Tests for the individual classes can be written ahead of time, but in my experience I've always ended up changing them as I write the code because of refectorings and tidying up and things like this. Little details like how the DaoUserRetriever works might change how the UserDao itself needs to work, which will change the unit tests for the UserDao.

And regarding the comment about mocks - if you don't use mocks then either you are writing everything as pure functions, or else you aren't doing Unit Testing. When testing that the DaoUserRetriever works correctly, you have to provide some implementation of the UserDao. Either the real one - which means instantly it's not a Unit Test - or else a Mock one.