DRY между производственными и тестовыми константами кода

Обычно я стараюсь избегать дублирования и придерживаюсь принципа DRY. Однако меня интересует такой случай:

public class Feature {
    final static String FEATURE_LABEL = "blah";

    public void doSomething() { ... }
    ...
}

public class FeatureTest {
    ...
    @Test
    public void doSomethingShouldMakeSomethingHappen() {
         assertEquals(Feature.FEATURE_LABEL, 
             feature.getSomethingHappens().getLabel());
    }

Если требование состоит в том, чтобы метка была «blah», и кто-то изменил FEATURE_LABEL на «bleh», тест будет пройден, даже если он больше не соответствует требованию. Является ли это допустимым местом для нарушения DRY?


person Paul Croarkin    schedule 14.01.2009    source источник


Ответы (5)


Да, используйте литерал здесь.

Цитирую себя из вопроса о литералах:

Жестко заданные литералы должны появляться в модульных тестах для тестовых значений, за исключением случаев, когда повторное использование значения в одном тестовом классе настолько часто, что может оказаться полезной локальная константа.

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

person Andy Dent    schedule 14.01.2009

Чтобы протестировать что-то — что угодно — важным соображением является то, что ваши условия тестирования не зависят от того, что вы тестируете. В противном случае ваши тесты не имеют единственного надежного значения; они превращаются в какой-то другой тест каждый раз, когда изучаемый объект меняется.

Не очень хорошая вещь.

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

person Frederick The Fool    schedule 14.01.2009

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

assertEquals(Feature.FEATURE_LABEL, Feature.FEATURE_LABEL);

Скажем, например, у вас есть ограничение на список. Нет смысла проверять, что limit == limit, тест должен попытаться поместить в список больше элементов, чем limit.

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

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

person Aaron Digulla    schedule 14.01.2009

Я бы остановился на ссылке на данный момент.

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

Имеет ли это хоть какой-то смысл?

person Jon Skeet    schedule 14.01.2009
comment
Я согласен с тем, что если требование изменится, тест должен измениться. Я обеспокоен тем, что константа может использоваться более чем в одном месте, и требование может измениться для одного места, но остаться прежним для другого места. - person Paul Croarkin; 14.01.2009

Я думаю, что то, что у вас есть, хорошо, и да, это правильное использование DRY: если это значение окажется в нескольких тестах, вы не хотите изменять несколько, если значение изменится. Но вы также должны добавить дополнительный тест, который проверяет значение Feature.FEATURE_LABEL.

Это хорошее место для применения «один и только два раза»: если бы у вас был только один тест, в котором проверялось значение FEATURE_LABEL, я бы просто использовал литеральную строку. Только если у вас есть несколько тестов, использующих его, я бы начал использовать ссылку (и добавил тест для значения).

person Jeffrey Fredrick    schedule 14.01.2009