What is Unit Testing?
Every complex system is made of smaller parts. A car has an engine, gearbox, brakes, a windshield, and more. Each of these components, when put together, make up a car. The only reason the car goes anywhere is because each of these components does its job well. Each of these smaller components is a “unit” in unit testing. A unit is the smallest testable part of any software. It usually has one or a few inputs and a single output.
You can unit test your car by taking it apart and running diagnostics on each of the individual components. Maybe you want to test the oil filter’s ability clean up some filthy oil, or perhaps you want to test the seal on the petrol cap. Each of those is a test that you can perform on the individual units which make up your car. In this way you can more easily narrow down the issue if there is a problem, and you can also ensure a component is outputting correctly before it affects another component.
A good unit test needs to have the following things:
- An isolated component that you are testing. If you test more than one thing together, that’s an integration test.
- A particular behavior that you are testing. It sort of goes without saying that this behavior needs be related to the component you are testing.
- A success and failure condition. Kind of a no-brainer. With unit tests, there’s no such thing as partially successful. When run, each unit test must either succeed or fail.
Benefits of Unit Testing
Unit testing reduces bugs and unintended issues when changing or maintaining code. If good unit tests are written and if they are run every time any code is changed, we will be able to quickly catch any issues introduced due to the change. Also, in order to make unit testing possible, we'll usually break down our code into smaller, independent chunks, this inherently means the risk of unintended issues is lowered.
Code that is broken down into independent units is also more reusable and readable
It is faster to identify & fix an issue found during unit testing than an issue found later at a higher level. Compare the cost (time, effort, destruction, humiliation) of an issue detected during acceptance testing or when the software is live.
Debugging is easier. When a test fails, only the latest changes need to be debugged. With testing at higher levels, changes made over the span of several days/weeks/months need to be scanned.
Code written with Unit Testing in mind is naturally more reliable.
What sorts of things can we test?
Pretty much everything. If it is a product of your code, you can test it. Here are some examples:
Making sure a view controller has all of its outlets hooked up so our app doesn't crash unexpectedly when the controller is presented.
Making sure text that is only supposed to appear on a Monday only appears on a Monday without having to wait until next Monday to find out.
Making sure a tableview has a cell registered for a nib file that is external to it, and that it won't cause the app to crash when the tableview tries to get the cell.
If any of those scenarios sound familiar, wouldn't it be nice to have a way to know with near 100% certainty that there won't be any problems before you run the app? That is what unit tests are for.
When is Unit Testing not relevant?
Ultimately, unit tests should save some time and/or money. When designing, creating and implementing automated tests takes more time than manual testing, it is counter-productive. However is should be said that when comparing the time savings, we should compare the time taken to design and implement an automated test versus the time taken to perform a manual test multiplied by the number of times we perform that manual test.
If you plan to release a standalone product with few regular updates, you won’t need to test all of its features that often. You might go years at a time between subsequent releases. In that case, it might make more sense to test everything manually whenever you need to do an update.
Other types of software products require near-constant maintenance and support. An enterprise system where users login and complete work tasks, or perhaps a social network, come to mind. These kinds of products have a very frequent release schedule, and they therefore need more testing overall.
If you find yourself manually testing the same logic over and over again, you might want to replace some of that manual testing with automated tests.
In addition, apps with more automated tests tend to have fewer bugs. When you get into a testing mindset, you start to consider more of the ways your code can misbehave. It forces you to get better at planning.
Unit Testing makes life easier
Let's say we have a program comprising of two units and the only test we perform is system testing (skipping unit and integration testing). During testing, a bug is identified. How do we determine the cause of the problem?
- Is the bug due to an error in unit 1?
- Is the bug due to an error in unit 2?
- Is the bug due to errors in both units?
- Is the bug due to an error in the interface between the units?
- Is the bug due to an error in the test or test case?
Unit testing is often neglected but it is, in fact, the most important level of testing.