(If you haven’t read or completed part 1 yet, click here: https://agileforall.com/the-password-strength-checker-design-kata-part-1/ )
How often did you have to go back to an existing test and change the password string?
No big deal, right? It wasn’t that hard. Recall, however, that this is a microcosm: That need to go back and revisit existing tests is a “smell” that contains important information. It’s information about how you’re thinking about the tests, and how your design is forcing you into making those changes.
For starters, these “tests” that we’re writing aren’t really tests until they pass the first time. Presumably, they’ll never fail again. So why bother? Because they’re also tiny parts of an overall engineering specification. They define what the system does, but in very small increments.
Test-first is a mindset where we decide what inputs give what outputs, before we decide how we want that to happen.
When one test forces you to go back and change another test, think of that as a form of coupling. Perhaps it’s “test coupling” or maybe really more like “idea coupling.” There are two separate notions that are getting mixed up in a single (albeit tiny) test. What are they?
I’m going to wait until part 3 to talk about that. For now, I’ll suggest this truism: If it’s difficult to test something, that’s not because testing is difficult.
Let’s move on.
Do your tests clearly describe each behavior, distinct from the others? Do you have any tests that would pass if the password being checked was not weak in the way the test was meant to exercise?
For example (in pseudocode):
it "says the password is weak if it's too short" expect(checker.isStrong("AAAA")).toBe(false)
Yes, the password is too short. But it also breaks other rules we saw in part 1.
SPOILER ALERT for Part 1!
I promised I’d tell you why I had to add a part 2. I delivered the lab as you’ve seen it, so far, for a few years, and then someone solved it with a single regular expression.
Which is fine, except that not everyone on their team understood regular expressions, and this particular regular expression was exceptionally complicated. Oh, it wasn’t huge, but if you have to look at it for longer than a second to figure out what it does, it’s too complicated.
I like code that shouts its intent. Regular expressions often whisper in cryptic riddles.
I refer to regular expressions as the “habanero pepper” of code: absolutely delicious in small amounts. Too much overall, and the dish is too HOT. Not chopped finely enough, and some bites will be TOO DURN HOT.
So, that day, I spontaneously invented “part 2” for this lab. Not that those devs had done anything wrong, but that they had managed to circumvent the whole point of my carefully crafted lab! And, yes, that means that part 2 is a bit contrived.
The Password-Strength Checker – Part 2
1. The client needs enhancements to your existing API, but they are hoping the new v2 API will remain backward-compatible with sites they’ve already built using v1.
2. Some clients want a way to obtain a list of all the reasons why the password is not strong enough.
Ever been asked by a site for a strong password, and you’re told “No! It has to be 10 or more characters long!” Then, when you make it longer, you’re told it has to contain something else? Those sites drive me crazy. I once had a banking site tell me that my password was too LONG! (I changed banks.) I also had a site tell me that I couldn’t have the same three characters in succession!!! 😉
What the consumer of your API wants here is some way to obtain a list of all the reasons the password is not strong, so the user can fix them all at once.
3. Some clients want to be able to pass a Boolean “Admin” flag to the API and, if true, the password must also…
- Be > 10 characters long.
- Contain a special character.
- Have a special character or digit as the last character.
Once you are done enhancing your API, please answer the following questions for yourself. (I recommend you write your answers down someplace where you won’t lose them in the next four weeks.)
1. What did you do to remain backward-compatible with the previous version? What other options did you consider, or will consider next time something like this occurs?
2. Would the use of test-doubles (e.g., mocks) help test all the code more easily? How so, or why not? Would your design be significantly different with/without test-doubles?
3. If your API survives in the wild, what further requests would be easy to add (using TDD, of course!)? What requests are most likely to happen, based on the API’s (rather short) history?
Lastly, draw a UML diagram, or simple circles and arrows, to describe your resulting v2.0 design.
Again, I hope you enjoy this lab. Next month, we’ll unpack all this in part 3.
Keep learning with Rob Myers through private virtual training in the following courses: