While practicing TDD, I came across this specification:
A user should be created with its password hashed, be persisted and an activation token should be returned.
Should my first unit test, (aiming to begin to test this scenario) should encompass the whole needs? :
shouldPersistTheNewlyCreatedUserWithAHasedPasswordAndReturnACorrespondingActivationToken
Or rather, splitting its main parts into several smaller tests, and testing incrementally:
shouldCreateAUser
shouldReturnAUserActivationToken
shouldReturnAnActivationTokenBasedOnNewlyCreatedUserEmail
shouldPersistTheNewlyCreatedUser
shouldHashTheUserPersistBeforePersistingIt
To put in a nutshell, should I rework the same test until a whole scenario passes? Or should I proceed incrementally by specifying additional features in other small tests.
IMHO, the benefit of the first solution is that the spec is defining in one place, no need to gather some chuncks of features to figure out the whole.
On the other hand, the number of assertions and expectations might be huge, therefore the test code would be less readable.
0
tl;dr: Second option. You should split into several smaller tests, as that is what you’re supposed to do with TDD (Red-Green-Refactor).
You start writing the simplest tests first to start out with the simplest implementation and build your way up towards a full scenario. The semantics with unit tests is totally different with scenario/functional/acceptance testing. Developers tend to confuse them all as the same but they’re semantically different and fill different purposes.
TDD is built on the concept where a test has the granularity of a unit. Usually this unit is down to a method in a class. The whole point with TDD is to design code to be testable. If you start with a whole scenario as a unit and not the actual methods themselves you’ll end up with code that may be doing too much.
There are different ways to go about with your scenario. I would roughly start by trying the following:
- First write a
UserService
that can create a new user with a single testcreateUserShouldReturnUser
. - Then write a
IActivationTokenFactory
interface that creates activation tokens. OnUserService
I’d continue with a test that uses the token factory as a mock object. The test iscreateUserShouldRequestActivationToken
that requests any activation token from the mock object and puts it on the user object that is returned. - I proceed to write a concrete
ActivationTokenFactory
that implementsIActivationTokenFactory
. The test is to make sure it produces “legit” tokens. - and so on… I can imagine some kind of
HashFactory
object and some kind of persistance layer would be created as well.
Rinse, repeat until I have a full scenario and refactor along the way. I’d write the scenario test by now that exercises all the code, and make sure I have some unhappy paths to test as well (with failed persistance exceptions and whatnot).
It’s easier to implement something if you divide and conquer… which is what you’re supposed to do with TDD. Once you hit a snag, it’s good to have these tiny unit tests for regressions if you decide to refactor something.
2