Real Life TDD With Spring – The Road to the First Test II

Gil Zilberfeld describes options for API design for TDD Spring applications
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. Which tests to write?
9. API design

Last time, we started thinking about considerations for picking the right test to start with. We decided to start with integration tests. So, which cases do we want to cover?

Obviously, they should capture the main functionality of the system. Going over our test cases, the following are the main capabilities of our system:

  • Pressing keys
  • Displaying results
  • Calculations

All the other cases are either relatively edge cases (error handling, overflows, etc. ) or more complex scenarios. While these are proper behaviors in our system, they can be covered by unit tests (so it seems right now). For our first set of integration tests we want the simpler stuff. Later we might consider adding more cases.

So we can choose the following cases for our integration tests:

  • Pressing keys and displaying results: “1”,”2″,”3″ => “123”
  • Calculations: “1”,”+”,”3″,”=” => 4

While these capture the main capabilities, I’d like to add the initialization case too:

  • Nothing => “0”

Why?

First, this could be important for building the system as a whole. Delivering an end-to-end story that displays “0” is both easy to do (looks like it, anyway) and valuable. It is good feedback for us that our system works.

Second, this is obviously a quick way to get to a working feature. If you’ve already thought a couple of steps ahead (and that’s not a bad thing to do, even with TDD), you’ve figured that passing the calculation tests would take a lot more work than the initialization test. A quick win is something we all can use. Plus, it looks like a very simple test, that will also drive the first API design.

What’s not to like?

Initial Analysis

Let’s think it through. We have a few core assumptions already:

  • We’ll be using TDD for the inner logic, which we didn’t design yet (obviously)
  • We’ll be using REST services as the API, with input and output as strings
  • Input will come in the form of “pressed keys”
  • Output will come out as the form of “displayed result”

If that’s all we go on, we can presume that we need an API that looks like this:

and its REST service wrapper. We press a key, then return what to display. Let’s call it the “pressKey” API for short.

For all other cases, we’ll need this API. However, with the initialization case, no key is being pressed. Hmm.

We have a couple of design options regarding our API suggestion.

  1. The UI will present the 0. The initialized system does not need to be asked what to display. Once the initial zero is displayed, we can use the “pressKey” API.
  2. Use the “pressKey” API, but give it another meaning. For example, use it like this in the beginning:

    or

    In both alternatives, the back-end will know to respond with “0”..
  3. We can split our “pressKey” API into two separate APIs, each in charge of a different operation.

This way, on initialization we just call the “getDisplay” API.

4. Or maybe, we need a separate API for initializing, in addition to the original “pressKey“.

We would call this in the beginning, telling the calculator we’re starting and we’re off. In this version the front-end will display the zero (as with option 1). Or we can use this  version:

Awkward a bit, why would “initialize” return a value?

Decisions, decisions. But you see, we’re starting to think about how we operate the system, as part of the use cases. We want to find the best alignment. The tests will follow.

I’ll give you my pick next time, but here’s your chance to suggest other options (with explanation) and name your favorite.

Real Life TDD With Spring – The Road to the First Test I

Gil Zilberfeld describes the thinking about selecting tests for TDD with Spring
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. Which tests to write?
9. API design

When we set up the project, we ran a couple of tests (integration and unit) to see if the environment we have runs them. There was no actual logic involved, but we got initial feedback that the platform is ready to work as-is.

The next logical step is to set up a couple of real tests. But what kind of tests should we start with, and how do they fit our TDD effort?

The right stuff

In our test analysis, we ran through a lot of cases, and some of them can serve as acceptance tests. As acceptance tests, they prove that the system works for the customer as specified. As such they would operate the UI and the entire system.

The main issue with testing through the UI is volatility – the UI tends to change a lot more than functionality. Which will cause us to change our tests frequently – a big effort, with relatively small value.

Another issue with testing through the UI is that we add another layer (or more) for the tests to pass through. Although they give as a “thumbs up” when they pass, we’re adding more pitfalls for the tests, to fail for the wrong reason.

