Часть 2: Тестирование простого приложения (Тестирование ПО)

09 Apr 2017

Оглавление

Первое приложение

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

Напишем, функцию, которая принимает на вход три стороны треугольника, которые заданы целыми числами и возвращает тип треугольника. Сохраним написанный код в файле triangle.php.

/\*\*  
 \* Не треугольник  
 \*/  
define('TRIANGLE\_BAD', 0);  
/\*\*  
 \* Равносторонний треугольник  
 \*/  
define('TRIANGLE\_EQUILATERAL', 1);  
/\*\*  
 \* Прямоугольный треугольник  
 \*/  
define('TRIANGLE\_RIGHT', 2);  
/\*\*  
 \* Равнобедренный треугольник  
 \*/  
define('TRIANGLE\_ISOSCELES', 3);  
/\*\*  
 \* Разносторонний треугольник  
 \*/  
define('TRIANGLE\_SIDED', 4);

/\*\*  
 \* По длинам сторон $a, $b и $c возвращает тип треугольника.  
 \* Если стороны не являются целочисленными, то выбрасывает исключение.  
 \*  
 \* @param $a  
 \* @param $b  
 \* @param $c  
 \* @return int  
 \* @throws Exception  
 \*/  
function triangle\_type($a, $b, $c)  
{  
 // Вполне ожидаемо,  
 // что нецелочисленные значения должны приводить к исключительной ситуации.  
 if (!is\_int($a) or !is\_int($b) or !is\_int($c))  
 {  
 throw new \Exception('Invalid triangle definition');  
 }

$max = null;  
 $min1 = null;  
 $min2 = null;

if (($a+$b)\>$c and ($a+$c)\>$b and ($b+$c)\>$a)  
 {  
 if (($a\>$b) and ($a\>$c))  
 {  
 $max = $a;  
 $min1 = $b;  
 $min2 = $c;  
 }  
 else if (($b\>$c) and ($b\>$a))  
 {  
 $max = $b;  
 $min1 = $a;  
 $min2 = $c;  
 }  
 else  
 {  
 $max = $c;  
 $min1 = $a;  
 $min2 = $b;  
 }

if (pow($max, 2) == pow($min1, 2) + pow($min2, 2))  
 {  
 return TRIANGLE\_RIGHT;  
 }  
 else if (($max==$min1) and ($max==$min2))  
 {  
 return TRIANGLE\_EQUILATERAL;  
 }  
 else if (($max==$min1) or ($max==$min2) or ($min1==$min2))  
 {  
 return TRIANGLE\_ISOSCELES;  
 }  
 else  
 {  
 return TRIANGLE\_SIDED;  
 }  
 }  
 else  
 {  
 return TRIANGLE\_BAD;  
 }  
}

Функция достаточно тривиальна, поэтому мы не будем останавливаться на ее реализации. Нас будет интересовать, как найти в ней ошибки.

Для начала потребуется реализовать механизм, который позволит вводить данные с консоли и получать результат. Сохраним следующий код в файле main.php. Чуть позже вы поймете, почему мы используем разные файлы для самой функции и для кода, который обрабатывает пользовательский ввод.

// здесь мы подключим ранее написанную функцию для определения типа треугольника  
require \_\_DIR\_\_ . DIRECTORY\_SEPARATOR . 'triangle.php';

function main()  
{  
 // проинициализируем переменные  
 $a = $b = $c = 0;

// получим длины сторон со стандартного ввода  
 $num = fscanf(STDIN, "%d %d %d\n", $a, $b, $c);  
 // если мы смогли считать длины трех сторон,  
 // то вызовем нашу функцию и покажем результат  
 if ($num == 3)  
 {  
 switch (triangle\_type($a, $b, $c))  
 {  
 case TRIANGLE\_BAD:  
 echo "Это не треугольник\n";  
 break;  
 case TRIANGLE\_EQUILATERAL:  
 echo "Это равносторонний треугольник\n";  
 break;  
 case TRIANGLE\_ISOSCELES:  
 echo "Это равнобедренный треугольник\n";  
 break;  
 case TRIANGLE\_RIGHT:  
 echo "Это прямоугольный треугольник\n";  
 break;  
 case TRIANGLE\_SIDED:  
 echo "Это разносторонний треугольник\n";  
 break;  
 }  
 }  
}

