2009-10-28 5 views
4

Должны ли мы стремиться к СУХОЙ, в том смысле, что изменения в функциональности влияют как можно меньше кода, наша предсказуемость в том смысле, что операция кода тривиальна при написании модульных тестов? В основном я спрашиваю о компромиссе между созданием вспомогательных методов, которые являются очень общими, и которые могут использоваться несколькими модульными тестами по сравнению с тем, что только ограничивает тестовый код одним тестом. В качестве примера рассмотрим случай завода, который имеет следующий метод подпись:Единичные тесты: сухие против предсказуемости

public Node buildNode(String path, String name, Map<String, Object> attributes); 

В зависимости от параметров при условии, что результирующий объект Узел будет отличаться, и поэтому мы должны проверить различные возможности. Если мы стремимся к предсказуемости мы могли бы написать два теста автономных единиц, как указано в первом примере, но если мы стремимся к DRY мы бы скорее добавить общий вспомогательный метод, например, как во втором примере:

EXAMPLE1: 
@Test 
public void testBuildBasicNode() { 
    Node node = testee.buildNode("/home/user", "Node", null); 
    assertEquals("/home/user/Node", node.getAbsolutePath()); 
    assertEquals(false, node.isFolder()); 
} 

@Test 
public void testBuildAdvancedNode() { 
    Map<String, Object> attributes = new HashMap<String, Object>(); 
    attributes.put("type", NodeType.FOLDER); 
    Node node = testee.buildNode("/home/user", "Node", attributes); 
    assertEquals("/home/user/Node", node.getAbsolutePath()); 
    assertEquals(true, node.isFolder()); 
} 

EXAMPLE2: 
@Test 
public void testBuildBasicNode() { 
    Node node = testee.buildNode("/home/user", "Node", null); 
    Node comparisonNode = buildComparisonNode("/home/user", "Node", null); 
    assertEquals(comparisonNode, node); 
} 

@Test 
public void testBuildAdvancedNode() { 
    Map<String, Object> attributes = new HashMap<String, Object>(); 
    attributes.put("type", NodeType.FOLDER); 
    Node node = testee.buildNode("/home/user", "Node", attributes); 
    Node comparisonNode = buildComparisonNode("/home/user", "Node", attributes); 
    assertEquals(comparisonNode, node); 
} 

private Node buildComparisonNode(String path, String name, Map<String, Object> attributes) { 
    // Not just a duplicate of the buildNode method, 
    // can be more trivial if we only limit it to unit tests that share some common attributes 
    ... 
} 

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

Также, как заключительная мысль, объявляете ли вы конечные переменные для строк, используемых в примерных модульных тестах, или они в порядке, как есть?

+0

в одном корпусе = один тест. Я не вижу DRY =) – mauris

ответ

8
  1. Хороший вопрос. Я слышал, прежде чем «единичные тесты могут быть влажными, но не вымачивать влажными ...». Для обеспечения устойчивости теста основное внимание уделяется (или предсказуемости). Чем больше ваша команда, тем важнее я думаю, что это должно быть для вас. Еще одна вещь, которую следует учитывать, заключается в том, что если есть специальный тестовый хелпер-код, который сам по себе может стать API, поэтому может быть не плохой идеей, если все знают, как его использовать. Мое правило: я удалю дублирование, если я смогу сделать это с помощью IDE в автоматическом рефакторинге, и я могу назвать его хорошим именем.

  2. Предложение ... ознакомьтесь с рисунком Test Data Builder от Nat Pryce о более удобном и расширяемом подходе.

+0

Спасибо, что шаблон Data Data Builder гениальный! – Zecrates

+0

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

4

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

Мое правило состоит в том, чтобы мои тесты были как можно меньшими и максимально читаемыми. Если использование DRY может помочь в достижении этого, я использую его. Если нет, то я этого не сделаю. :-)

Надеюсь, что это поможет. Я не являюсь мировым экспертом при модульном тестировании, поэтому я могу ошибаться. :-)

0

Еще одна вещи, чтобы думать о - часто, когда вы удаляете дублирование в тестах это говорит вам, что что-то производственный код или дизайн, который ждет законных причин (а использование в производстве кодового), чтобы переместить его в производственный код. Я не могу точно помнить, где я получил этот ах hah момент от ... но я думаю, что это было связано с TDD as if you mean it.

Смежные вопросы