It’s true that integration tests can fail because of everything in that black box we write. But our UI tests, can fail because of more network issues, security and configuration between the UI and the API.

What happens then?

Let’s talk for a minute about the cost of “maintaining the tests”. Although the test failure is the signal, what happens next is we start investigating everything – the tests, the code, the environment they ran, the build system, and other things as well. This takes time (sometimes a lot). If we find out the tests failed for “the right reason” – the code doesn’t do what we intended it to do – that’s good. They fulfilled their job.

If the tests failed for any other reason – tests are no longer correct, environment issues, missing files that were not added to source control – we call that “waste”. This is not just a waste of time – these failures erode our trust in the tests we have, and reduce our motivation to keep writing them. On top of the wasted investigation time, it sometimes results with homework: Fix the tests, add the file, re-configure the environment.

If maintenance cost is high in integration and system tests, why not just skip all those and just write unit tests? They wouldn’t fail for the wrong reason – they are isolated, they test very small scope, and they are cheap to write.

If we only wrote unit tests, that don’t check the whole system, we won’t have a proof that the system actually works. So as we write tests, selecting the right type of tests becomes a balancing act. The testing pyramid model describes the resulting set of tests.

Ok, so far we agree that we don’t want UI tests, and we’ll need to select API and unit tests. How is that related to TDD?

The Big, The Small and the Ugly

Test-first adds another dimension to the game. In order to use TDD to make progress quickly, we get pulled toward unit tests. Using TDD from the API level is possible, but we won’t move as quickly. We’ll need to build APIs and call them in the tests, add beans to configurations, setup dependencies, all  slower than writing tests for the code itself. In TDD we expect very short cycles of development. Every couple of minutes we have another passing tests. Going outside to the API level, depending on how complex the system is, the cycle can turn to hours (and sometimes days, I’ve seen it) between passing tests.

On the other hand, we still want those bigger tests. We’s want to set up a few API tests, that act as guardrails. They make sure that we’re on the right track, while we’re messing with internal things inside the system. Having these big scope tests can lead us in the right direction.

If we write them BDD-style, they can even tell the story in the domain language, not “ugly” REST API dialect. Remember those test cases? we wrote them in our own language, not using any technobabble. We can use BDD tools (e.g. Cucumber) for that. Or we can simply write tests in a readable manner.

Another advantage of having the bigger tests, is help us think about the APIs of the system. With Test-First (in any scale) we think about how we talk with the system. The integration tests can help us there as well.

If we had all the time in the world, we would cover all the code with all kinds of tests. But we don’t. So we need to choose which cases we want to use as the API tests, and which to leave as unit tests.

Let’s start off with a couple of API tests, then move to TDD at the unit level.

Real Life TDD With Spring – Initial Project Setup

Gil Zilberfeld describes the initial Spring project, tests and TDD setup
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. Which tests to write?
9. API design

Finally! Actual TDD and code and Spring!

Well, we’ll start with Spring first. But first a reminder.

We’ve already constrained ourselves with the architecture. This is going to be a web based, REST services based Spring application. In the general case, the architecture should support building the app in the most reliable, quick and cheap way. In our case, it’s because I want to improve my Spring skillz.

If you want to follow up, I’ll be posting the code on github. I’m going to have a single project, and tag each version of with the post’s name and date. For example, this post is tagged 1.0.

Generation S

The first thing I’ll do is create a project in Spring Boot. It’s the quickest way to set up a Spring based project these days (I used version SpringBoot 2.0).

What needs to go into our project setup? We’ll select a maven project with the following metadata:

Group: tddWithSpring
Artifact: calculatorDisplay

I’m selecting two dependencies for the generation of the project:

  • Web – Obviously, we’re building a web app.
  • JPA – For database access.
  • H2 – In memory database for our integration tests

JPA and H2 are not needed to begin with, since if you recall, we don’t have any persistency requirements yet. We can start without them and add them later, based on our needs. But since I’m running the show, we’ll add both dependencies now. (One of the other thing I did is added and commented out properties for those two in the application.properties file under resources).

