часть его методов недоступна извне. В этом случае тестировщик лишен возможности
обращаться в своих тестах к данным класса и произвольным образом вызывать методы;
единственное, что ему доступно – вызывать методы внешнего интерфейса класса.
Существует несколько подходов к тестированию классов, каждый из них накладывает
свои ограничения на структуру драйвера и заглушек.
1.
Драйвер создает один или больше объектов тестируемого класса, все обращения
к объектам происходят только с использованием их внешнего интерфейса. Текст драйвера в
этом случае представляет собой т.н. тестирующий класс, который содержит по одному методу
для каждого тестового примера. Процесс тестирования заключается в последовательном
вызове этих методов. Вместо заглушек в состав тестового окружения входит программный
код реальной системы, соответственно, отсутствует изоляция тестируемого класса. Именно
такой подход к тестированию принят сейчас в большинстве методологий и сред разработки.
Его классическое название – unit testing (тестирование модулей).
2.
Аналогично предыдущему подходу, но для всех классов, которые использует
тестируемый класс, создаются заглушки
3.
Программный код тестируемого класса модифицируется таким образом, чтобы
открыть доступ ко всем его свойствам и методам. Строение тестового окружения в этом случае
полностью аналогично окружению для тестирования структурных программ.
4.
Используются специальные средства доступа к закрытым данным и методам
класса на уровне объектного или исполняемого кода – скрипты отладчика или accessors в
Visual Studio.
Основное достоинство первых двух методов – при их использовании класс работает
точно таким же образом, как в реальной системе. Однако в этом случае нельзя гарантировать
того, что в процессе тестирования будет выполнен весь программный код класса и не
останется непротестированных методов.
Основной недостаток 3-го метода – после изменения исходных текстов тестируемого
модуля нельзя дать гарантии того, что класс будет вести себя таким же образом, как и
исходный. В частности, это связано с тем, что изменение защиты данных класса влияет на
наследование данных и методов другими классами.
Тестирование наследования – отдельная сложная задача в объектно-ориентированных
системах. После того, как протестирован базовый класс, необходимо тестировать классы-
потомки. Однако, для базового класса нельзя создавать заглушки, т.к. в этом случае можно
пропустить возможные проблемы полиморфизма. Если класс-потомок использует методы
базового класса для обработки собственных данных, необходимо убедиться в том, что эти
методы работают.
Таким образом, иерархия классов может тестироваться сверху вниз, начиная от базового
класса. Тестовое окружение при этом может меняться для каждой тестируемой конфигурации
классов.
54
На примере "Калькулятора"
Тесты, проделанные нами на прошлом семинаре, как правило, проводятся не вручную.
Для целей тестирования пишут специальную программу — тестовый драйвер, который и
проводит тестирование. Более того, такие программы часто пишутся на другом языке, нежели
тестируемая программа, или создаются автоматически, с помощью специальных утилит.
На этом семинаре мы сами напишем простой тестовый драйвер на C# для тестирования
функций "Калькулятора", используя спецификацию второго семинара.
Замечание. Код программы слегка изменен для упрощения компиляции отдельных
модулей. Так, исключена работа с переменной Program.res, а класс CalcClass объявлен
как public.
Для начала рассмотрим функцию деления. Тест-требования к ней мы уже составили. Для
простоты будем пользоваться лишь четырьмя общими тест- требованиями.
1.
Оба входных параметра принадлежат допустимой области, и выходное значение
принадлежит допустимой области.
2.
Первый входной параметр принадлежит допустимой области, второй не
принадлежит допустимой области
3.
Первый входной параметр не принадлежит допустимой области, второй
принадлежит допустимой области
4.
Оба входных параметров принадлежат допустимой области, а значение функции
не принадлежит допустимой области.
Составим программу:
private void buttonStartDel_Click(object sender, EventArgs e)
{
try
{
richTextBox1.Text = "";
richTextBox1.Text += "Test Case 1\n";
richTextBox1.Text += "Входные данные: a= 78508, b = -304\n";
richTextBox1.Text += "Ожидаемый результат: res = 78204 &&
error = \"\""+"\n";
int res = CalcClass.Add(78508, -304);
string error = CalcClass.lastError;
richTextBox1.Text += "Код ошибки: " + error + "\n";
richTextBox1.Text += "Получившийся результат: " +"res = "+
res.ToString() +" error = "+error.ToString() +"\n";
if (res == 78204 && error == "")
{
richTextBox1.Text += "Тест пройден\n\n";
}
else
{
richTextBox1.Text += "Тест не пройден\n\n";
}
}
catch (Exception ex)
{
richTextBox1.Text += "Перехвачено исключение: " +
ex.ToString() + "\nТест не пройден.\n";
}
try
{
richTextBox1.Text += "Test Case 2\n";
55
richTextBox1.Text += "Входные данные: a= -2850800078, b =
3000000000\n";
richTextBox1.Text += "Ожидаемый результат: res = 0 && error =
\"Error 06\"\n";
int res = CalcClass.Add(-2850800078, 3000000000);
string error = CalcClass.lastError;
richTextBox1.Text += "Код ошибки: " + error + "\n";
richTextBox1.Text += "Получившийся результат: " + "res = " +
res.ToString() + " error = " + error.ToString() + "\n";
if (res == 0 && error == "Error 06")
{
richTextBox1.Text += "Тест пройден\n\n";
}
else
{
richTextBox1.Text += "Тест не пройден\n\n";
}
}
catch (Exception ex)
{
richTextBox1.Text += "Перехвачено исключение: " +
ex.ToString() + "\nТест не пройден.\n";
}
try
{
richTextBox1.Text += "Test Case 3\n";
richTextBox1.Text += "Входные данные: a= 3000000000, b = -
2850800078\n";
richTextBox1.Text += "Ожидаемый результат: res = 0 && error =
\"Error 06\"\n";
int res = CalcClass.Add(3000000000, -2850800078);
string error = CalcClass.lastError;
richTextBox1.Text += "Код ошибки: " + error+"\n";
richTextBox1.Text += "Получившийся результат: " + "res = " +
res.ToString() + " error = " + error.ToString() + "\n";
if (res == 0 && error == "Error 06")
{
richTextBox1.Text += "Тест пройден\n\n";
}
else
{
richTextBox1.Text += "Тест не пройден\n\n";
}
}
catch (Exception ex)
{
richTextBox1.Text += "Перехвачено исключение: " +
ex.ToString() + "\nТест не пройден.\n";
}
try
{
56
richTextBox1.Text += "Test Case 4\n";
richTextBox1.Text += "Входные данные: a= 2000000000, b =
2000000000\n";
richTextBox1.Text += "Ожидаемый результат: res = 0 && error =
\"Error 06\"\n";
int res = CalcClass.Add(2000000000, 2000000000);
string error = CalcClass.lastError;
richTextBox1.Text += "Код ошибки: " + error +"\n";
richTextBox1.Text += "Получившийся результат: " + "res = " +
res.ToString() + " error = " + error.ToString() + "\n";
if (res == 0 && error == "Error 06")
{
richTextBox1.Text += "Тест пройден\n\n";
}
else
{
richTextBox1.Text += "Тест не пройден\n\n";
}
}
catch (Exception ex)
{
richTextBox1.Text += "Перехвачено исключение: " +
ex.ToString() + "\nТест не пройден.\n";
}
}
Листинг. Текст программы
Каждый тестовый пример находится внутри блока try-catch для того, чтобы перехватить
любое сгенерированное исключение внутри методов Add().
При этом файл CalcClass.dll, в котором и реализованы все математические методы,
необходимо добавить в References проекта.
Проведем тестирование и получим следующий результат:
Test Case 1
Входные данные: a= 78508, b = -304
Ожидаемый результат: res = 78204 && error = ""
Код ошибки:
Получившийся результат: res = 78204 error =
Тест пройден
Test Case 2
Входные данные: a= -2850800078, b = 3000000000
Ожидаемый результат: res = 0 && error = "Error 06"
Код ошибки: Error 06
Получившийся результат: res = 0 error = Error 06
Тест пройден
Test Case 3
Входные данные: a= 3000000000, b = -2850800078
Ожидаемый результат: res = 0 && error = "Error 06"
Код ошибки: Error 06
Получившийся результат: res = 0 error = Error 06
Тест пройден
Test Case 4
Входные данные: a= 2000000000, b = 2000000000
Ожидаемый результат: res = 0 && error = "Error 06"
57
Код ошибки: Error 06
Получившийся результат: res = 0 error = Error 06
Тест пройден
Точно такой же результат мы бы получили и при ручном тестировании, если бы
выявленные ошибки были исправлены. Заметим, что при таком подходе к тестированию нам
удается локализовать ошибки. Если что-то работает не так, как надо, то можно с уверенностью
утверждать, что ошибка содержится именно в функции деления, в то время, как на прошлом
семинаре мы не могли сказать, где именно она произошла.
Замечание. Мы считаем, что тестовый драйвер сам не содержит ошибок. Тестирование
тестового драйвера выходит за пределы изучаемой темы.
Раздаточный материал
Программа
Будут выданы .dll файлы, которые нужно протестировать методом "черного ящика", и
пример тестового драйвера.
Составить тест-план и провести модульное тестирование следующих методов:
Нахождение остатка.
///
/// Деление по модулю
///
///
делимое
///
делитель
/// остаток
public static int Mod(long a, long b)
Унарный плюс.
///
/// унарный плюс
///
///
///
public static int ABS(long a)
Унарный минус.
///
/// унарный минус
///
///
///
public static int IABS(long a)
Вычитание.
///
/// вычитание
///
///
уменьшаемое
///
вычитаемое
/// разность
public static int Sub(long a, long b)
Умножение.
///
/// умножение
///
///
множитель
///
множитель
/// произведение
public static int Mult(long a, long b)
58
Деление.
///
/// частное
///
///
делимое
///
делитель
/// частное
public static int Div(long a, long b)
Достарыңызбен бөлісу: |