Есть ли смысл в написании модульного теста, который является подмножеством другого теста?

15

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

def test_length():
    result = my_function()
    assert len(result) == 2

def test_order()
    a, b = my_function()
    assert a < b

Здесь, если test_lengthне test_orderполучится , то тоже не получится. Лучше написать test_lengthили пропустить?

РЕДАКТИРОВАТЬ: обратите внимание, что в этой ситуации оба теста в основном независимы друг от друга, каждый может быть запущен в отдельности, или они могут быть запущены в обратном порядке, это не имеет значения. Так что ни один из этих бывших вопросов

является дубликатом вышеупомянутого.

Михай
источник
2
@ GlenH7 - это похоже на другой вопрос. Другой - «если у вас есть функции, которые Aвызывают Bи возвращают один и тот же результат, вы должны проверить оба Aи B». Это больше о тестах, которые перекрываются, а не о тестируемых функциях. (Хотя это сбивает с толку, как они в настоящее время названы).
Теластин
1
Строго говоря, эти тесты на самом деле не перекрываются (они не зависят от успеха). Функция lambda: type('', (), {'__len__': lambda self: 2})()пройдет первое, но не второе.
Лиори
1
@ GlenH7: FWIW, я действительно не изменил вопрос, просто попытался сделать его безопасным для комара ;-) (@gnat: без обид, просто шучу!)
Док Браун

Ответы:

28

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

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

Telastyn
источник
Я проголосовал за ваш ответ, хотя в нем пропущен один незначительный момент. В Python второй тест также завершится неудачно, когда my_functionне вернет ровно два значения, без какого-либо утверждения - потому что назначение приведет к исключению. Таким образом, на самом деле нет необходимости использовать несколько утверждений и две проверки для проверки одного и того же.
Док Браун
@DocBrown - я так и предполагал, но я не знаком с сообщениями об ошибках Python, чтобы знать, может ли быть смысл иметь ошибку подтверждения вместо случайного исключения / ошибки по информативным причинам.
Теластин
Я думаю, что в текущей ситуации сообщение об ошибке, вероятно, будет достаточно информативным. Но в целом это может быть не так. Вот почему я согласен с вами, что общий подход для такого случая должен состоять в том, чтобы думать о «слиянии», используя два утверждения. Если окажется, что тест можно еще больше упростить, пропустив одно из утверждений, прекрасно.
Док Браун
5
@DocBrown Другое значение при явной проверке с помощью assert заключается в том, что любому, проверяющему тест, становится ясно, что это сбой тестируемого кода, а не сам тест. Когда мои тесты сталкиваются с неожиданными исключениями, мне всегда приходится тратить некоторое время на выяснение того, какой из них на самом деле неправильный.
Крис Хейс
@ChrisHayes: я написал «в этом нет необходимости», а не «в этом нет никакой ценности».
Док Браун
5

Ваши тесты должны быть явными. Это не совсем вывод, что если text_length терпит неудачу, test_order терпит неудачу.

Я не уверен, как обстоят дела в Python, который вы опубликовали, но если len(result)3, то первое не получится, но второе может пройти (и если не в Python, то в таких языках, как JavaScript, конечно).

Как вы сказали, вы хотите проверить, что функция возвращает два числа и что они в порядке. Два теста это так.

Призрак Мадары
источник
1
It's not completely inferred that if text_length fails test_order fails- в Python это даст вам исключение.
Док Браун
Я думаю, что речь идет о случае, когда отказ test_lengthподразумевает отказ test_order. Тот факт, что подобная пара тестов, написанных на Javascript, не будет вести себя так же, как эти два теста на python, не имеет никакого значения.
Дауд говорит восстановить Монику
2
@DavidWallace: помните, вопрос был "Лучше всего писать test_length или пропускать его"? В Javascript вам нужны оба теста, чтобы убедиться, что вы не пропустили проблему (если вы не объединяете два теста в один). В Python вы можете спокойно пропустить первое (что отрицается в первом предложении этого ответа и объявляется как «я не уверен» во втором). Честно говоря, хороший ответ выглядит иначе. Тем не менее, я не стал опускать этот ответ, так как хорошая часть - это упоминание JavaScript.
Док Браун
4
@DavidWallace: поскольку мы находимся здесь на сайте programmers.com, а не на stackoverflow.com, ИМХО давать ответ, который может быть применен к более чем одному языку, лучше, чем ответ только для определенного языка. Но при обращении к определенному языку ответ должен быть правильным относительно языка, а не путать некоторые свойства.
Док Браун
1
Дело в том, что вопрос «стоит ли писать тест, который является подмножеством другого теста», и этот ответ говорит: «В некоторых языках ни один из ваших тестов не является подмножеством другого», а затем приходит к выводу, что оба теста поэтому необходимо. Мне кажется, что кто-то сказал: «Ваш код вообще не компилируется в C ++, и, кроме того, в C ++ функция может возвращать только одно значение, поэтому вам не нужно проверять, что оно возвращает два. Только написать один тест» ;-)
Стив Джессоп
2

Единственное значение test_lengthздесь состоит в том, что, если все проходит, само его присутствие указывает, что «длина» была проверена.

Так что на самом деле нет необходимости иметь оба этих теста. Оставьте только, test_orderно подумайте о переименовании test_length_and_order.

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

Дауд говорит восстановить Монику
источник
1
testБородавка имеет смысл тестовой структуры Python. Что, конечно, не должно меняться, если вы фанат этого, но это меняет вес аргумента, необходимого, чтобы остановить кого-то, использующего его ;-)
Стив Джессоп
@ SteveJessop Да, я постоянно забываю, что это нужно. Когда я писал тесты Python некоторое время назад, у меня появилась привычка начинать их все test_that_, как test_that_return_values_are_in_orderи так далее. Возможно, будущая версия тестового фреймворка как-то обойдет это требование.
Дауд говорит восстановить Монику
@DavidWallace: вы можете изменить префикс, если хотите.
Ли Райан
1

Я бы оставил эти тесты отдельно, если бы они так развивались. Да test_order, скорее всего, потерпит неудачу test_length, но вы определенно знаете, почему.

Я также согласен с тем, что, если он test_orderпоявился первым, и вы обнаружили, что тестируемый сбой состоял в том, что результатом, возможно, были не два значения, добавьте эту проверку как assertи переименуйте тест, чтобы иметь test_length_and_orderсмысл.

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

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

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

Поэтому я бы, вероятно, начал с ваших отдельных тестов, а затем расширил test_lengthбы test_length_and_typesи оставил test_orderотдельные, предполагая, что последний считается "нормальной обработкой".

Марк Херд
источник