Тесты для функции

183
23 декабря 2019, 09:20

Есть функция для работы с переменной (полем) “Возраст студента”. КАК спроектировать для этой функции любые кейсы (тесты) для верификации?

if (!studentAge.matches("\\d+") || 
        Integer.parseInt(studentAge) < 5 || 
        Integer.parseInt(studentAge) > 65) {
    System.out.println("Invalid Age!");
}
Answer 1

Тесты - это контракт.

Для того чтобы спроектировать тесты, необходимо знать что должна делать функция и чего не должна.

В Вашем случае необходимо убедиться что функция делает то что Вы задумали - проверяет возраст студента.

Вот Ваш пример, в виде класса

public class Student {
    private String studentAge;
    public Student(String studentAge) {
        this.studentAge = studentAge;
    }
    public void checkAgeError() {
        if (!studentAge.matches("\\d+") || 
                Integer.parseInt(studentAge) < 5 || 
                Integer.parseInt(studentAge) > 65) {
            System.out.println("Invalid Age!");
        }
    }
}

Я назвал функцию checkAgeError() формальные unit-тесты для нее написать будет сложно т.к. функция содержит System.out которая является сайд эффектом (side effect), для этой функции (т.к. название не отражает то, что функция что-то выведет) хоть и тип возвращаемого значение void неявно нам на это намекает. Но проблема не в наличии сайд эффекта а в его характере - вывод в консоль это IO (общение с подсистемой ввода-вывода) и ссылка на это поле использована статическая.

Что в этом плохого - как раз то, что cложно написать тест для этой функции, чтобы убедиться что она отработала корректно Вам необходимо иметь дело со статическим полем класса System, это сильно повезло что System.out можно перезадать на свой OutputStream, а не на вывод в консоль и узнать что туда было записано в рамках вызова тестового метода.

Чтобы этого не делать надо немного перепроектировать функцию, чтобы она возвращала результат проверки, а не выводила текст в консоль.

public void checkAgeError() {
    return !studentAge.matches("\\d+") || 
         Integer.parseInt(studentAge) < 5 || 
         Integer.parseInt(studentAge) > 65;
}

Теперь выводить в консоль нужно в том месте, где вызывается функция checkAge() а не внутри нее, и мы можем легко написать unit-тесты к этой функции.

Какой тут контракт?

  1. Возраст должен быть задан числом
  2. Возраст должен быть в диапазоне 5-65

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

import org.junit.Test;
import static org.junit.Assert.*;
public class StudentTest {
    @Test
    public void checkAgeDataType() {
        assertTrue( "must return true when age is not number",
                new Student("").checkAgeNotMatch());
    }
    @Test
    public void checkAgeMin() {
        assertTrue( "must return true when age is lower than 5",
                new Student("4").checkAgeNotMatch());
    }
    @Test
    public void checkAgeMax() {
        assertTrue( "must return true when age is greater than 65",
                new Student("66").checkAgeNotMatch());
    }
    @Test
    public void checkAgeInterval6() {
        assertFalse( "must return false when age is number between 5 and 65",
                new Student("6").checkAgeNotMatch());
    }
    @Test
    public void checkAgeInterval64() {
        assertFalse( "must return false when age is number between 5 and 65",
                new Student("64").checkAgeNotMatch());
    }
    // так же возможно проверка поведения с выбросом исключения
    @Test(expected = NullPointerException.class)
    public void checkAgeIsNull() {
        assertTrue( "must throw npe when number is null",
                new Student(null).checkAgeNotMatch());
    }
}

Теперь, когда функция checkAgeNotMatch сломается по какой-либо причине, должен упасть какой-о из тестов, но для этого нужно их постоянно запускать

PS: это очень простой пример, тут нет mock объектов, которым можно было бы закрыть System.out, однако не буду я тут писать целую книгу..., извините что может быть слегка сумбурно, давайте вместе поправим, если что-то запутано написано =)

READ ALSO
Как заставить героя двигаться в нужном направлении?

Как заставить героя двигаться в нужном направлении?

Пишу небольшой 2D шутер, на данный момент герой поворачивается в ту сторону куда смотрит мышь, не могу понять как сделать так чтобы он двигался...

143
Intellij-IDEA путает версию Java

Intellij-IDEA путает версию Java

IDEA возвращает вот такую ошибку:

143
RecyclerView onClick

RecyclerView onClick

как сделать OnClick в моем адаптереодна кнопка если на нее нажали то должно происходить какое то действие

148
заполнение Collection JAVA

заполнение Collection JAVA

Как написать метод,который помещает в Коллекцию сразу все объект(а не по одному)? Есть класс Products с конструктором и переопределением toString...

150