Now, let’s press the “Generate Project” button, and… don’t you love technology? We’ve got a starter project. The regular Maven setup includes source files and tests.

But we still need to do some small modification and cleanups to the project before we start

Remove all clutter

First, let’s remove the mvnw files that were created as part of the project generation. We don’t need them. Let that be a lesson to you – not everything that can be generated is actually good for you. .

The generated project already contains a CalculatorDisplayApp class, and an empty CalculatorDisplayApplicationTests test class. Note that this class is for integration tests – you can see it annotated with @SpringBootTest and @RunWith(SpringTestRunner.class). I’ve renamed the class IntegrationTests for now.

Let’s run the integration test (which I’ve renamed also), the one that was generated:

The empty test passes, imagine that. But there’s a whole lot of logs going on while it’s running. These Spring logs maybe helpful (maybe), but for us, it’s just a distraction. Digging around StackOverflow, I found the solution to remove all logging below ERROR level (that’s unneeded DEBUG and INFO lines). It involves adding a logback-test.xml file to the test/java/resources folder. Each line tells the minimal logging for each package. For example:

That’s a lot less noise on the console. With those in, we’ve minimized the logging to something more acceptable.

Run, test, run!

The first thing I usually do to make sure a system runs is to fail a test and then pass it. I do it with the integration test (I’ve added  assertTrue(false)  and then change it to true). The test runs as expected.

However, running this single test clocks in between 11-15 seconds from the command line (using “mvn test“, not including compilation). Interestingly enough, running the same test from within Eclipse (“Run as JUnit Test“) takes around 5-7 seconds. (Times are on my laptop, YMMV).

That tells us a lot about the overhead of running the Spring framework, and also about Maven. The more complex our software gets, we’ll see this overhead grow, and that’s bad for our feedback cycle. We need integration tests, but for our TDD purposes, we’ll need quick running unit tests as well.

Here’s a thought: If I’m already in learning mode, why not use  JUnit 5? Sure, why not. Add the following to the POM file:

To check that JUnit works, I’ve added a regular test class (unsurprisingly called UnitTests), with a small test (same as before). This is a regular JUnit test, and no @RunWith annotation:

This test alone, runs in Eclipse in less than 50 micro-seconds. That’s a very good feedback time.

However, when we run it with maven (“mvn test“), which runs both integration and unit tests, we get again to 10-15 seconds. To run just the unit test, we can run Maven with a specific test class, just the UnitTests class:

Hmmm. This brings the time to around 5-7 seconds. Still not micro-feedback, but a lot better, without getting the whole Spring framework grinding us to a halt.

So, what have we learned?

  • We have a project ready for work, that was sort-of easy to set up.
  • We know our tests are running.
  • We know that integration tests that need Spring have a big overhead to do nothing. We also learned that running tests through Maven adds another overhead that we might want to work around.

Next up: Deciding which tests to write.

Code for this post is here.

Real Life TDD – Test Case Analysis, Part IV

Gil Zilberfeld talks about more tdd examples for the Spring microservice project with integration testing
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. Which tests to write?
9. API design

PM: You know, you could have something built by now. You do call this series “integration testing with Spring TDD” or something.

Me: Yeah, and I would probably build something you did not want. Just a few more cases. Remember, we were talking about actual calculations and special cases?

PM: Ok. Let’s take a step backwards. What happens when we do this?

“5”,”+”,”-“,”3″,”=”

Me: Hmmm. User error. She pressed “+”, then decided “-“. So clearly she changed her mind. “-” cancels “+”, and we’ll see “2”.

PM: If I had a penny for every time a developer said “Clearly”…

Me: Gotcha. So am I right or not?

PM: Let’s go with your intuition. That means that the following sequence behaves the same:

“5”,”+”,”C”,”-“,”3″,”=” => “2”

Because the user canceled the operation and entered a new one. We need to support that too.

Me: It could be. But if we only look at the beginning of the sequence:
“5”,”+”,”C”

Do we see “5”? Or “0”? Do we want to cancel the operation? Or the last number we entered (this time we didn’t start to write it yet). We may have a conflict with a former example.

