How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about. | |||
---|---|---|---|
1. Introduction | 2. The requirements | 3. Test case analysis I | 4. Test case analysis II |
Test case analysis III | 6. Test case analysis IV | 7. Setting up a project with Spring Boot | 8. Which tests to write? |
9. API design |
Product Manager: Man, you still haven’t touched your keyboard? I haven’t seen you doing any Spring or TDD or anything technical yet. Are we going anywhere with this?
Me: Of course! It’s calculation time! That’s what you wanted, right? Let’s write some examples.
- Pressing “=” shows the calculation result. There are no intermittent results shown until it is pressed.
First let’s address the final part – we only have one display. It behaves according to all our examples above, and shows the result in that display. It’s one of those “illuminating” requirements that don’t add anything to our implementation.
Now, let’s start talking calculations. Since we’re not building a “calculator” from scratch, meaning we’re going to use regular computer’s “+”,”-“,”*” and “/” operations, we don’t need to check that the calculation is correct (e.g. 2 plus 2 is 4), but that the result is displayed correctly. This can be tricky in some cases. But let’s start with the plain vanilla things:
Regular operation calculation. Should we have something like this?
“2”,”+”,”3″,”=” => “5”
Yes, we probably should. But how many examples like this do we actually need? Numbers are infinite, we have four operations, and frankly, it’s not us that doing the calculations. We’d probably want one for each operation.
We haven’t discussed so far which tests we want to be unit, and which are going to be integration or end-to-end. But this looks more like a main flow of the calculator operation, so we’d want that kind of test to be wider than the regular unit. We still don’t know about whether it’s instead or in addition to the smaller tests. That’s ok for now.
Tonight’s Specials
Now for some special cases. Do we display negative numbers? We don’t have a way to enter a “-3”, but should we show it as a result?
PM: That’s true, let’s discuss the two cases separately. First, a negative result – yes, I want to show it.
“3”,”-“,”5″,”=” => “-2”
PM: Now, about entering “-2”. We currently don’t have the budget for a “-” button. The users will have to make do with that behavior until v2. (Or in other words, that can be left as an exercise for the student).
PM: What other special cases do you have for me?
Me: Well, similar to the negative numbers, we can talk about division results. We don’t have a “.” key. What do we show and how can the user calculate with floating points?
PM: Well the second part is easy. Since we don’t have budget for a “-” key, do you think we kept some mystery budget for the “.” key? No, we didn’t. That’s V3.
Me: But what about results?
PM: Well, no floating point results. We’ll show rounded results.
Me: You mean like this?
“1”,”0″,”/”,”3″ => “3” – Rounding down
“1”,”1″,”/”,”3″ => “4” – Rounding up
“5”,”/”,”2″,”=” => “3” – Rounding the middle
That last two seem a bit strange. Do you really want that behavior? Because the plain div operation doesn’t round. It truncates, rounding down and then remove the non-integer part.
PM: Ok, I’ve learned a new word. Why do you have to have a separate word for everything? We don’t want weird behavior, we’ll truncate.
“1”,”0″,”/”,”3″ => “3” – Rounding down
“1”,”1″,”/”,”3″ => “3” – Rounding up
“5”,”/”,”2″,”=” => “2” – Rounding the middle
PM: I’m happier now. Can we move on? I’ve got a lunch meeting.
Me: Nope, we’re not done yet. We’ve got a few more cases we’d like to put in example form.
Calculations with zero, but without pressing zero, for example. We know that:
“0”,”+”,”1″,”=” => “1”
But should this work after initializing?
“+”,”1″,”=” => “1”
PM: Yes.
Me: And after reset?
“1”,”C”,”+”,”1″,”=” => “1”
PM: Yes.
Me: And if “C” is pressed after an operation?
“1”,”+”,”1″,”C”,”=” => “1”
“1”,”+”,”1″,”2″,”C”,”=” => “1”
“1”,”+”,”C”,””=” => “0”
PM: Whoa. Hold your horses and cancel that lunch.
That’s becoming weird again. In the first example, “C” means “cancel the last digit”. In the second one, it means “cancel the whole second number”. And in the last one it resets the whole calculation.
Me: What does “C” really mean? And can we call it the Reset key? And if so, why does it called “C”?
PM: You have too many questions. Let’s make it simple. “C” cancels everything since the last operation. If it’s pressed after the calculation it resets the current number.
Let’s revisit the last examples and add a couple:
“1”,”C”,”+”,”1″,”=” => “1” – Cancels the first number
“1”,”2″,”C”,”+”,”1″,”=” => “1” – Cancels the first number
“1”,”+”,”1″,”C”,”=” => “1” – Cancels the last number
“1”,”+”,”1″,”2″,”C”,”=” => “1” – Cancels the last number
“1”,”+”,”1″,”=”,”C” => “0” – Resets the display
Me: Riddle me this: what do you expect if we do this:
“5”,”+”,”C”,”-“,”3″,”=”
PM: What do you mean?
Me: Exactly. What does “C” mean after an operation? Does it cancel the operation? If it does, at the end I’ll see “2”. If however, “C” resets the display, we’ll get “-3”.
PM: Where do you come up with all these cases? No user would do this!
Me: If I had a penny for every time a PM said “No user would do this!”…
And for more things a user wouldn’t do, wait for the final part of test case analysis, coming soon.
4 Comments
Aaron Evans · March 28, 2018 at 5:30 pm
Great post!
It’s a good example of finding edge cases and demonstrating the complexity in even the simplest of applications, and the need for planning, it’s a bad example of TDD or Agile development in general.
I sympathize with the Product Manager. I want to see 1+1=2 shipped (working code!) before I start worrying about edge cases like negative numbers resulting from canceled operations. Because in practice, you only find these edge cases when you go to implement them — or, more likely — when a user defies the dictum “No user would do this!”
Gil Zilberfeld · March 29, 2018 at 9:34 am
Thanks Aaron!
Got a few more “No user would do that” coming up 🙂
I have issues (for a while now) with terms like “edge cases”, “negative cases”, and other categorizations of cases. I think I need a separate post about it. Regardless, any of the cases, edge or not, can be discovered in any point of development. The earlier is better, because we can decide what to do about them, and more important: how they fit into the rest of the requirements – if it’s aligned with them, conflicting, more or less important, more or less will be used, etc. The earlier we understand the bigger picture, we can develop a better product effectively.
I promise to get to the TDD part, we’ll spend a whole lot of time on it. I do try to ask the questions, write example, and understand better before I write the first test. What I’m working through in this series is what works for me.
David V. Corbin · March 28, 2018 at 5:32 pm
Great Post… but…
IF “+”,”1″,”=” => “1” works, why not “-”,”1″,”=” => “-1” ???
Seems like further development of test cases invalided a previous assumption [which is actually a good thing”
Gil Zilberfeld · March 29, 2018 at 9:35 am
Thanks David!
And this is why we do requirement analysis before writing code – we find problems earlier!
I will add that example to the next post, it’s something I missed.