Typescript and Test Driven Design don’t fix everything, but…
But they make you think differently about the issues at hand.
For a long time, I thought that I did not have the time to write tests or that I’ll simply do it later. It took me a few years to see how my thinking was completely backwards.
Let me quickly draw you a picture…
I’m a slow adapter
When I started using react and redux in tandem, I had a look at typescript and decided to stick with the jsx variant for the time being. And don’t even get me started on test-driven design…
What has changed?
For once, it wasn’t that I was totally against those libraries, languages and workflows. I just don’t jump the wagon on the first mention of a new thing on the developers’ horizon, an attitude that kept me save from framework fatigue during the recent years.
Another reason is reality. If you are not a student, working for one of the few big IT giants or do your own thing, it is very likely for you to be locked in with some legacy code base, monolithic projects or tight deadlines.
I was already comfortable with react and redux by the time I started working at Das Büro am Draht, a few years ago. We sat together as a team and later in one of our workgroups and discussed the merits of working with typescript. Management gave us time to both learn and practice working with typescript and agreed on slower deliveries while we started climbing the learning curve until we were comfortable using it for all new development.
Test-driven design is a different story altogether though. While typescript comes with a certain amount of overhead, it becomes manageable and doesn’t impact development times after a while. Test-driven development on the other hand… First of all, it doesn’t make sense to use it everywhere. Do you plan to test if your component actually renders that div? Do you repeat the “red until green” Cycle for every component you are going to write? Well, I don’t.
Additionally TDD means you need to spend more time while developing new stuff for the first time, hacking away and writing tests later allows you to ship faster and… Probably never write those tests in the end, unless your project ever switches over from active development into a maintenance/support mode.
TDD is hard to sell to management or your project manager and in most cases, it’s even harder to get the client to agree unless you simply don’t tell him and do it silently.
But today is different. I’m here today to tell you that TDD has its uses and pairs very well with typescript too. Why? It makes you think.
I can’t show you our customers’ business logic here but I’ll try to explain it on a private project I do after hours for fun. In the following examples, I’m setting up the basics to a pseudo-AI that determines the results of a decision-making model based on configurable sets of parameters. It’s basically a way to tell your heroes in a game how to pick targets and choose skills and spells based on predefined rules.
Usually, I would sit down and simply write the basics and hack away from there. If it works, keep it and expand it. If not, discard and try again. I’ve previously written about rapid prototyping and I’m still a big fan but this time I have a very clear idea about what I want to end up with so I can sit down and properly plan ahead using the previously mentioned tools.
I’m dividing my process into the following steps:
- paper prototype planning
- Empty functions
- Typescript types
- Tests (red)
- Actual code
- Green test check
This process makes me think about what type of data I’m handling, how to process and transform it and this is also a good way to structure your code before you write it. When preparing coding sessions, tutorials and seminars, this has come in handy as well.
If you have no clue what I’m talking about when I mention gambits or unit behaviour rules, think of characters in a strategy / role playing game that need to decide what to do next based on rules. The enemy might attack the player or use a healing spell when his hitpoints are below a certain level… that sort of thing.
What do we need for this example? I spell it out and write it down. This is a step I often do on paper or my smartphone way before I write the first line of code.
1) Paper Prototype
- the player can create and adjust gambits that will control his units
- gambits will be checked one after another until a condition is met
- get all targets matching the selection
- filter them down based on the condition
- see if the skill/attack is available
- return the action and target or check the next gambit
Our next step is to put this general idea into functions we could implement.
2) Empty Functions
As you can see, from reading this rough sketch alone you already get a pretty good idea of what will happen when and why. All functions have fitting names (some may change during implementation anyway) and you also get a good idea about what these functions will need to do their job.
3) Typescript Types
Feel free to skip this code example unless you are actually interested in writing game code… in that case, carry on!
Let’s put those types to good use by creating our functions.
As you can see, we have already built a lot of confidence in our code just by defining types. What do we want from the tests now? If we produce an output that does not match our types, the typescript compiler will already catch this. We don’t write tests to reach a certain level of code coverage, we will write tests to check in those nooks and corners where typescript can’t look.
If we were to implement tests for this demo project, we would check for validation issues with user input, application of filters (like getAvailableTargets()) and complexity levels inside our functions including whether or not certain subfunctions have been called and make sure that edge cases and such are caught correctly.
Writing the actual tests would go a bit too deep for this article but I will cover it when I talk about my pet project in the future.
My initial point was that using Typescript and Test Driven Design makes you think differently about the issues at hand. They force you to think ahead and make a mental model before you start coding.
Is this slower?
Yes, to some extent it will be slower, even if we finish the whole project and can start to reap the benefits of our preparation. Unless you are very well accustomed to both TDD and TS, you will need a bit of extra time to plan, figure out types and write tests, but… !
If during the development of your project, you realise that you made a planning mistake and need to adjust some parts, allow for an extra case, different data or something, you will soon reap the fruits of your labour. One of the key strengths of Typescript compilers is to point out all the spots you might otherwise miss during refactoring or redesigning parts of your code and having those edge cases tested will make sure you don’t miss any of those either, even with the new data structure.
I will go into more detail about the actual testing of this pet project soon and you can then follow my progress commit by commit.
I’ve been writing Typescript projects with a lot more confidence and adding tests before code to some of my projects made me think an plan better. I hope I can share this experience with you in the future.
Some words about me:
If you want to see more of my work and progress, feel free to follow me and check out my other articles. If you clap feverishly for the articles you like most, it will be easier for me to decide which directions to pursue in following articles so use your ability to cast a vote for future content.
I’m also currently working on other series covering complex React Native Setups using Typescript and scalable apps with Redux, where I’ll go into details about how and why I do stuff the way I do as well as some articles on my experience in building games for web and mobile with React.
Here are some of my recent topics:
- React Quick Start with Typescript, Redux and Router
- Linting/Prettier with Typescript
- Redux + Toolkit with Typescript
- clean and simple Redux, explained
- Game Theory behind Incremental Games
- Custom and flexible UI Frames in React Native
And if you feel really supportive right now, you can always support me on patreon, thus allowing me to continue to write tutorials and offer support in the comments section.