PM: You’re starting to annoy me.
Me: The feeling is mutual.

PM: Ok. For consistency, “C” should cancel numbers. That means:

“5”,”+”,”C” => “0”
“5”,”+”,”C”,”-“,”3″,”=” => “-3”

Me: Are you sure?
PM: For now.

Good enough. But we’re not done yet. Simple calculator my foot.

The Calculator Runneth Over

Let’s talk calculation overflow. We’ve already discussed the display limits when entering data, and the answer was we can’t – we’ll stop entering new digits. But we can overflow also as a result of a calculation. What happens then?

PM: There’s nothing better than an “E” for error. Show me an “E”.

“9”,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”+”,”1″,”=” => “E”

ME: We didn’t have an “E” before. It opens the door to other questions, like how do you get out of “E” mode.

PM: Any key starts a new thing.

“9”,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”+”,”1″,”=”,”2″ => “2”
“9”,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”+”,”1″,”=”,”C” => “0”

Me: Hmm. And what about an operation key?

PM: Hmm. Then don’t change the display until a digit or a “C” is pressed. Operations stay in “E” mode, and we don’t start a new calculation until a digit is pressed.

“9”,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”9″,”+”,”1″,”=”,”+” => “E”

Right, overflow is done. How about our favorite error, division by zero?

PM: Lucky we’ve already got an “E” mode. Use the same behavior.

“8”,”/”,”0″,”=” => “E”

This is the end

Me: Almost there, a couple more clarifications please, for the cases right after we’ve completed a calculation. What happens when we reset after a calculation?

PM: Show “0”, clearly.

“1”,”+”,”2″,”=”,”C” => “0”

Me: Clearly. How about if we press a digit key?

PM: That’s a start of a new calculation. First we show the new digit:
“1”,”+”,”2″,”=”,”4″ => “4”

PM: Then, it’s a new calculation:
“1”,”+”,”2″,”=”,”4″,”*”,”3″,”=” => “12”

Me: What if the next key is an operation? Is that the continuation of the calculation?

PM: Let me think. Yes it is.

“1”,”+”,”2″,”*”,”4″,”=” => “9”

Me: So we’re keeping the order of operations (multiplications before addition)?

PM: Of course. This is like 1+(2*4).

Me: I feel a disturbance in the force. What does this result in?
“1”,”+”,”2″,”*”,”=”

PM: Well, that’s a 3. I think…
“1”,”+”,”2″,”*”,”=” => “3”

It makes sense. But then, if we look at this one again:
“1”,”+”,”2″,”*”,”4″,”=” => “9”

It’s a conflict – not of an implementation, but of expectations.

PM: Ok, let’s keep it simple for now. We won’t keep track of the order of operations. We do it as input comes in.

“1”,”+”,”2″,”*”,”4″,”=” => “12”

PM: Don’t worry. We’ll get the additional budget for that feature yet. But for now, I think we’ve covered all the functionality. I think. I want to  believe. Did we?

Me: Yup. For now. Funny, it didn’t look like we needed so many examples to figure out a simple calculator.

It Is Nigh

If you’ve made it so far, you’re probably thinking – should I really know all these things prior to writing a single bit of code? Should I analyze all the possible cases before I even fire up Eclipse?

My answer is: You don’t. If you promise to come up with all these cases as you go.

The thing is, there are probably cases I’ve missed here. You’re going to miss some as well. And assuming you do, those cases will either not be coded or tested. That means that the system gets released that way, and in many cases, that’s a risk, sometimes a big one.

We usually don’t wait for the analysis to be over, and sometimes we don’t even wait for it to start. We’re eager to “create” although we still don’t know what we need to develop. That means we’re programming based on a lot of assumptions, some of them are wrong. Based on these assumptions, we’ll write some code that may contain bugs. And that is not so easy to maintain once written.

So, in a nutshell, yes, you do have to think those through.

Right. Enough planning. Time to set up a Spring project.

Real Life TDD – Test Case Analysis, Part III

