1.4-Software-Development-Principles

Waarom zou je Unittest?

Het afgelopen schooljaar is je misschien het volgende opgevallen:

Om controle te nemen over deze sitatie raden we je aan om een gedeelte van je tijd te investeren in het schrijven van unittests.

  1. Tests geven je vertrouwen dat (stukken van) jouw code werken zoals verwacht.
  2. Het ontwerpen van tests dwingt je om na te denken over je ontwerp.
  3. Tests ondersteunen je wanneer je in de toekomst je code wil aanpassen.

Wanneer je tests schrijft, denk je na en onderzoek je mogelijke problemen voordat ze ontstaan.

Wanneer je Unittest?

Schrijf je tests zo vroeg mogelijk:

Tip van de professionals: Aan het einde van de dag, rond een uur of vier, moet je niet beginnen aan een nieuwe moeilijke taak. Investeer het laatste deel van je dag in het schrijven van unittests. Op die manier kun je evalueren dat jouw werk van die dag overeen komt met de originele requirements. (Daarnaast kan het relatief ontspannend werken.)

Denken over tests maakt je ontwerp (en je code) beter:

Maak gebruik van Test Driven Development

Je kunt zelfs tests schrijven voor er ook maar iemand daadwerkelijke code gaat schrijven! Dit heet Test Driven Development. (TDD)

Hoe gaan we Unittesten?

Het JUnit vijf Logo is een cirkel met een witte vijf in het midden, de linker helft is rood, de andere helft groen.

JUnit is a testing framework that allows you to automatically run all the test methods that you wrote. In these methods pieces of your code are used and the resulting answers are checked against the results that you would expect.

Tests must be executed independently of each other, so that in the end you will presented with the total report. Preferably all tests succeed, but if someone in the team made a mistake, some tests will be shown as failed. This will help you determine the problem:

List of unittest results

This partial report shows the classes that contain test code: TestTuple and MinHeapTest. (Yes naming conventions here are a subject of ongoing discussion.)

In this report it is easily shown that all tests that were designed for TestTuple have executed successfully. Whereas the MinHeapTest class contains one test method that failed. This gives us a point to start investigating.

Writing code for testing

The principle concept of unit testing is to add code to your project that is meant to test the actual production code. In principle this would mean that for each function that does something, you write at least one test function that confirms that it works.

A simple example might test the function that calculates the surface area of a rectangle. To test this method you will probably provide it with some values and confirm that for all of those possible values the correct surface area is returned.

To satisfy your curiosity we provide an example of such a test function. Please don’t worry about the details yet:

class Rectangle {
	public static int calculateSurface(int height, int width) {
		return height * width;
    }
}

class TestMathUtils {
	@Test
	public void CalculatingSurface_ReturnsTheCorrectValues() {
		// Prepare some data.
		int width = 5;
		int height = 3;
		// Do the calculation.
		int answer = Rectangle.calculateSurface(width, height);
		// Determin the correct answer separately from the code.
		int expectedAnswer = 15;
		// Confirm that both answers are equal.
		assertEquals(expectedAnswer, answer, "Surface calculation is not correct.");
	}
}

The code example above supplies four tests that confirm that the surface calculations works as expected.

But unittesting can do much more:

Limits of unittesting

Unittest are intended to test small segments of your code, through code. Something you cannot do is test that when the user presses a button on the screen, the program responds correctly.

Your goal when writing unittests is test all parts of your program separately and independently. When done correctly, you may be confident that when all those parts work together they function as expected as well.

Additional uses for unittesting

Sometimes as said in previous lessons, it becomes necessary to refactor your code. (Imagine that you come up with a new way to calculate the surface of an area) When the interface is unchanged, the same unittests can confirm that the new version of the code still works as originally designed.