Используете ли вы константы из реализации в ваших тестовых примерах? - PullRequest
25 голосов
/ 29 июля 2010

Допустим, у вас есть такой код (в вымышленном языке, поскольку для этого вопроса это не имеет значения):

constant float PI = 3.14;
float getPi() 
{ 
   return PI;
}

Вы бы протестировали его следующим образом:

testPiIs3point14()
{
   // Test using literal in test case
   AssertEquals( getPi(), 3.14 );
}

Или вот так:

testPiIs3Point14()
{
   // Test using constant from implementation in test case
   AssertEquals( getPi(), PI );
}

Другими словами, используете ли вы константы из вашей тестируемой системы в ваших тестовых случаях?Или это считается деталью реализации?

Ответы [ 7 ]

17 голосов
/ 29 июля 2010

Два теста служат для разных целей.

Первый гарантирует, что getPi() всегда вернет 3.14. Он охватывает как константу, так и функцию, и потерпит неудачу, если кто-либо найдет значение PI, используемое в программном обеспечении, недостаточно точным и заменит его, скажем, 3.14159. Это может быть хорошо или плохо, в зависимости от контекста.

Пока вторая форма охватывает только функцию. Это не подведет, если кто-то изменит константу; он потерпит неудачу только в том случае, если функция модифицирована для возврата другой константы (с другим значением).

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

6 голосов
/ 30 апреля 2014

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

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

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

Что-то вроде реализации этих двух тестов:

testPiIs3point14()
{
   AssertEquals( PI, 3.14 );
}

testGetPiReturnsPi()
{
   AssertEquals( getPi(), PI );
}

PS: Хотя проверка значения может быть не столь важной для всех констант, для некоторых она может быть очень важной. Один из примеров, который я могу вспомнить, - это константы, содержащие URL-адреса или похожие значения.

3 голосов
/ 29 июля 2010

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

2 голосов
/ 27 августа 2015

Если вы действительно выставили оба как часть общедоступного интерфейса, это были бы два теста:

testPI() {
   AssertEquals(PI, 3.14);
}

test_getPi() {
   AssertEquals(getPi(), 3.14);
}

Однако, если PI - это деталь реализации, а getPi - общедоступное средствочтобы получить значение, вы должны написать test_getPi, как указано выше, а PI должно быть private без testPI модульного теста.Этот последний случай, вероятно, больше похож на то, что вы должны делать.

Полагаю, у вас может быть третий тест, подобный следующему:

test_getPi_PI_AlwaysAgree() {
   AssertEquals(getPi(), PI);
}

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

Обратите внимание, что этот последний тест не говорит о том, что getPi должен возвращать 3.14, только то, что два фрагмента кода должны иметь одинаковое значение, без указания того, что это за значение.Чтобы утверждать, что значение одного или другого должно быть некоторым конкретным значением, используйте один из первых двух тестов.

2 голосов
/ 29 июля 2010

Я думаю, что здесь есть два разных случая:

  1. Если вы тестируете константу, критическую для результата вычисления, как в вашем примере, я думаю, что лучшеиспользуйте независимый тест, а не повторно используйте ту же самую константу из кода, который вы пытаетесь протестировать.Вместо непосредственного тестирования значения константы, я бы протестировал (например) функцию CalculateAreaOfCircle (), которая проверяет правильность формулы Площадь и в то же время проверяет значение PI.

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

1 голос
/ 31 июля 2010

Я использую первую форму - даже если она дублирует значение (только дважды), она более читаема.

[Test]
public void GetPIReturns3_14()
{
  Assert.AreEqual(3.14, testSubject.GetPI());
}

Дублирование необходимо, потому что если вы повторно используете константу, как во втором тесте, вы на самом деле ничего не тестируете.По сути, вы тестируете «Является ли константа == константой?».Этот тест никогда не провалится.например, если я изменю PI на 1010.1010, второй тест не будет неудачным.

1 голос
/ 29 июля 2010

Определенно вторая форма, цель константы - не вводить "магическое число". Теперь, в модульных тестах, вы склонны использовать магические числа довольно часто, и это нормально.

В этом случае вы ввели константу И использовали магическое число. Я бы либо использовал константу везде, либо не использовал бы ее вообще.

...