main();

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

Откроем терминал, перейдем в каталог, с проектом и выполним следующую команду (для того, чтобы все сработало у вас должен быть установлен интерпретатор php в системе).

$ php main.php

Программа будет ожидать ввод трех чисел, разделенных пробелами.

И вот что мы можем увидеть на экране.

null

Поэкспериментируйте немного с программой вводя разные наборы чисел.

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

Итак. Ниже приведен набор тестовых сценариев, которые должны быть написаны для нашей функции.

  1. тест для проверки действительно неравностороннего треугольника (наборы [1, 2, 3], [2, 5, 10] треугольниками не являются).
  2. проверка на действительно равносторонний треугольник
  3. проверка на равнобедренный треугольник (наборы вида [2, 2, 4] треугольником не являются)
  4. как минимум три теста для проверки равнобедренного треугольника, которые представляют собой перестановки одного и того же набора чисел ([3, 3, 4], [3, 4, 3], [4, 3, 3])
  5. тест на нулевую длину одной из сторон
  6. тест на сторону, имеющую длину меньше нуля
  7. проверка набора чисел, в котором сумма длин двух сторон равна третьей
  8. тест перестановок для троек чисел из теста 7
  9. проверка набора чисел, в котором сумма длин двух сторон меньше третьей ([12, 15, 30])
  10. тест перестановок для троек чисел из теста 9
  11. проверка на нулевую длину всех трех сторон
  12. проверка на передачу нецелочисленных значений
  13. проверка на передачу неполного набора значений
  14. проверка не только входных данных, но и ожидаемого выходного значения в каждом из тестов 1-13

Если вы не смогли назвать все кейсы, то не пугайтесь. Среднее число тестов, которые называли в разное время опытные разработчики составило 7,8.

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

Это упражнение должно было продемонстрировать вам, что тестирование простых программ наподобие вышеприведенной является отнюдь не тривиальной задачей. А теперь попытайтесь представить себе, насколько трудоемким окажется тестирование, скажем, бухгалтерской программы крупного предприятия, компилятора или же системы управления воздушным движением, объем кода которых может достигать сотен тысяч строк. Еще большие трудности возникают с приложениями, которые написаны с использованием объектно-ориентированных языков (куда входит и php) и подходов. В частности, тесты для подобных приложений должны выявлять ошибки с созданием экземпляров объектов и взаимодействия между ними.

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

Тестируем

Конечно же самым простым решением будет просто закодировать все тестовые случаи для нашего проекта и написать нечто вроде следующего кода (файл triangle_test_simple.php).

require \_\_DIR\_\_ . DIRECTORY\_SEPARATOR . 'triangle.php';

function testForIsoscelesTriangle()  
{  
 echo "Test for [3, 4, 4]: ";  
 if (triangle\_type(3, 4, 4) == TRIANGLE\_ISOSCELES) {  
 echo "ok\n";  
 } else {  
 echo "fail\n";  
 }  
}

function main()  
{  
 testForIsoscelesTriangle();  
}

main();

И такое часто практикуется. Особенно в среде разработчиков на C\C++. На каждый логически связанный набор тестовых случаев создается свой файл. Который содержит множество функций обрабатывающих по одному сценарию каждая.

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

Один из вариантов создания инструмента для работы с подобными тестами вы можете увидеть в файле triangle_test.php. Запустите его и увидите на экране подробный лог тестирования проекта.

null

Литература

Исходные тексты программ

Оглавление