|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|
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
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”
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?
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:
String pressKey(String key);
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.
- 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.
- Use the “pressKey” API, but give it another meaning. For example, use it like this in the beginning:
1String display = pressKey(null); // passing null means "I'm starting, send me a zero"
1String display = pressKey("C"); // passing "C" is a reset operation (from the requirements)
In both alternatives, the back-end will know to respond with “0”..
- We can split our “pressKey” API into two separate APIs, each in charge of a different operation.
void pressKey(String key);
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.