Должен ли я тестировать (дублировать) данные или только поведение?

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

Предположим, у нас есть аварийный номер телефона классификатора:

public class ContactClassifier {

    public final static String EMERGENCY_PHONE_NUMBER = "911";

    public boolean isEmergencyNumber(String number) {
        return number.equals(EMERGENCY_PHONE_NUMBER);
    }
}

должен ли я проверить его таким образом (дублирование "911"):

@Test
public testClassifier() {
    assertTrue(contactClassifier.isEmergencyNumber("911"));
    assertFalse(contactClassifier.isEmergencyNumber("111-other-222"));
}

или (проверьте, правильно ли распознано " настроено" номер):

@Test
public testClassifier() {      
    assertTrue(contactClassifier.isEmergencyNumber(ContactClassifier.EMERGENCY_PHONE_NUMBER));
    assertFalse(contactClassifier.isEmergencyNumber("111-other-222"));
}

или ввести " 911 " в конструктор,который выглядит наиболее разумным для меня, но даже если я это сделаю - должен ли я написать тест для "клея приложения", если компонент был создан с правильным значением? Если кто-то может сделать опечатку в данных (коде), то я не вижу причин, по которым кто-то может сделать опечатку в тестовом случае (я уверен, что такие данные будут копировать-вставить)

6 ответов


какой смысл в тестовых данных что можно проверить? Это постоянное значение на самом деле постоянное значение? Это уже определено в коде. Java гарантирует, что значение на самом деле является значением, поэтому не беспокойтесь.

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

что вас беспокоит, так это то, что данные, которые должны быть хорошо известны всем), помещаются в тест, и это совсем не правильно. Что вы можете сделать, это переместить его на уровне интерфейса. Таким образом, по дизайну у вас есть известные данные приложения, предназначенные для части контракта, и его правильность проверяется компилятором java.

значения, которые хорошо известны, не должны проверяться, но должны обрабатываться интерфейсами какого-либо рода для их поддержания. Изменение легко, да, и ваш тест не потерпит неудачу во время этого изменения, но чтобы избежать несчастных случаев с ним, вы должны иметь запрос на слияние, обзоры и задачи, связанные с ними. Если кто-то меняет его случайно, вы можете найти это в обзоре кода. Если вы совершаете все, чтобы овладеть, у вас есть большие проблемы, чем константы вдвойне определенный.

Теперь, на части, которые беспокоят вас в других подходах:

1) Если кто-то может сделать опечатку в данных (код), то я не вижу причин, по которым кто-то может сделать опечатку в тестовом случае (я уверен, что такие данные будут копировать-вставить)

на самом деле, если кто-то изменяет значения в данных, а затем продолжает развиваться, в какой-то момент он будет запускать clean-install и видеть эти неудачные тесты. В этот момент он, вероятно, изменит / проигнорирует тест, чтобы пройти его. Если у вас есть человек, который изменяет данные так случайно, что у вас есть большие проблемы, а если нет, и изменение определяется задачей - вы заставили кого-то сделать изменение дважды (по крайней мере?). Никаких плюсов и много минусов.

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

3) Должен ли я проверить его таким образом:

@Test
public testClassifier() {
    assertTrue(contactClassifier.isEmergencyNumber(ContactClassifier.EMERGENCY_PHONE_NUMBER));
    assertFalse(contactClassifier.isEmergencyNumber("111-other-222"));
}

не в этом путь. Это не тест, а тестовая партия, т. е. несколько тестов в одном методе. Это должно быть так (convention-s):

@Test
public testClassifier_emergencyNumberSupplied_correctnessConfirmed() {
    assertTrue(contactClassifier.isEmergencyNumber(ContactClassifier.EMERGENCY_PHONE_NUMBER));
}


@Test
public testClassifier_incorrectValueSupplied_correctnessNotConfirmed() {
    assertFalse(contactClassifier.isEmergencyNumber("111-other-222"));
}

4) это не обязательно, когда метод правильно назван, но если он достаточно длинный, вы можете рассмотреть возможность именования значений внутри теста. Например

@Test
public testClassifier_incorrectValueSupplied_correctnessNotConfirmed() {
    String nonEmergencyNumber = "111-other-222";
    assertFalse(contactClassifier.isEmergencyNumber(nonEmergencyNumber));
}

