Assertion vs Assumption when writing tests (JUnit)

Tags: java, junit
Published: 2020-11-16 07:27:23

Most of the developers are familiar with the concept of assertion in test writing. *Assertion* is the process of making sure some condition is met. Most of the times the condition is a kind of check for the result of the tested unit. For example an array being empty, of a variable being filled with the proper value. Check this example, let's write a simple calculator class. This class has two methods: `add` and `multiply`. `add` method simply adds two integers and returns the result, but the `multiply` method uses the add method for multiplication.
```

public class Calculator {

    public int add(int a, int b) {
        return a + b;
    }

    public int multiply(int a, int b) {
        int result = 0;
        for (int i = 0; i < b; i++) {
            result += add(result,a);
        }
        return result;
    }
}

```

For the add method my unit test would be like this :

    @Test
    public void testAdd() {
        int a = ThreadLocalRandom.current().nextInt();
        int b = ThreadLocalRandom.current().nextInt();
        int result = calculator.add(a, b);

        Assertions.assertEquals(a + b, result);
    }

For any two given integers, the result of `add` method should be equal to adding them together.
My unit test for `multiply` method would be like this:
```
    @Test
    public void testMultiply() {
        int a = ThreadLocalRandom.current().nextInt();
        int b = ThreadLocalRandom.current().nextInt();

        int multiplicationResult = calculator.multiply(a, b);

        Assertions.assertEquals(a * b, multiplicationResult);
    }
```

It works, but there's something to note. We know that the `multiply` method heavily relies on the `add` method. If add is not working, there's no point in testing the `multiply`. For our example, it would not hurt much but in real-world problems, it may affect test time and resources heavily. What can we do? We can use *Assumption*.

    @Test
    public void testMultiply() {
        int a = ThreadLocalRandom.current().nextInt();
        int b = ThreadLocalRandom.current().nextInt();
        int additionResult = calculator.add(a, b);

        Assumptions.assumeTrue(a + b == additionResult);

        int multiplicationResult = calculator.multiply(a, b);

        Assertions.assertEquals(a * b, multiplicationResult);
    }

When assuming, you check the prerequisites of the test, if they are not available or do not have the expected value, there's no point in continuing with the test. In the above example, we first make sure that the `add` method is working properly.

Why not just assert them? The difference is if the assertion fails the test will fail but if the assumption fails the test will be ignored. Although in many tests they are asserted, that's not the way it should be. We do not want to test the functionalities of other methods, but we want to be sure that they are working properly.

If the tests are well written for all the parts of code, the part which makes the assumption fail would have proper tests that would fail.

For example, if our add method is not working properly, the `testAdd` would fail and the `testMultiply` would be ignored.