Unit Testing – Get inspired
Imagine what is possible
Can you relate to the comment in the code below?
In the traditional way of working it can be quite scary to modify working code, so the tendency is to not touch it. This fear is not conducive to agility. I want to inspire you on how safe you could feel to modify working code of high risk functionality without fear of introducing defects in production.
Scenario 1: We are tasked with refactoring the changePassword()
method implementation, to improve its efficiency, without breaking anything. Would you feel comfortable touching this high stakes changePassword()
code which millions of users use every day that protects very sensitive information? I want to convince you that you can feel comfortable doing this if you had unit tests.
Check out the screenshot below of what our development environment could look like:
In this scenario we start with implementation A of changePassword()
which passes all the associated unit tests and has been working without defects in production, but it runs slow and we want to speed up the implementation.
After making some modification to changePassword()
, in an attempt to make it run faster, we now have implementation B, which passes only some of the unit tests. It seems like we broke something, but we don’t panic, because we immediately found out exactly what we broke as we broke it and can fix the problems right away as we code.
After further refactoring of the code we end up with implementation C of changePassword()
, which runs faster than implementation A and once again passes all the unit tests, increasing our confidence we can safely deploy it to production.
Scenario 2: Now, imagine we want to roll out an enhancement to make passwords stronger. We choose to add a rule that changed passwords must have at least one special character, like ‘#’ for example.
We add a new unit test to ensure our application enhancement works and to ensure future modifications don’t break it. But while implementing the new rule we broke the application code and caused an older unit test to fail, which is ok, because we immediately find out. We can now fix the implementation on the spot before we forget what our code is doing, before the code goes anywhere more permanent, and before it affects anyone else.
So, what is the big idea? Well, we greatly increase our trust in making code changes, because we maintain a safety net of unit tests that immediately inform us when we break anything, so we can fix it right away. Remember that developers can run the unit tests directly in their Integrated Development Environment (IDE) any time, as they code and want to test their changes, over and over again, many times a day, every day.
What if we need to modify existing functionality? Well, our unit tests will tell us right away if we break anything, so we can fix it while the code is still fresh in our mind.
What if we need to add new functionality? Well, our existing unit tests will tell us right away if we break anything, and the new unit tests will tell us when we’re done, because they define what “works” means, and until they all pass our code does not “work”.
But, how do we get started? We accompany all new code with unit tests. The new code is the risky code. The old code is already working in production.
But, how do we make sure all new code is accompanied by unit tests? We write the unit tests first, before writing new application code. We’ll learn more about this in the lesson about Test-Driven Development (TDD). Rather than “implementing” a class first and then “using” it (elsewhere in our code), we first use the class (in our unit tests) and then implement it incrementally as we add unit tests that define additional aspects about it that should work.
What else do unit tests give us? Unit tests document the behavior of our application. New team members can learn a lot from it about what the application code does. Unit tests also ensure we write just enough application code to meet our needs, avoiding excessive unnecessary code.