внешние константы как таковые имеют проблему. импорт исчезает, и константа добавляется в пул констант класса. Следовательно, когда в будущем константа изменяется в исходном классе, компилятор не видит зависимости между ними .Class файлы, и оставляет старое постоянное значение в тестовом классе.

таким образом, вам понадобится чистая сборка.

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

конечно повторяющиеся значения могут быть помещены в местные константы. Предотвращает опечатки, легко-как локальная абстракция, проясняет семантический смысл значение.

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


на мой взгляд, тест должен проверить поведение, а не внутреннюю реализацию. Дело в том, что isEmergencyNumber проверяет число над константой, объявленной в классе, который вы пытаетесь проверить, является проверкой внутренней реализации. Вы не должны полагаться на него в тесте, потому что это не безопасно.

позвольте привести несколько примеров:

Пример 1: кто-то изменил EMERGENCY_PHONE_NUMBER по ошибке и не заметили. Второе испытание никогда не настигнет он.

Пример #2 предположим ContactClassifier изменяется не очень умным разработчиком на следующий код. Конечно, это совсем крайний случай и скорее всего это никогда не произойдет на практике, но она также помогает понять, что я имею в виду.

public final static String EMERGENCY_PHONE_NUMBER = new String("911");

public boolean isEmergencyNumber(String number) {
    return number == EMERGENCY_PHONE_NUMBER;
}

в этом случае ваш второй тест не потерпит неудачу, потому что он зависит от внутренней реализации, но ваш первый тест, который проверяет реальное поведение слова, поймает проблему.


написание модульного теста служит важной цели: вы указываете правила, которым должен следовать тестируемый метод. Таким образом, когда метод нарушает это правило, т. е. поведение изменяется, тест завершится неудачей. Я предлагаю написать на человеческом языке, каким вы хотите, чтобы было правило, а затем соответственно написать его на компьютерном языке. Позвольте мне уточнить.

1 когда я спрашиваю ContactClassifier.isEmergencyNumber метод "строка "911" экстренный номер?" надо сказать, да. Переводится как

assertTrue(contactClassifier.isEmergencyNumber("911")); 

это означает, что вы хотите контролировать и проверять, какое число указано константой ContactClassifier.EMERGENCY_PHONE_NUMBER. Его значение должно быть 911 и что метод isEmergencyNumber(String number) делает свою логику против этой строки "911".

2 когда я спрашиваю ContactClassifier.isEmergencyNumber метод " строка, указанная в ContactClassifier.EMERGENCY_PHONE_NUMBER номер экстренной помощи ?", он должен сказать "да".

переводится как

assertTrue(contactClassifier.isEmergencyNumber("911")); 

что это означает вам все равно, какая строка указана константой ContactClassifier.EMERGENCY_PHONE_NUMBER. Только что метод isEmergencyNumber(String number) делает свою логику против этой строки.

Итак, ответ будет зависеть от того, какое из вышеперечисленных поведений вы хотите обеспечить.


Я бы выбрал

@Test
public testClassifier() {
    assertTrue(contactClassifier.isEmergencyNumber("911"));
    assertFalse(contactClassifier.isEmergencyNumber("111-other-222"));
}

поскольку это не проверяет что-то из тестируемого класса, которое может быть неисправным. Тестирование с помощью

@Test
public testClassifier() {      
    assertTrue(contactClassifier.isEmergencyNumber(ContactClassifier.EMERGENCY_PHONE_NUMBER));
    assertFalse(contactClassifier.isEmergencyNumber("111-other-222"));
}

никогда не поймает, если кто-то вводит опечатку в ContactClassifier.EMERGENCY_PHONE_NUMBER.


на мой взгляд, нет необходимости проверять эту логику. Причина в том, что эта логика для меня тривиальна. Мы можем протестировать всю строку нашего кода, но я не думаю, что это хорошая идея. Например геттер и сеттер. Если мы следуем теории, чтобы проверить всю строку кода, мы должны написать тест для каждого из геттера и сеттера. Но эти тесты имеют низкую ценность и стоят больше времени на запись, на поддержание. Это не очень хорошая инвестиция