Gil Zilberfeld continues exploring test cases for the Spring based calculator application
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. 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.

Real Life TDD – Test Case Analysis, Part II

Gil Zilberfeld continues to discuss TDD test cases for Spring's API testing
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. Which tests to write?
9. API design

So many TDD cases, so many posts. And no Spring in sight yet. I promise, we’ll get there. For now, let’s continue to explore the requirements. Where did we last stop?

  • Pressing any digit key, the display adds that digit to the existing display. The exception is ‘0’ which is presented only if a number different from ‘0’ wasn’t already pressed.

What’s the deal with the zero key behaving differently? An example for regular digits looks like this:

“4”,”5″ => “45”

But for zeros it’s different:

“0”,”5″ => “5”

Of course, we still need to remember that a zero still behaves as a regular digit after another was pressed:

“5”,”0″ => “50”

And let’s not forget the ever shifty:

“0”,”0″=>”0

Did you notice something? We’re collecting lots of examples, and none have yet to do any calculation. That display sure is tricky.

What’s the next requirement?

  • Pressing an operation key does not change the display.

What does that mean? An example,

“6”,”+” => “6”
“7”,”8″,”/” => “78”

However, when we add a number after the operation key, we’ll see new that number:

“6”,”+”,”1″ => “1”
“7”,”8″,”/”,”3″ => “3”

That makes sense. We’re starting to input a new number after the operation. Funnily enough, if we key two operations sequentially, the display works the same:

“6”,”+”,”-” => “6”

We don’t yet know what that means in terms of calculation, but that’s ok. We’ll explore that later.
A more complex set still yields the same behavior:

“6”,”+”,”4″,”-” => “4”

Time for a bit of semantic talk. Is “=” an operation key? We don’t expect the same behavior after doing the calculation. However, it’s a non-digit key, so how should we call it?

Product Manager: No, it’s not an operation key. Operations are “+”,”-“,”*” and “/”. Others are not operation keys, although, we don’t have a special name for their group.

Ok, we don’t have long to go. Before calculations.

  • Pressing “C” resets the calculator

That’s easy enough:

“1”, “C” => “0”

Wait. Is that still true after an operation?

PM: Yes.

Ok then.
“1”, “/”, “C” => “0”

Now, how do we know that the calculator is really reset? It may show me zeros, but is the last number still affecting calculation? That would need to be put into the calculation examples.

Which are coming up next.

All this, and the calculator is not helpful yet. Imagine that.

Real Life TDD – Test Case Analysis, Part I

Gil Zilberfeld explains TDD test case analysis and planning
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. Which tests to write?
9. API design

It is customary to think that in TDD we run ahead and just write tests. Not today, Spring will have to wait.

Let’s break down the requirement into test cases, so we can understand them better. Buckle your seat belts, it’s going to be a bumpy ride.

We’ll translate the requirements into “when-then” situations. Here’s the first one:

  • When the calculator starts it displays “0”.

That’s easy. No pressing of keys shows zero. We’ll write examples so many times, so let’s use shorthand for the input to the output.

Nothing => “0”

Let’s continue.

  • The calculator has a keypad with digits (0-9), operations (+,-,*,/), a C clear button and an ‘=‘ button to execute the calculation.

We all know calculators (hopefully) and how they work. We instinctively understand that the specified keys are clickable, while all other keys are not. However we can’t translate this requirement into an example. That’s ok, we can for the next one.

  • Any other key pressed doesn’t affect the display

So for example:

“E” => No change

Wait a minute. There’s no “E” on the keypad. What kind of requirement is this?
A confusing one. Check out how it makes sense when we read the requirements the first time, but when we read it again it’s not helpful.

Maybe because “key” is something we press on the computer’s keyboard, not necessarily the keys on the screen.

Or maybe, we’ve already translated it in our mind into “service” language. API services can accept other keys, but don’t necessarily do anything with them. Hmm… Let’s ask the product manager.

PM: Let’s make it easy – we can accept only the keys available in the GUI. No need to translate it into code, error handling or anything. We can throw that example away.

