Page 2 of 2

Posted: Wed Dec 26, 2007 8:10 am
by Maugrim_The_Reaper
Weirdan wrote:Maugrim, could we spec out the logging behaviour of the model class as well? (something along the lines of):
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).
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
I'd say 4 behaviours:

- 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 ;)) it means your spec/test is so non-specific you still don't know what the spec/test was even for!

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.

Posted: Wed Dec 26, 2007 4:05 pm
by alex.barylski
Edit: Actually, after some more tinkering...I realize now that testing for emptiness and format invalidity are good ideas...I have still removed the empty check from my production code as it's already handled by invalidity and the user is notified that empty = invalid, bad format = invalid. However in the context of the test, I can see it making sense to keep that specification, incase the validity check ever changes and somehow accepts empty as being valid...

Touche!
Maugrim_The_Reaper wrote:I'd say 4 behaviours:

- 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
Originally I had four, but the first two can actually be joined into a single behavior, because if the email is empty it is already invalid...so I dropped the empty check and just let the invalidity test handle that.

Although I can see why you would want to drill down into specifics...
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.
Thanks for confirming that...this is what I suspected.