2013-07-16 4 views
3

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

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

Это может быть лучше всего объяснено примером реальной жизни. После зеленого цикла TDD мы имеем следующий код:

public bool ShouldVerifyDomain 
    { 
     get 
     { 
      return this.Orders.SelectMany(x => x.Items).Any(x => x.Product.RequiresDomainVerification); 
     } 
    } 

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

public bool HasItemsThatRequireDomainVerification 
{ 
    get 
    { 
      return this.Items.Any(x => x.Product.RequiresCascadeDomainVerification); 
    } 
} 

И модифицирована ShouldVerifyDomain к:

public bool ShouldVerifyDomain 
    { 
     get 
     { 
      return this.Orders.Any(x => x.HasItemsThatRequireDomainVerification); 
     } 
    } 

ОК, что выглядит немного лучше, я счастлив с этим. Перейдем к следующему тесту в моем списке ... но ... подождите, мы теперь тестируем свойство HasItemsThatRequireDomainVerification через свойство на другом объекте .... это истинный модульный тест или я должен добавить тест (s) для непосредственного тестирования HasItemsThatRequireDomainVerification.

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

Что он может дать нам? «Документация» открытого интерфейса Order.

Мысли?

ответ

8

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

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

После рефакторинга, если вас беспокоит необходимость добавления дополнительных тестов, первое место, которое вы должны посмотреть, - это ваше тестовое покрытие. Если вы все еще на 100% (или близки к нему, как и перед рефактором), вы, вероятно, все еще хороши. Если, с другой стороны, рефакторинг добавлены пути кода, которые не описаны в тестах, то у вас есть несколько вариантов:

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

Что вы спрашиваете, очень похож на старый вопрос о покрытии теста, который был задан во многих формах:

  • Должен ли я проверить личные членов?
  • Должен ли я писать индивидуальный тест для каждого метода?
  • Должен ли каждый член каждого объекта проходить тест?

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

public class Customer 
{ 
    public string Name { get; set; } 
} 

Мне нужно написать тест, который инстанцирует Customer Do, пишет значение Name к нему, а затем утверждает, что он может читать обратно то же самое значение? Совершенно очевидно. Если это не удается, то есть что-то глубоко неправильно. Однако, если эта строка кода будет покрыта испытаниями? Абсолютно. Где-то должен быть тест, в котором используется CustomerName. Если этого не происходит, если ни один тест в системе не использует это свойство, то либо тесты являются неполными, либо это свойство фактически не требуется системе и должно быть удалено.

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

4

Когда вы делаете TDD, ваши тесты должны быть на уровне функциональных «функциональных тестов», так как функциональность не изменяется, вы не должны менять свои тесты.

Изменение реализации или рефакторинга рассматривается в TDD, если входные и выходные функции являются одинаковыми.

TDD не должен приводить вас к 100% -ному охвату.

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

+0

Почему downvote? – Skyp

2

Вы не изменили поведение, только синтаксис. Код по-прежнему работает одинаково, он просто написан по-разному. Я думаю, что до тех пор, пока он все еще работает одинаково, ваши модульные тесты остаются надежными.

Я думаю, что мы закончим спуск кроличьей дыры, если рефакторинг потребует, чтобы мы начали тестировать новый код, который мы реорганизовали. Когда это закончится?

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