Makes sense here. We can assume the Logger is a separate sub-system with it's own set of specs, so since we already know its behaviour, there's no point using the real object in a separate set of specs (i.e. if it fails in the Logger specs, it will fail here - and we get domino effect failures).Weirdan wrote:Maugrim, could we spec out the logging behaviour of the model class as well? (something along the lines of):
I'd say 4 behaviours:1) Verify email is not empty and is valid
2) Verify email address is not already taken - duplicates
3) The actual creation of the record in which case a valid PKID is returned
- should (something) when email address is empty
- should (something) when email address form is invalid
- should (something) when an email address already exists in system
- should create record of valid email address and return PKID
Trick is in being specific. If it's a consolation, I reword specs on average twice - simply because one spec can actually become two when you really drill them down, or even more. I also reword when the spec doesn't make enough sense if read. For example let's say you state "email address is invalid". Looks great but for one problem - what's invalid mean? Really mean? Could be 1) email address is empty, 2) email address form/syntax is invalid, 3) email address already exists in database, 4) email address does not exist (no MX record). Invalid is simply not specific enough - it could mean 4 or more different things. Since you're learning - in general, "invalid" is like a code smell. If you see that word ask yourself what criteria makes something invalid, and split the spec accordingly.
Not all will apply (you might not have MX checking at all - not unusual since an email confirmation works too), but by drilling into specifics you'll get a better set of specifications, and have split the problem into even smaller bitesize chunks to implement.
Now here's where the 1:N kicks in for methods - it's likely the 4 specs relate to 1 Model method (which checks all 4 possibles internally) so you do get a 1:4 ratio between class method, and specs. You'd get 1:1 if you weren't specific and put everything into one spec method with multiple checks (e.g. 4 assert* calls in a test, or 4 spec(*)->should->* calls per spec method) or even if the lack of specifics caused you to simple miss one possible completely (i.e. of 4 specific scenarios, a test/spec only captures 2, with the others never addressed).
I'd say they all belong in the same class/Context but split into 4 spec methods. Each one showing an example of how each of the 4 specs come about. The plus side here of course is that if one fails, you'll get the exact spec that failed - not a general pointer to a method checking all 4 which can be confusing. The other plus is that being specific you'll notice your implementation process just got a lot easier - you're implementing in smaller steps than if you'd lumped too much into the pre-written spec - less scope for interpretation makes implementation straight forward and less likely to create uncertainty. Uncertainty is a major pain in TDD/BDD - if you write a spec/test and need to think about what you're implementing (not the how - which does require thinking
As usual, once you can split into 4 specs, the spec methods are easy - just copy each spec into the Method Name for the spec method and you have a skeleton class to work with.