Опубликован: 28.11.2007 | Уровень: специалист | Доступ: платный | ВУЗ: Национальный исследовательский ядерный университет «МИФИ»
Практическая работа 4:

Модульное тестирование

< Лекция 5 || Практическая работа 4: 12 || Лекция 6 >
9.2.1.3. Подходы к проектированию тестового окружения

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

Первый подход к модульному тестированию основывается на предположении, что функциональность каждого вновь разработанного модуля должна проверяться в автономном режиме без его интеграции с системой. Здесь для каждого вновь разрабатываемого модуля создается тестовый драйвер и заглушки, при помощи которых выполняется набор тестов. Только после устранения всех дефектов в автономном режиме производится интеграция модуля в систему и проводится тестирование на следующем уровне. Достоинством данного подхода является более простая локализация ошибок в модуле, поскольку при автономном тестировании исключается влияние остальных частей системы, которое может вызывать маскировку дефектов (эффект четного числа ошибок). Основной недостаток данного метода – повышенная трудоемкость написания драйверов и заглушек, поскольку заглушки должны адекватно моделировать поведение системы в различных ситуациях, а драйвер должен не только создавать тестовое окружение, но и имитировать внутреннее состояние системы, в составе которой должен функционировать модуль.

Второй подход построен на предположении, что модуль все равно работает в составе системы и если модули интегрировать в систему по одному, то можно протестировать поведение модуля в составе всей системы. Этот подход свойственен большинству современных "облегченных" методологий разработки, в том числе и XP.

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

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

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

Замечание. О том, что такое Reflection, можно прочесть на http://msdn2.microsoft.com/en-us/library/cxz4wk15(VS.80).aspx

9.2.2. На примере "Калькулятора"

Рассмотренный на предыдущем семинаре пример прост прежде всего за счет того, что нам не приходится создавать тестового окружения. Чтобы увидеть весь описанный механизм в действии, протестируем метод RunEstimate класса AnalaizerClass. Этот метод использует методы из класса CalcClass, в надежности которых мы не уверены. Заменим эти методы заглушкой, состоящей исключительно из функций стандартного класса Math. Для этого воспользуемся файлом My.dll и добавим его в проект.

На семинаре мы не будем составлять тест-требования (этим студенты займутся в домашней работе). Продемонстрируем, как создать тестовое окружение и запустить метод. Проверяем операцию сложения на примере 2+2, т.е. в стеке до начала выполнения самой операции (т.е. после компиляции) находятся следующие элементы: " 2 ", " 2 ", " + ".

private void buttonStart_Click(object sender, EventArgs e)
        {
            // создаем провайдер для генерирования и компиляции кода на C#
            System.CodeDom.Compiler.CodeDomProvider prov = 
System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp");
            // создаем параметры компилирования
            System.CodeDom.Compiler.CompilerParameters cmpparam = new 
System.CodeDom.Compiler.CompilerParameters();
            // результат компиляции - библиотека
            cmpparam.GenerateExecutable = false;
            // не включаем информацию отладчика
            cmpparam.IncludeDebugInformation = false;
            // подключаем 2-е стандартные библиотеки и библиотеку 
CalcClass.dll
            cmpparam.ReferencedAssemblies.Add(Application.StartupPath + 
"\\CalcClass.dll");
            cmpparam.ReferencedAssemblies.Add("System.dll");
            cmpparam.ReferencedAssemblies.Add("System.Windows.Forms.dll");
            // имя выходной сборки - My.dll
            cmpparam.OutputAssembly = "My.dll";
            // компилируем класс AnalaizerClass с заданными параметрами
            System.CodeDom.Compiler.CompilerResults res = 
prov.CompileAssemblyFromFile(cmpparam, Application.StartupPath + "\\AnalaizerClass.cs");
            // Выводим результат компилирования на экран
            if (res.Errors.Count != 0)
            {
                richTextBox1.Text += res.Errors[0].ToString();
            }
            else
            {
                // загружаем только что скомпилированную сборку(здесь тонкий 
момент - если мы прото загрузим сборку из файла, то он будет заблокирован,
                // acces denied, поэтому вначале читаем его в поток и лишь 
потом подключаем)
                System.IO.BinaryReader reader = new 
System.IO.BinaryReader(new System.IO.FileStream(Application.StartupPath + "\\My.dll", System.IO.FileMode.Open, System.IO.FileAccess.Read));
                Byte[] asmBytes = new Byte[reader.BaseStream.Length];
                reader.Read(asmBytes, 0, (Int32) reader.BaseStream.Length);
                reader.Close();
                reader = null; 
                System.Reflection.Assembly assm = 
System.Reflection.Assembly.Load(asmBytes);
                Type[] types = assm.GetTypes();
                Type analaizer = types[0];
                // находим метод CheckCurrency - к счастью, он единственный
                System.Reflection.MethodInfo addinfo = 
analaizer.GetMethod("RunEstimate");
                System.Reflection.FieldInfo fieldopz = 
analaizer.GetField("opz");
                System.Collections.ArrayList ar = new 
System.Collections.ArrayList();
                ar.Add("2");
                ar.Add("2");
                ar.Add("+");
                fieldopz.SetValue(null, ar);
                richTextBox1.Text += addinfo.Invoke(null, null).ToString();
                asmBytes = null;
            }
            prov.Dispose();
}
9.1.