Next.

  • Pressing any digit key, the display adds that digit to the existing display. The exception is ‘0’ which is presented only if a number different from ‘0’ wasn’t already pressed.

Ok. That’s easy enough. Let’s start with the obvious.

“1” => “1”
“2” => “2”

Wait. Do we need all nine examples? After all, these examples translate later into tests, so do we need to write nine tests?

We probably don’t. Examples may look different from each other, but the code would probably be the same for all of them. We don’t know it yet (that’s TDD for you), but knowing the domain and our coding skills, it’s ok for now to have one example of the digits.

Also, ours is a very simple calculator, not a nuclear facility management calculator. If there’s a bug in our calculator (for example: “8” => “k”), our calculator may now work, but it also won’t cause a Chernobyl-style meltdown. If there are high risks, I may want to add the test cases up-front to make sure they work.

Ok, that’s one digit. Let’s do two:

“3”,”4″ => “34”

Here’s another thing: All things being equal, I could choose the following:
“1”,”1″ => “11”

Again, while we didn’t program anything yet, we feel that these examples are equivalent in terms of how the code will behave. But by using more digits in my examples, I’m reducing the risks that the code will work for only keypads with, let’s say, one key. Also, consider that examples are a form of documentation: they explain the capabilities of our calculator even better than the code itself. With more diversity we understand it better.

We might want to add a 3 digit example as well:

“4”,”6″,”8″ => “468”

When should we stop? Again a question to the PM: How big is the display?

PM: (Thinking) Our budget is enough for 10 digits.

Ok, we want an example for the edge case:

“1”,”2″,”3″,”4″,”5″,”6″,”7″,”8″,”9″,”0″ => “1234567890”

I know what your thinking. What happens on the next key?

PM: (Thinking again) the display doesn’t change.

So the example would be:
“1”,”2″,”3″,”4″,”5″,”6″,”7″,”8″,”9″,”0″,”1″ => “1234567890”

Ok, maybe the display doesn’t change, but how about the values? is the last case really “1234567890”, or is it “12345678901” in disguise?

PM: Nope, what you see is what you get.

How do we put that into an example? For the time being we haven’t delved into actual calculation, but let’s do that now for this case:

“1”,”2″,”3″,”4″,”5″,”6″,”7″,”8″,”9″,”0″,”1″,”+”,”1″,”=” => “1234567891”

Now we can expect the calculator understand the values correctly.

Did you really think you’ll see Spring, or even some code by now? You need patience young padawan. We’re just starting out, next time: Test Case Analysis – The Sequel.

Real World TDD with Spring – The Requirements

Gil Zilberfeld talks about the calculator requirements for the Spring TDD project
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. Which tests to write?
9. API design

Most TDD examples start out with a calculator. Ours will start here as well. But we’re not going to write a test for “2+2”. No sir, we’re going to test drive the calculator display.

But what does it actually do?

We’re going to create a simple calculator as a web app. We’re going to concentrate on the backend (although we might get to the other end in the future). It’s going to use REST APIs to access a backend where all the logic will be. While regulator calculators focus on what happens when we press “=”, for us the main focus is what happens on the screen as we press the keys. There’s some logic in there as well.

We’re already making a couple of technical decisions – we’re using Java, Spring (and I’ll use Spring Boot for creating the main project), we’ll be using REST for the delivery protocol. Also, everything interesting is done at the backend. The UI is just displaying the results of the backend calculations.

We’ve skipped over any discussion about whether any of these decisions is the right one. In the real world, we’ll have lots of meetings around them. Plus, look at who are the competitors for our app and its value, and consider whether to build it, or what makes it special. But we’re above that.

For our purposes these architecture decisions are not negotiable. Within these constraints, we’ll do what we can.

