1. Unit testing basics

Supporting video
It is a simple fact that, the reason why it is called unit testing is because, the aim of the tests written by us (developers) is to assure the quality and reliability of the code implemented.
A unit test is a piece of a code (usually a method) that invokes another piece of code and checks the correctness of some assumption afterward. If the assumptions turn out to be wrong, the unit test has failed. A unit is a method or function.
SUT stand for system under test, and some people like to use CUT (class under test or code under test). When you test something, you refer to the thing you’re testing as the SUT/CUT.
Now I think the best way to understand this is to check how it looks in code, take a look at the following example.
Example 1.
Take a look at the code written in sample_1, which will be disected in the forthcoming few sections. The project resides under the folder app and the rest are build files generally, where settings.gradle what interests us. Inside the gradle settings, we can inspect what we will be testing during this first example. Namely, rootProject.name will tell us the package where the class resides and include will contain the name of it. If we go to App.java we can see that the SUT/CUT is class App. There are a few practical things to note at this point
- The CUT is
publicwhich renders it testable from the outside, otherwise it would be extremely hard to “break” into the internals of the class at hand - All the functions that are implemented inside the class are
publicas well, which is also necessary for testing purposes
sample_1/App.java what is the CUT? What is the smallest unit of code that can be tested?
It is always subjective to the actual codebase that you are working on, what would be the smallest amount/unit of code that can be tested by these unit testing techniques. Usually it is a good descriptor of the codebase at hand on how big is this thing actually.
Going further, to do unit testing it is a necessity to understand the actual code at hand, which in this case is the following
- The class has three constructors, a default
App(), a simple overloadApp(int aNumber), which sets only private member of the class andApp(boolean aWait), which makes the constructor wait for a certain amount of time to evaluate.1 - Has a simple function returning a greeting
String getGreeting() - The usual java
public static void main(String[] args)function, nothing special about it - A print function,
public static void callNavi(String aMessage), which simply forwards the arguments to the standard output - A multiply function
public int multiply(int aLeft, int aRight), which gets two arguments, and multiplies two numbers.
Now you can see sometimes, a function which is testable might not be a good candidate for tests because simply it is irrelevant and other cases there might be functions are not testable and relevant, which is the worst case possible in terms of testing.
To test the functionalities inside sample_1 we have to understand the build system a little bit better. Take a look at build.gradle file and see what it does in terms of helping us test the previous functionnalities. The structure is the following, plugins tells us what class/package/application are we building with the help of gradle. the next one is one of the most important ones, which is repositories, which tells gradle wherefrom to fetch the necessary libraries and packages to build and run the project and its dependencies. The next one is dependencies which as the name tells lists all the dependencies needed by our application. The testing block specifies the actual testing libraries that we will use throught the project to run the implemented tests. The last one is application, which defines what is the main class, the main entry point, which can be used by the frameworks.
Now, let’s take a look at the already implemented test inside Apptest.java. The structure is the following
- It starts by importing all the necessary packages, it is nothing special
- It is a
public classso the unit testing framework can access all the details inside it - Inside the class there is one member
App mApp, which is basically the application that we are about to test. - Here comes the cool part, the first function that will be run by our unt testing framework (junit5) is the usual
Setupfunction with the@Beforeannotation. This function simply runs only once before all the tests are being run by our framework. In this case it is simply constructing ourAppclass with0in the constructor argument. - Next the function
multiplicationOfZeroIntegereShouldReturnZerois being implemented. The usual test functions shall be annotated with@Test. Here comes the interesting part, inside this function there areassertions, which means the test is assumptions and asserting against them.
Imagine having 10.000 tests and having multiple assertions inside them, how would you be able to pinpoint where the implementation might have gone wrong?
- The similar function
multiplicationOfZeroIntegereShouldReturnZeroWithMemberhas the same-ish functionality, however if you take a look it doesn’t have an instantiation of the class that will be tested during the evaluation. beforeAffectsTestis simply demonstrating that one can just assert with a test function, which usually raises awareness for an unimplemented test case or in TDD tells the developer that this functionality need to be developed further.- And at last the
TearDownfunction with cleans up all the necessary memory/setups/etc after running the unit tests.
After seeing how this code looks like, one can go further and pose the following definition on what is a unit test.
Supporting video
A unit of work is the sum of actions that take place between the invocation of a public method in the system and a single noticable end result by a test of that system. A noticeble end result can be observed without looking at the internal state of the system and only through its public APIs and behavior. An end result is any of the following
- The invoked public method returns a value
- There is a noticeable change in the state of behavior, that can be determined by interrogating the internal state
- There is a callout to a 3rd – party system where over the test has no control of
This could be true, however if there is a 3rd party functionality that we have no control over, changing its functionality might affect our tests, which wouldn’t render them as unit tests anymore.
A unit test is a piece of code that invokes a unit of work and checks one specific end result of that unit of work, whereas it is fully isolated. If the assumptions on the end result turn out to be wrong, the unit test has failed. A unit test’s scope can span as little as a method or as much as multiple classes.
- The primary goal of unit testing is to
- Take the smallest piece of testable software in the application
- Isolate it from the remainder of the code
- Determine whether it behaves exactly as you expected
Practically, a unit test is a piece of code that
- Invokes another piece of code
- Checks the correctness of some assumption afterward
- If the assumption turn out to be wrong, the unit test has failed. A unit is a method or function.
Properties of good unit tests are
- It should be automated and maintainable
- It should be easy to implement
- It should be relevant tomorrow
- Anyone should be able to run it at the push of a button
- It should run quickly
- It should be consistent in its results
- It should be fully isolated
- When it fails, it should be easy to detect what was expected and determine how to pinpoint the problem
A unit test is an automated piece of code that invokes the unit of work being tested, and then checks some assumptions about a single end result of that unit. A unit test is almost always written using a unit testing framework. It can be written easily and runs quickly. It’s trustworthy, readable, and maintainable. It’s consistent in it’s results as long as production code hasn’t changed
Supporting video
Tasks 1.
- Run the tests in
sample_1, based on the instructions from github - Check what is wrong with the tests
- Implement the test functions for
Appwhere it is “logical”
- Could you test
staticfunctions? What is the problem with these?
Footnotes
And throwing an exception
throws InterruptedExceptionas necessary.↩︎