LINUX.ORG.RU

Юнит-тесты. Как вы определяете полноту тестируемости?


1

1

Например, есть функция multiply, которая по идее должна бы перемножать два аргумента. Выглядит вот так:

def multiply(a, b):
  if a == b and a == 2:
    return 4
  elif a == b and a == 3:
    return 9

И есть тест на нее, который еще и будет пройден:

assert(multiply(2, 2) == 4)
assert(multiply(3, 3) == 9)

Но, конечно же, за такую функцию умножения надо по шее давать.

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

★★★★★

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

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

Artem_Korneev
()
Ответ на: комментарий от Artem_Korneev

> тест был написан:
> - после написания функции

> - с оглядкой на написанный код функции


Почему же? Нет.

>Также неплохо бы писать сначала тест, а потом уже саму реализацию функции.


Так и было, я гарантирую это.

Score-49
()

Этот код являет собой отличный пример test-driven development 
(http://en.wikipedia.org/wiki/Test-driven_development).

Это когда код пишется после написания тестов. 
И коректируется, если новый тест не проходит.

К примеру дальнейшее написание кода может быть следующим:

Во время финального тестирования продукта тестер ввел a=4, b=4. 
Всё упало и программер пишет тест:

assert(multiply(4, 4) == 16)

def multiply(a, b):
  if a == b and a == 2:
    return 4
  elif a == b and a == 3:
    return 9
  elif a == b and a == 4:
    return 16

Но сразу видно, что multiply это же возведение в квадрат. 
Соответственно код рефакторится:

def multiply(a, b):
    return a * a

Далее (после того как проэкт поступил в продажу, на данных 
пользователя обнаруживается баг при входящих 
данных a = 2, b = 5)

assert(multiply(2, 5) == 10)

Тест падает - так как 2 * 5 != 4

И писатель кода догадывается, что надо написать не a ^ a, а 

def multiply(a, b):
    return a * b

Задача решена=)

stpg
()
Ответ на: комментарий от tailgunner

>Unit-тест как раз надо писать, зная внутренности.

и glass box, и black box применяются к unit, ограничений вроде нет )

stave ★★★★★
()

Цитирую дословно: "Even if 100 percent test coverage were possible, that is not a sufficient criterion for testing. Roughly 35 percent of software defects emerge from missing logic paths, and another 40 percent from the execution of a unique combination of logic paths. They will not be caught by 100 percent coverage.", Robert Glass, Facts & Fallacies of Software Engineering.

Супер книга, кстати :-)

smh ★★★
()
Ответ на: комментарий от stave

>> Unit-тест как раз надо писать, зная внутренности.

> и glass box, и black box применяются к unit, ограничений вроде нет )

Незачем себя ограничивать, конечно. Но black box'ы напишут и те, кто не понимает реализацию. Так что прогеру лучше сосредоточиться на glass.

tailgunner ★★★★★
()

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

Deleted
()

Если для языка есть code path coverage tool (типа gcov для gcc), то можно такие юнит-тесты писать, чтобы все варианты отрабатывались.

mv ★★★★★
()

А может кто подкинет пару названий книг по тестированию на русском или английском?

dimon555 ★★★★★
()

1. на с/с++/еще ряде языков тебе компилятор даст варнинг или вообще не пропустит, т.к. у тебя есть случай, когда не возвращается ничего

2. если ты его явно пропишешь (else return 0 хотя бы), то ты сможешь уже юзать code coverage tool, т.е. проверить, что тесты прошли по всем веточкам кода.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от cruz7

> Таки unit-тесты проверяют имплементацию или интерфейс, т.е. API?

Здесь дихотомия не API/реализация, а black box/glass box. Любой unit-тест, например, функции, проверяет ее API.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Здесь дихотомия не API/реализация, а black box/glass box. Любой unit-тест, например, функции, проверяет ее API

Т.е. тесто-писателю не обязательно знать имплементацию ф-ции, для которой пишется тест?

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

cruz7 ★★
()
Ответ на: комментарий от cruz7

>> Здесь дихотомия не API/реализация, а black box/glass box. Любой unit-тест, например, функции, проверяет ее API

> Т.е. тесто-писателю не обязательно знать имплементацию ф-ции, для которой пишется тест?

Для blackbox - не обязательно (или даже вредно), для galssbox - необходимо.

> может быть порекомендуете толковый букварь по юнит-тестам

Да я сам не великий специалист в теории, которая именно за unit-тестами. ИМХО, unit-тесты - это скорее часть методологии разработки, чем тестирования. Ноги у этого растут из XP, так что Бек, "Экстремальное программирование". Что-нибудь по методологии тестирования тоже не помешает, но тут ХЗ... я когда-то учился по Майерс "Исскуство тестирования программ".

tailgunner ★★★★★
()

При написании функции нужно видеть все её критические диапазоны.

Если сразу не видно - функцию нужно рефакторить на более простые и понятные.

При тестировании нужно проверить каждый критический диапазон снаружи, внутри и точно по значению.

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

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

>При написании функции нужно видеть все её критические диапазоны.

>Если сразу не видно - функцию нужно рефакторить на более простые и понятные.

>При тестировании нужно проверить каждый критический диапазон снаружи, внутри и точно по значению.

И это все делается одним и тем же человеком, или обязанности тестировщика на отдельном инженере? Это же получается целая хитрая и трудоемкая наука!

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

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

cruz7 ★★
()
Ответ на: комментарий от cruz7

>Это же получается целая хитрая и трудоемкая наука!

Именно так. Тестирование - это целая наука. Я как-то админил ребят, которые занимались тестированием UML-моделей для Honeywell Solutions. Целый отдел, человек в 20 занимался одним тестированием. От качества их тестирования зависело то, насколько безопасно завтра будет летать на Боингах и Эйрбасах :) До этого тоже думал, что тестирование - это так себе... :)

>а как быть например с функциями, возвращающие указатель на IP пакет?


Тестирование в общем случае задача точно не решаемая. Как с указателями быть - не задумывался. Когда программировал на Си/Си++ о вопросах тестирования не задумывался, а когда стал задумываться о тестировании - к тому времени использовал уже языки без указателей :)

KRoN73 ★★★★★
()
Ответ на: комментарий от mv

>Если для языка есть code path coverage tool (типа gcov для gcc), то можно такие юнит-тесты писать, чтобы все варианты отрабатывались.

Да-да, lcov (http://ltp.sourceforge.net/coverage/lcov.php) как раз в этом помогает.

php-coder ★★★★★
()
Ответ на: комментарий от tailgunner

Странно, что присутствующие здесь любители ФП не упомянули program reasoning :))) И не кинули какашкой (как обычно) в юниты.

cathode
()
Ответ на: комментарий от stpg

Ну хоть один человек доходчиво описал, как TDD работает на практике.

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

shimon ★★★★★
() автор топика
Ответ на: комментарий от cathode

> Странно, что присутствующие здесь любители ФП не упомянули program reasoning :))) И не кинули какашкой (как обычно) в юниты.

Moar?

shimon ★★★★★
() автор топика
Ответ на: комментарий от cathode

>Странно, что присутствующие здесь любители ФП не упомянули program reasoning :))) И не кинули какашкой (как обычно) в юниты.

это всё оттого что мы добрые. и вежливые

jtootf ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

>А еще оттого, что юнит-тесты в несколько раз короче.

и бесполезней ;)

jtootf ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.