Ok, let’s look at the requirements:

  • The calculator has a display area, which is updated as the keys are pressed.
  • The calculator has a keypad with digits (0-9), operations (+,-,*,/), a C clear button and an ‘=‘ button to execute the calculation.
  • Any other key pressed doesn’t affect the display.
  • When the calculator starts it displays “0”.
  • Pressing any digit, the display adds that digit to existing display. The exception is ‘0’ which is presented only if a number different than ‘0’ wasn’t already pressed.
  • Pressing an operation key does not change the display.
  • Pressing “C” resets the calculator.
  • Pressing “=” shows the calculation result. There are no intermittent results shown until it is pressed.

You know, something like this:

Gil Zilberfeld describes the calculator web app in TDD

By the way, this is not one of the fancy calculators that shows you interim results even before you press “=”. Only a press on “=” shows the result. I am going to add another requirement to make it interesting (as if it wasn’t already).

  • The input and output to the logic at the backend are strings.

I know, we’re going to corner the market with this one.

Now most people at this point do the following:

  1. Say they understand the requirements
  2. Create an empty Spring Boot project in Eclipse
  3. Code very fast

But we’re not most people. We’re going to wait on that, until we actually understand the application in terms of test cases. Because that’s how we roll.

PS: That’s really how we roll. Not just in examples.

Even in “regular” TDD, we do some test planning. We don’t just rush over and write the first test.

Understanding the test cases allows us to clear things up, understand the requirement better and ask questions from our majestic product manager (that’s me also). Even negotiate  the requirement if we’re crafty enough.

That’s the next step. Requirement analysis through test cases. If if you think that’s simple, you maybe surprised.

 

Real World TDD With Spring – Introduction

Gil Zilberfeld talks about real world TDD (test driven development) on the Spring framework on microservices
Standard
How does TDD look like in real world development of microservices or web interfaces? That's what this series is all about.
1. Introduction2. The requirements3. Test case analysis I4. Test case analysis II
Test case analysis III6. Test case analysis IV7. Setting up a project with Spring Boot8. Which tests to write?
9. API design

I’ve been working with teams who use Spring as the basis of their programming for some time now. Spring, many times as a microservices development and runtime framework. Luckily (or not) they want to incorporate TDD (Test driven development) into their development, but they bump into all kinds of problems.

I’ve talked about how architecture decisions usually have an impact on our unit testing strategy, and of course the process of implementation. With TDD things get even uglier, because of the underlying framework (which brings many other benefits) hamper the main benefits of TDD.

Here’s a couple of issues:

  • The kick-off phase is slow. If you want to just sit down and start coding, you need to set up the whole framework first. That’s a one time (hopefully) hassle, but a hassle nonetheless.
  • The tests seem different than “regular” TDD tests. They are more technical, because they operate an REST APIs, for example. It’s not “our” design, it’s Spring’s design.
  • The time running the tests is long, from a few seconds to minutes just to run a single test.
  • The time until we have passing test is long. You need to write code, inject it, configure it, connect to the database, and that’s just for a CRUD operation.
  • Once the code is there, and we’ve added functionality, we tend to spread the logic in multiple places, because the architecture leads us there. Therefore, the “design” part of TDD loses it’s effectiveness.

And so we “just” add tests (some of them are valuable), but our tests don’t drive the design, they take long to write, then take long to run, and we don’t get the benefit from the ever increasing cycle of feedback.

It’s not that Spring (or any framework we choose in any language) is bad. However, if we let Spring’s architecture drive the design, it is an uphill battle. TDD – meet your kryptonite.

I’ve started thinking about this series trying to look up examples on TDD with Spring, and coming up with the code and coding patterns I mentioned. And while it maybe difficult, I think we can still do better.

So here’s what we’ll do. We’ll create an application, built on REST APIs, and we’ll do it in TDD. Although we’re starting out with a selected architecture, we’ll try to not let it hurt our TDD effort.

We’re going through all the stages of development from requirements analysis, through architecture design, the actual development of the functionality, testing, and refactoring. Spoiler: It’s going to be a while before we’ll actually see Spring in action.

Plus, as we go on, we’ll talk about how we tackle testing of the capabilities that we add, like persistency, supporting multiple users, security, etc. Some of the things will be easy, but a lot may seem foreign to the existing examples and documentation.

That’s ok. We want to get better.

Up next: The requirements.