Практическая работа № 2.21. Интеграционное тестирование в MVSTE
97
Цель работы: провести интеграционное тестирование в MVSTE
Интеграционное тестирование
Задачи и цели интеграционного тестирования
Результатом тестирования и верификации отдельных модулей, составляющих
программную систему, является заключение о том, что эти модули являются внутренне
непротиворечивыми и соответствуют требованиям. Однако отдельные модули редко
функционируют сами по себе, поэтому следующая задача после тестирования отдельных
модулей – тестирование корректности взаимодействия нескольких модулей, объединенных в
единое целое. Такое тестирование называют интеграционным. Его цель – удостовериться в
корректности совместной работы компонент системы.
Интеграционное тестирование называют еще тестированием архитектуры системы. С
одной стороны, это название объясняется тем, что, интеграционные тесты включают в себя
проверки всех возможных видов взаимодействий между программными модулями и
элементами, которые определяются в архитектуре системы, – таким образом, интеграционные
тесты проверяют полноту взаимодействий в тестируемой реализации системы. С другой
стороны, результаты выполнения интеграционных тестов – один из основных источников
информации для процесса улучшения и уточнения архитектуры системы, межмодульных и
межкомпонентных интерфейсов. Т.е. с этой точки зрения интеграционные тесты проверяют
корректность взаимодействия компонент системы.
В результате проведения интеграционного тестирования и устранения всех выявленных
дефектов получается согласованная и целостная архитектура программной системы, т.е.
можно считать, что интеграционное тестирование – это тестирование архитектуры и
низкоуровневых функциональных требований.
Интеграционное тестирование, как правило, представляет собой итеративный процесс,
при котором проверяется функциональность все более и более увеличивающейся в размерах
совокупности модулей.
Задачи и цели интеграционного тестирования
Структурная классификация методов интеграционного тестирования
Как правило, интеграционное тестирование проводится уже по завершении модульного
тестирования для всех интегрируемых модулей. Однако это далеко не всегда так. Существует
несколько методов проведения интеграционного тестирования:
восходящее тестирование;
монолитное тестирование;
нисходящее тестирование;
Восходящее тестирование. При использовании этого метода подразумевается, что
сначала тестируются все программные модули, входящие в состав системы, и только затем
они объединяются для интеграционного тестирования. При таком подходе значительно
упрощается локализация ошибок: если модули протестированы по отдельности, то ошибка
при их совместной работе есть проблема их интерфейса. Тогда область поиска проблем у
тестировщика становится достаточно узкой, а поэтому гораздо выше вероятность правильно
идентифицировать дефект.
Однако у восходящего метода тестирования есть существенный недостаток –
необходимость в разработке драйвера и заглушек для модульного тестирования перед
проведением интеграционного тестирования и необходимость в разработке драйвера и
заглушек при интеграционном тестировании части модулей системы.
С одной стороны, драйверы и заглушки – мощный инструмент тестирования, с другой –
их разработка требует значительных ресурсов, особенно при изменении состава
интегрируемых модулей. Т.е. может потребоваться один набор драйверов для модульного
тестирования каждого модуля, отдельный драйвер и заглушки для тестирования интеграции
двух модулей из набора, отдельный – для тестирования интеграции трех модулей и т.п. В
первую очередь, причина в том, что при интеграции модулей отпадает необходимость в
некоторых заглушках, а также требуется изменение драйвера, которое будет поддерживать
новые тесты, затрагивающие несколько модулей.
98
Монолитное тестирование предполагает, что отдельные компоненты системы
серьезного тестирования не проходили. Основное преимущество данного метода – отсутствие
необходимости в разработке тестового окружения, драйверов и заглушек. После разработки
всех модулей выполняется их интеграция, затем система проверяется вся в целом, как она есть.
Этот подход не следует путать с системным тестированием. Несмотря на то, что при
монолитном тестировании проверяется работа всей системы в целом, основная задача этого
тестирования – определить проблемы взаимодействия отдельных модулей системы. Задачей
же системного тестирования является оценка качественных и количественных характеристик
системы с точки зрения их приемлемости для конечного пользователя.
Тем не менее, монолитное тестирование имеет ряд серьезных недостатков:
очень трудно выявить источник ошибки (идентифицировать ошибочный фрагмент
кода);
трудно организовать исправление ошибок;
процесс тестирования плохо автоматизируется.
Нисходящее тестирование предполагает, что процесс интеграционного тестирования
движется следом за разработкой. Сначала при нисходящем подходе тестируют только самый
верхний управляющий уровень системы, без модулей более низкого уровня. Затем постепенно
с более высокоуровневыми модулями интегрируются более низкоуровневые. В результате
применения такого метода отпадает необходимость в драйверах (роль драйвера выполняет
более высокоуровневый модуль системы), однако сохраняется нужда в заглушках.
Временная классификация методов интеграционного тестирования
На практике чаще всего в различных частях проекта применяются все рассмотренные в
предыдущем разделе методы в совокупности. Каждый модуль тестируют по мере готовности
отдельно, а потом включают в уже готовую композицию. Для одних частей тестирование
получается нисходящим, для других – восходящим. В связи с этим представляется полезным
рассмотреть еще один тип классификации типов интеграционного тестирования –
классификацию по частоте интеграции:
тестирование с поздней интеграцией;
тестирование с постоянной интеграцией;
тестирование с регулярной или послойной интеграцией.
Тестирование с поздней интеграцией – практически полный аналог монолитного
тестирования. Интеграционное тестирование при такой схеме откладывается на как можно
более поздние сроки проекта. Этот подход оправдывает себя в том случае, если система
представляет собой конгломерат слабо связанных между собой модулей, которые
взаимодействуют по какому-либо стандартному интерфейсу, определенному вне проекта
(например, в случае, если система состоит из отдельных Web-сервисов).
Схематично тестирование с поздней интеграцией может быть изображено в виде
цепочки R-C-V-R-C-V-R-C-V-I-R-C-V-R-C-V-I, где R – разработка требований на отдельный
модуль, C – разработка программного кода, V – тестирование модуля, I – интеграционное
тестирование всего, что было сделано раньше.
Тестирование с постоянной интеграцией подразумевает, что как только разрабатывается
новый модуль системы, он сразу же интегрируется со всей остальной системой. Тесты для
этого модуля проверяют как сугубо его внутреннюю функциональность, так и его
взаимодействие с остальными модулями системы. Таким образом, этот подход совмещает в
себе модульное тестирование и интеграционное. Разработки заглушек при таком подходе не
требуется, но может потребоваться разработка драйверов. В настоящее время именно этот
подход называют unit testing, несмотря на то, что в отличие от классического модульного
тестирования здесь не проверяется функциональность изолированного модуля. Локализация
ошибок межмодульных интерфейсов при таком подходе несколько затруднена, но все же
значительно ниже, чем при монолитном тестировании. Большая часть таких ошибок
выявляется достаточно рано именно за счет частоты интеграции и за счет того, что за одну
итерацию тестирования проверяется сравнительно небольшое число межмодульных интер
фейсов.
99
Схематично тестирование с постоянной интеграцией может быть изображено в виде
цепочки R-C-I-R-C-I-R-C-I, в которой фаза тестирования модуля намеренно опущена и
заменена на тестирование интеграции.
При тестировании с регулярной или послойной интеграцией интеграционному
тестированию подлежат сильно связанные между собой группы модулей (слои), которые
затем также интегрируются между собой. Такой вид интеграционного тестирования называют
также
иерархическим
интеграционным
тестированием,
поскольку
укрупнение
интегрированных частей системы, как правило, происходит по иерархическому принципу.
Однако, в отличие от нисходящего или восходящего тестирования, направление прохода по
иерархии в этом подходе не задано.
Таблица 18
Восходяще
е
Нисходяще
е
Монолитн
ое
Поздняя
интеграц
ия
Постоянная
интеграция
Регулярная
интеграция
Время
интеграци
и
поздно
(после
тестирован
ия
модулей)
рано
(параллель
но
с
разработко
й)
поздно
(после
разработки
всех
модулей)
поздно
(после
разработк
и
всех
модулей)
рано
(параллель
но
с
разработко
й)
рано
(параллель
но
с
разработко
й)
Частота
интеграци
и
Редко
часто
редко
редко
часто
часто
Нужны ли
драйверы
Да
нет
нет
нет
да
да
Нужны ли
заглушки
Да
да
нет
нет
нет
да
Таблица 18 представляет основные характеристики рассмотренных выше видов
интеграционного тестирования. Время интеграции характеризует момент времени, когда
проводится первое интеграционное тестирование и все последующие, частота интеграции –
насколько часто при разработке выполняется интеграция. Необходимость в драйверах и
заглушках определена в последних двух строках таблицы.
На примере "Калькулятора"
Как уже отмечалось, в MVSTE под unit-testing подразумевается именно интеграционное
тестирование, а конкретно — тестирование с постоянной интеграцией. В "Автоматизация
модульного тестирования" мы уже протестировали метод RunEstimate(), при интеграции
классов AnalaizerClass и CalcClass. Аналогично, составив требования к этой подсистеме из
двух классов (а это будут требования ко всем методам AnalaizerClass ), можно провести
следующий этап тестирования. В качестве примера протестируем все методы такой
подсистемы, сделав по одному тестовому примеру на каждый метод:
///
/// A test for RunEstimate ()
/// Проверяем, что, если в стеке находится корректное выражение, представленное
обратной польской записью, то
/// метод RunEstimate правильно посчитает это выражение
///
[DeploymentItem("BaseCalculator.exe")]
[TestMethod()]
public void RunEstimateTest()
{
string expected = "3";
string actual;
100
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.opz =
new ArrayList();
ArrayList _opz =
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.opz;
_opz.Add("7");
_opz.Add("8");
_opz.Add("+");
_opz.Add("5");
_opz.Add("/");
actual
=
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.RunEstimate();
Assert.AreEqual(expected, actual,
"BaseCalculator.AnalaizerClass.RunEstimate did not return the expected
value.");
}
///
///A test for CheckCurrency ()
/// Проверяет, что, если в выражении нарушена скобочная структура, то метод
возвращает false
///
[DeploymentItem("BaseCalculator.exe")]
[TestMethod()]
public void CheckCurrencyTest()
{
bool expected = false;
bool actual;
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.expression =
"((5+3)*2-(3*10))-2)+((5+3)";
actual
=
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.CheckCurrency();
Assert.AreEqual(expected, actual,
"BaseCalculator.AnalaizerClass.CheckCurrency did not return the expected
value.");
Assert.AreEqual(18,
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.erposition,
"BaseCalculator.AnalaizerClass.CheckCurrency did not return the expected
value.");
}
///
///A test for CreateStack ()
/// Проверяет, что если отформатированное выражение равно
///"( ( 5 + 3) * 2 - ( 3 * 10 ) ) - 2 + ( 5 + 3 ) ",
/// то стек содержит следующие элементы "5 3 + 2 * 3 10 * - 2 - 5 3 + + "
///
[DeploymentItem("BaseCalculator.exe")]
[TestMethod()]
public void CreateStackTest()
101
{
string expected = "5 3 + 2 * 3 10 * - 2 - 5 3 + + ";
ArrayList actual;
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.expression =
"( ( 5 + 3 ) * 2 - ( 3 * 10 ) ) - 2 + ( 5 + 3 ) ";
actual = TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.Create
Stack();
string actualstr = "";
foreach (object obj in actual)
{
actualstr += obj.ToString() + " ";
}
Assert.AreEqual(expected, actualstr,
"BaseCalculator.AnalaizerClass.CreateStack did not return the expected
value.");
}
///
///A test for Estimate ()
///Проверяет, что , если выражение , которое необходимо проверить,равно
///((5+3)*2-(3*10))-2+(5+3), то метод вернет его значение,
/// равное -8
///
[DeploymentItem("BaseCalculator.exe")]
[TestMethod()]
public void EstimateTest()
{
string expected = "-8";
string actual;
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.expression =
"((5+3)*2-(3*10))-2+(5+3)";
actual = TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.Estimate();
Assert.AreEqual(expected, actual,
"BaseCalculator.AnalaizerClass.Estimate did not return the expected value.");
}
///
///A test for Format ()
/// Проверяет, что если выражение равно "
/// ((5+3)*2-(3 *10))-2+ (5+3)", то метод отформатирует его к виду:
/// "( ( 5 + 3 ) * 2 - ( 3 * 10 ) ) - 2 + ( 5 + 3 ) "
///
[DeploymentItem("BaseCalculator.exe")]
[TestMethod()]
public void FormatTest()
{
102
string expected = "( ( 5 + 3 ) * 2 - ( 3 * 10 ) ) - 2 + ( 5 + 3 ) ";
string actual;
TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.expression =
"((5+3)*2-(3 *10))-2+ (5+3)";
actual = TestProjectCalculator.BaseCalculator_AnalaizerClassAccessor.Format();
Assert.AreEqual(expected, actual,
"BaseCalculator.AnalaizerClass.Format did not return the expected value.");
}
Запустив тесты, можно убедиться в корректной (для данных тестовых примеров) работе
методов.
Замечание. Тестовых требований, как и функциональных, к методам нет, однако их
можно составить по системным требованиям и описанию методов.
Как уже отмечалось в "Модульное тестирование" , эти методы обладают многими
недостатками, и некоторые из них генерируют исключения на некорректных входных данных.
Это, в принципе, логично, так как запуск метода RunEstimate() подразумевает, что входное
выражение уже обработано в методах CheckCurrency(), Format(), CreateStack(), а отдельно от
них этот метод вызываться не будет. Для этого необходимо сделать уровень доступа к ним
private и протестировать их только на корректных данных. Основное же внимание стоит
уделить методу Estimate(), который поочередно запускает все вышеперечисленные методы и
имеет уровень доступа public. Один из тестов для него приведен выше.
После того, как такая система из двух модулей протестирована, переходим к
тестированию всей системы, присоединив последние модули.
Замечание. Модуль BaseCalc, который отвечает за пользовательский интерфейс, мы не
тестируем, так как это выходит за рамки курса.
Теперь можно использовать системные требования, для тестирования программы в
целом. Проведем тесты для метода Main(string[] args), так как это единственный метод, не
считая уже протестированного Estimate(), который не относится к графическому интерфейсу
и с которым работают пользователи:
///
///A test for Main (string[])
/// Для чисел, каждое из которых меньше либо равно MAXINT и больше либо равно
MININT,
/// функция суммирования должна возвращать правильную сумму с точки зрения
математики
///
[DeploymentItem("BaseCalculator.exe")]
[TestMethod()]
public void MainTest()
{
string[] args = new string[1]; // TODO: Initialize to an appropriate value
args[0] = "2+2";
int expected = 0;
int actual;
actual = TestProjectCalculator.BaseCalculator_ProgramAccessor.Main(args);
Assert.AreEqual(expected, actual,
"BaseCalculator.Program.Main did not return the expected value.");
}
///
103
///A test for Main (string[])
/// Для чисел, сумма которых больше чем MAXINT и меньше чем MININT,
/// а также в случае, если любое из слагаемых больше чем MAXINT или меньше чем
MININT,
/// программа должна выдавать ошибку Error 06
///
[DeploymentItem("BaseCalculator.exe")]
[TestMethod()]
public void MainTest1()
{
string[] args = new string[1]; // TODO: Initialize to an appropriate value
args[0] = "2711477380+1000000";
int expected = 6;
int actual;
actual = TestProjectCalculator.BaseCalculator_ProgramAccessor.Main(args);
Assert.AreEqual(expected, actual,
"BaseCalculator.Program.Main did not return the expected value.");
}
И так далее. Таким образом, проверив методы Main() и Estimate(), а также некоторые
методы визуального интерфейса, можно убедиться в соответствии системы требованиям или,
наоборот, обнаружить какие-то ошибки в межмодульном взаимодействии.
Программа
Будут выданы исходные тексты программы BaseCalculatorNew для тестирования
методом "белого ящика" средствами MVSTE и пример тестового драйвера.
Составить тест-план и провести интеграционное тестирование (средствами MVSTE)
методов Main() и Estimate().
Достарыңызбен бөлісу: |