At Ingage Partners, we believe that great software is built by great teams , and great teams never stop learning.
That belief drives everything we do, from how we work with clients to how we grow our own people. One of the ways we live that out is through C3: Code, Craft, Community - a monthly meetup we host that brings together developers from all levels and backgrounds to sharpen their skills, pair program, and explore the deeper questions behind great software development.
We don’t just talk about learning. We practice it, publicly and together.
July’s Meetup: Designing for Testability
At our July 16th session, we continued to use the tools and techniques we have practiced the last few months to apply it to the messiness of a real world problem. What good is it to learn the academics of Test Driven Development (TDD) and still struggle to implement it into your daily work on real world code? In the real world we need to determine when to mock dependencies, how to organize our code into logical and testable classes / modules / code blocks, and how to tie all of the pieces together to do something useful.
The Kata: Scheduling with Edge Cases
This month’s Kata was based on a project that I had worked on years ago that needed to examine a shop’s appointment calendar and determine what time blocks were free for new appointments. While on its face this problem sounds fairly straightforward there are quite a few edge cases that may catch you by surprise. Let’s say you have an appointment you think will take 4 hours of work to complete. Three of those hours could be at the end of the workday, and the remaining one hour could be the start of the next work day. Furthermore maybe the 3 hours is on a Friday, and with the shop being closed on Saturdays and Sundays the hour is completed on the following Monday morning. All these edge cases scream to me as a great place to add unit tests. In the project that inspired the kata we had over 25 unit test cases on this single class.
Decoupling Logic from Dependencies
Now suppose that we wrote all of that logic interspersed with getting the appointments from an API and saving a specific appointment when the customer selected the best time. In order to unit test we would need to mock these calls to the API in order to test our somewhat complicated business logic above. This can certainly be unit tested, but maybe there is a better way.
In one of our recent meetings it was suggested that some code is easily testable. For example, in testing an Add function it’s easy to assert that Add(2, 2) will always equal 4. It’s simple to test because it’s a pure function. The output is determined only by the functions inputs. It doesn’t have any complicated dependencies. It does not need a connection to an API, DB, or some other remote resource that can sometimes fail. It does not change the state of the system or have any other observable side-effects.
Applying Single Responsibility
Could we make use of this observation to structure our code to make it more easily testable and better organized? I believe we could extract the complicated business logic into a class, AppointmentBlockCalculator, whose responsibility is to calculate the possible time blocks that are still open. It’s not responsible for calling the API to get the existing appointments, nor for saving the appointments. In addition to trying to make this a pure function we are applying the Single Responsibility Principal which is the S from SOLID principals. If we are successful in keeping our function pure we will not require mocking anything to test the most complicated code. It’s slightly more complicated than the Add(2,2) example, but can be tested with rudimentary knowledge of TDD.
Organizing the System for Testability
The system will still need to be able to call the API to load appointments and save appointments. Applying Single Responsibility Principal again, what if we put that code in an AppointmentRepository class that deals with all of those details of calling the REST based API. Unit testing the repository would likely require mocking the HTTP calls. We would still have focused test cases. The successful result would be straightforward and not have complicated branches of logic to test. A majority of the unit tests would need to test the failure cases such as when the network is down.
We will need one more class AppointmentService as the glue between the data AppointmentRepository and the AppointmentBlockCalculator that delegates most of the work to these classes. In order to unit test we would mock both of these dependencies.
Learning in Action
After a brief explanation of pure functions, dependencies, and Single Responsibility Principal we broke into pairs and worked on implementing an appointment block finder for vehicles. The requirements and hints can be found in our GitHub repo.
We came together to learn from each other. We can’t predict what people will learn, but challenge everyone to learn at least one new thing. Here are a few things our community members learned as part of this Kata.
- What makes a function a pure function
- How we can apply Single Responsibility Principal to write maintainable code
- When we need to be able to mock dependencies and when we might be able to avoid dependencies altogether
- One team used the https://devdocs.io/ to review documentation
- How to call and explore REST API’s
- This kata can be worked in almost any order. Some teams started exploring the API, while others focused on writing and testing the AppointmentBlockCalculator.
Why It Matters
This is more than a meetup. It’s a reflection of who we are.
At Ingage, we invest in growth—not just within our company, but in the broader Cincinnati tech community. We know that building better developers builds better teams, and better teams create better outcomes for our clients.
Come Code With Us
If you’re a developer in the Cincinnati area, we’d love to see you at our next C3 meetup. We meet monthly, on the third Wednesday of the month, at the Ingage Partners office (2943 Riverside Drive), and we welcome developers of all backgrounds, experience levels, and technologies & programming languages. Come to write code, pair up, ask questions, and eat some pizza.
Because when we share knowledge, everyone levels up.