Quality Unit testing: Part 1
Things you need to understand before writing unit tests
5 min read
I'm glad that you are here to learn about software testing specifically unit testing that can avoid future bugs in your programs that could end up in production. Writing unit tests which has some positive net value takes a lot of effort. It's like preparing QAs that matter to you the most before going on a blind date so that you know you can't fuck up at any cost, but as we all know that we can't cover all the cases at the same time. Still, it's a safety check for the person which gives him/her confidence to show up confidently. Of course, there are other benefits as well further we will look into that.
In this series, I'll be following the top-bottom approach where you will look at all concepts from the high level and then dig deep into each topic. If you are new to software testing, then let me first introduce you to the Testing Pyramid.
This diagram shows the representation of 3 layers based on their return on investment.
Unit test: Tests the unit of function.
Integration test: Tests how two components work together.
End-to-End test: Tests that many components of your application work together well as if different systems work together well with each other.
Unit tests cover 70% of the test after that Integration test covers 20% and the rest 10% is the End-to-End test of the total test.
For this series, we are mainly going to focus on Unit Testing, altho there are already tons of books on Unit tests as well, I've personally followed these two books as suggested by my senior Unit Testing Principles, Practices, and Patterns and Effective Software Testing. When I was learning about Unit testing to implement it in my production project I didn't find any good short and crisp articles that I can refer to. So, I'm writing one 😉
What is Unit Test
You write a function and we want it to behave intently when you provide certain inputs. One way to test your function is to test it manually in that case, you have to run it multiple times which will take an enormous amount of your time. So, a better solution is to write a unit test that can be run using JUnit (testing framework) which will test your code against a set of inputs that you've provided whenever you want. Quick feedback is often quite valuable in a fast pace environment, so running Unit tests whenever PR is raised to see if all the tests are passing CI/CD like GitHub Actions is just one example.
Unit testing means
Testing a piece of code
Blazingly fast to run relatively to Integration and E2E tests
In an isolated manner
You can't argue on the first two points, but the third one is where the debate is started
Here, the unit can vary depending on the approach you are following. There are mainly two schools of thought are:
Classical school of unit testing or Detroit Style
London school of unit testing or Mockist
Classical school of unit testing or Detroit Style:
This interprets isolation as where multiple tests are running either in sequential, parallel, or in any order that shouldn't affect the output of the Unit tests.
London school of unit testing or Mockist
Focuses on testing the System under test and replacing all the dependencies with a test double specifically mocks. This allows the tester to focus on the System under test exclusively by avoiding any external impact.
Unit test you should avoid
Before understanding how good unit tests look like let's see what bad unit tests consist of
It shouldn't give false positives. If you are refactoring your code but the behavior remained the same, but after that change unit tests started failing then after some point either you'll stop maintaining those tests, or if you are too religious about the tests that you wanna see that green check which gives you that orgasmic vibe, then you've to refactor after every small change.
It shouldn't give false negatives, where behavior is changed but the tests are still passing.
Good unit tests
Before writing unit tests, In order to understand what good unit tests look like, you need to understand what makes them good. There are 4 pillars that you need to take into account when writing unit tests
Protection against regression: Regression is a software bug. The test must fail if it's not fulfilling the intended behavior. It is also known as a false negative because tests are passing even though the intended behavior is not achieved.
Resistance to refactoring: The degree to which the codebase can sustain refactoring without turning the tests into the red. If tests started to fail after doing modifications to the codebase for cleaning purposes that means your code is brittle.
Fast feedback: Usually unit tests are blazingly fast.
Maintainability: This depends in two defines how hard it is to understand the tests and how hard it is to run the tests if it's working with a dependency with interact with external APIs.
These four rubrics can you give the idea to evaluate your unit tests.
One more thing
I'll be covering the different types of unit testing like
Output-based testing style
State-based testing style
This will give you an idea about which one to apply according to your state machine and check coverages like code coverage, branch coverage, and mutation coverage to check the coverage area. It doesn't guarantee that your code will never fuck up instead it all depends on the tests cases that you came up with did you consider writing for the boundary cases as well because that's where most bugs are located
So far I've given you a high-level overview of what types of concepts we are going to be covering in the upcoming articles. I know it was theoretical and abstract, but in the next part, we will look at the code snippets which are bad, and using these rules we'll try to make them better.
Also, I was trying to keep the article short and crisp because I don't wanna bore you with long articles.
Thanks to Pedro, Piyush, Vaibhav, Dev, and Shreya for reviewing my draft.
Alright, until then see Yaa!, Peace 💚
Part 2 : https://medium.com/@hellosagar/quality-unit-testing-part-2-70507682e16d