1. Unit testing basics

Supporting video

Why is it called unit testing?

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.

Definition (Unit testing 1.)

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.

Definition (SUT and CUT)

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 public which 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 public as well, which is also necessary for testing purposes
In case of 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 overload App(int aNumber), which sets only private member of the class and App(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.
Understanding these functionalities, which ones are actually testable? And if they are testable, is it justified at all to test them?

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 class so 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 Setup function with the @Before annotation. This function simply runs only once before all the tests are being run by our framework. In this case it is simply constructing our App class with 0 in the constructor argument.
  • Next the function multiplicationOfZeroIntegereShouldReturnZero is being implemented. The usual test functions shall be annotated with @Test. Here comes the interesting part, inside this function there are assertions, which means the test is assumptions and asserting against them.
At this point, can you tell what might be the problem if we have multiple assertions inside a single test function?

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 multiplicationOfZeroIntegereShouldReturnZeroWithMember has 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.
  • beforeAffectsTest is 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 TearDown function 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

Definition (Unit testing 2.)

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.

Definition (Unit testing 3.)

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
Definition (Unit testing 4.)

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.

  1. Run the tests in sample_1, based on the instructions from github
  2. Check what is wrong with the tests
  3. Implement the test functions for App where it is “logical”
  4. Could you test static functions? What is the problem with these?

Footnotes

  1. And throwing an exception throws InterruptedException as necessary.↩︎