Замечание. На самом деле данный подход позволяет выявить множество недостатков программы, которые другими тестами не выявляются. Можно попробовать поэкспериментировать с "Калькулятором" и убедиться, что он работает корректно. Однако, если в тестируемый метод подать на вход не " 2 ", " 2 ", " + ", а " 2 ", " 2 ", " + ", " + ", то программа закончит работу с исключением. Это говорит о том, что метод RunEstimate написан не корректно. Можно, например, было бы скрыть, т. е. сделать доступ private, всем методам AnalaizerClass, кроме Estimate (это было бы более правильно, но для простоты тестирования они сделаны public. Стоит отметить, что Visual Studio 2005 имеет также механизмы для тестирования подобных методов.). Тем самым мы не позволим другим выполнять "потенциально опасные" методы и передавать им некорректные значения. Однако это не является достаточным механизмом защиты программы. Необходимо провести более качественную валидацию используемых методами параметров.

Замечание. К проблеме создания тестового окружения можно подойти с двух сторон – либо откомпилировать код, с заранее подключенными dll файлами к проекту, либо воспользоваться областью CodeDom и компилировать в процессе выполнения. Это особенно удобно, если нужно менять тестовое окружение в процессе работы.

9.3. Раздаточный материал

9.3.1. Программа

Будут выданы .dll файлы, которые нужно протестировать методом "черного ящика" и пример тестового драйвера.

9.4. Домашнее задание

Составить тест-план и провести модульное тестирование следующих методов:

  1. /// <summary>
    /// Проверка корректности скобочной структуры входного выражения 
    /// </summary>
    /// <returns>true - если все нормально, 
    false - если есть ошибка</returns>
    /// метод бежит по входному выражению, символ за 
    символом анализируя его и считая количество скобок. 
    В случае возникновения
    /// ошибки возвращает false, а в erposition записывает позицию, 
    на которой возникла ошибка.
    public static bool CheckCurrency()
  2. /// <summary>
    /// Форматирует входное выражение, выставляя между 
    операторами пробелы и удаляя лишние, а также отлавливает 
    неопознанные операторы, следит за концом строки
    /// а также отлавливает ошибки на конце строки
    /// </summary>
    /// <returns>конечную строку или сообщение об ошибке, 
    начинающиеся со спец. символа &</returns>
    public static string Format()
  3. /// <summary>
    /// Создает  массив, в котором располагаются операторы и 
    символы, представленные в обратной польской записи (безскобочной)
    /// На этом же этапе отлавливаются почти все остальные 
    ошибки (см код). По сути - это компиляция.
    /// </summary>
    /// <returns>массив обратной польской записи</returns>
    public static System.Collections.ArrayList CreateStack()
  4. /// <summary>
    /// Вычисление обратной польской записи
    /// </summary>
    /// <returns>результат вычислений или сообщение об ошибке</returns>
    public static string RunEstimate()
< Лекция 5 || Практическая работа 4: 12 || Лекция 6 >
Илья Макаренко
Илья Макаренко

Добрый день.

Вопрос №1

Какова стоимость получения диплома о мини-МБА по данному курсу? Или ориентироваться на указанную на сайте?

Вопрос №2

Возможно ли начать обучение без потери результатов, не отправив документы на зачисление, а отправку выполнить позже?

Александр Медов
Александр Медов

Здравствуйте, какова полная сумма предоставленной услуги с печатью документа и отправкой по почте?

Денис Сафронов
Денис Сафронов
Россия
жылдыз насырбекова
жылдыз насырбекова
Киргизия, бишкек