Как мы тестируем тесты

Практика курса по тестированию сильно отличается от практик других курсов. Обычно вам нужно написать реализацию какой-то функции под существующий набор тестов. Здесь же всё наоборот. Вам нужно написать тесты для готового кода.

Разберем конкретный пример. Представьте себе функцию, которая возвращает последний элемент в массиве:

int[] numbers = {2, 2};
System.out.println(last(numbers)); // 2

Можно ли однозначно утверждать, что она работает верно основываясь на примере выше? Нет. Вполне возможно, что эта функция реализована так:

public static int last(int[] items) {
    return 2; // всегда возвращает 2
}

В таком случае понадобится еще один вызов чтобы проверить ее работу:

int[] numbers = {5};
System.out.println(last(numbers)); // 5

Достаточно ли теперь? И снова нет. Ее код может быть таким:

public static int last(int[] items) {
    return items[0]; // возвращает первый элемент вместо последнего
}

При такой реализации и первый вызов и второй вернут правильные данные, хотя функция реализована неверно. А вот правильная реализация функции:

public static int last(int[] items) {
    return items[items.length - 1];
}

Не вдаваясь в подробности устройства тестов (это тема следующего урока), можно увидеть, для чего они в принципе нужны. С их помощью мы убеждаемся, что код (в данном случае функция) работает правильно для всех (почти) возможных входных данных, а не только в каком-то конкретном случае.

Существует ровно один хороший способ, проверить, что вы написали тесты правильно – вызывать эти тесты с разными реализациями функции. Если реализация правильная, то тесты должны успешно пройти, если нет, то тесты должны свалиться с ошибкой. Такой подход позволяет проверять эффективность тестов, а не то, как они написаны. Только так можно научиться их писать самостоятельно.

Далее рассмотрим как мы технически реализуем проверку ваших тестов. Она тесно связана с темой функциональных интерфейсов и у некоторых студентов может вызывать трудности. Хотим сразу отметить – ничего страшного, если вдруг наша внутренняя реализация пока для вас непонятна. Здесь мы её даём для ознакомления, сам курс по тестам можно пройти и без этого.

Итак, как мы это делаем технически? В коде урока создается класс, содержащий несколько разных реализаций того кода, который вам нужно протестировать. Вот как будет выглядеть этот файл в случае тестирования функции last.

// обычно это файл Functions.java

// Разные реализации функции last. Имена функций не важны.
// Только одна реализация верная (right), остальные неверные (fail)
public class Functions {

    public static int fail1(int[] items) {
        return 2;
    }

    public static int fail2(int[] items) {
        return items[0];
    }

    public static int right1(int[] items) {
        return items[items.length - 1];
    }
}

В этом классе созданы 3 различные реализации интересующей нас функции. Они отличаются названием и реализацией, но входные аргументы (в данном случае int[] items) и тип возвращаемого значения (в данном случае int) у этих функций совпадают. Это позволяет нам передавать эти функции в качестве аргумента в ваши тесты. Для этого мы используем простой интерфейс с методом у которого точно такие же аргументы и тип возвращаемого значения:

public interface MyFunction {
    int apply(int[] arg);
}

И теперь, когда вы напишете некий свой метод, например, вот такой:

public static void userTest(MyFunction last) {
    int[] numbers = {7, 5};
    System.out.println(last.apply(numbers));
}

Мы сможем на вход вашего метода подать любую нужную нам реализацию метода. Например, вот так:

public static void checkTests() {
    userTest(Functions::fail1); // {7, 5} => 2
    userTest(Functions::fail2); // {7, 5} => 7
    userTest(Functions::right1); // {7, 5} => 5
}

А уже далее наша проверочная система (мы используем JUnit5) должна будет проверить следующее:

  1. Тесты с правильной реализацией функции выполнились успешно.

  2. Тесты, в которые передана неправильная реализация функции, должны упасть. Таким образом мы сможем убедиться, что ваши тесты не пропустили неправильную реализацию.

Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Javascript, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →