![](/stc/img/evkova.png)
отладка и тестирвание программ
Введение
Компьютерная технология надежно вошли в человеческую жизнь. Подъем научного и технического прогресса невозможен без автоматизации вычислительных процессов. Именно необходимость в автоматизации вычислительных процессов стала изначальным импульсом к популяризации программирования.
Традиционная технология программирования формировалась на заре вычислительной техники, когда в распоряжении обычных пользователей были ограниченные ресурсы ЭВМ, а разработчик программ был в то же время и главным ее пользователем. В этих условиях главное внимание обращалось на получение эффективных программ и оптимального использования ресурсов ЭВМ.
Сейчас же, когда сфера применения техники и интернета чрезвычайно расширилась, разработка программ осуществляется разными людьми и компаниями. Поэтому наряду с эффективностью на первый план выдвигаются и другие важные характеристики программ такие, как удобство, хорошая документированность, надежность. Проблема разработки программ, соответсвующий подобным требованиям, объясняется трудоемкостью процесса программирования.
Для создания программы есть необходимость придерживаться определенных принципов и новых тенденций программирования.
В наше время из-за постоянного прогресса компьютерных технологий программирование играет одну из важнейших ролей в жизни человека.
Глава 1. Разработка программы
Предметная область.
Суждения – это мысль о предмете, в которой утверждается или отрицается связь между предметом и его признаком, отношения между предметами, и которая может быть либо истинной, либо ложной. Языковой формой выражения суждения является повествовательное предложение. Вопросительные и побудительные предложения суждениями не являются.
Простое суждение — это суждение о принадлежности предметам свойств , а также суждения об отсутствии у предметов каких-либо свойств. В простом суждении могут быть выделены термины суждения — субъект, предикат, связка, квантор.
Для упрощения записи операций над суждениями введем следующие обозначения:
- символ «S» используется для обозначения субъекта;
- символ «P» используется для обозначения предиката;
- символ «:» применяется для обозначения связки «есть»;
- символ «~» используется для обозначения отрицательной частицы «не»;
- символ «#» обозначает термин «ни один»;
- символ «!» применяется для обозначения термина «только»;
Тогда результат выполнения операций в общем виде может быть представлен в виде таблицы (см. Таблица 1).
модульный эквивалентность тестирование регрессионный
Таблица 1. Правила выполнения операций над суждениями
Операция |
Суждение |
|
Общеутвердительное S : P |
Общеотрицательное # S ~: P |
|
Превращение |
# S ~: ~P |
S : ~P |
Обращение |
Р : S |
# P ~: S |
Противопоставление предикату |
# ~P ~: S |
Р : S |
Описание алгоритма.
На вход программе подается одна или несколько строк. Эти строки представляет собой простое категорическое суждение, над которым необходимо выполнить требуемые операции. Для удобства дальнейшей работы строка разбивается на компоненты (квантор, субъект, связка, предикат).
Дополнительными данными для работы программы являются словари, которые хранят используемые кванторы, субъекты, связки и предикаты (все компоненты суждения).
В случае, если формат строки не удовлетворяет требования, или тип суждения не соответствует указанному в задании (в соответствии с классификацией, приведенной выше), генерируется исключение (ошибка времени выполнения).
При успешном разборе суждения последовательно выполняются требуемые операции и их результат возвращается пользователю в виде строки на естественном языке. При этом реализация грамматической точности построенного предложения не является приоритетной задачей.
Входные данные могут быть считаны из текстового файла (файл input.txt находится в той же директории, что и исполняемый файл). В случае отсутствия указанного файла пользователю предлагается ввести исходные данные с клавиатуры. Выходные данные выводятся на экран и дублируются в текстовом файле (файл output.txt находится также в той же директории, что и исполняемый файл). Данные словарей находятся в текстовых файлах, расположенных в подкаталоге Data (сам подкаталог располагается в том же каталоге, что и исполняемый файл) и имеют имя:
<Название_компонента>.txt
Процесс выполнения операций над суждениями представлен в виде блок-схемы: выполняется построчное чтение данных из потока ввода (файл или консоль), для каждой строки определяет тип суждения и выполняются допустимые операции над ним.
Глава 2. Тестирование методом «черного ящика»
Спецификация программы.
Написать консольную программу выполнения операций превращения, обращения и противопоставления предикату над общеутвердительными и общеотрицательными суждениями.
Входные данные находятся в текстовых файлах (расширение .txt). Каждая строка файла содержит единственную запись – суждение. При отсутствии входного файла осуществляется пользовательский ввод. Словари компонентов суждений так же хранятся в текстовых файлов в соответствии с определенным форматом.
Результаты выполнения операций записываются в текстовый файл (расширение .txt). Также осуществляется вывод результатов на экран.
Программа должна быть написана на алгоритмическом языке высокого уровня C#. Требуемая версия .NET Framework – 4.0 и выше. Программа должна выполняться на операционных системах семейства Windows версии XP и выше.
При ошибках пользовательского ввода или внутренних ошибках программа должна осуществлять вывод соответствующего сообщения.
Ограничения.
-
-
-
- По умолчанию, суждения записываются на русском языке. Для записи суждений на английском языке следует изменить строку 11 в файле app.config. Допустимые значения: Ru – русский зык (по умолчанию), En – английский язык.
- Формат записи суждений:
-
-
<Квантор> <Субъект> <Логическая связка> <Предикат>
Все компоненты являются обязательными. Компоненты Субъект и Логическая связка должны состоять из единственного слова.
-
-
-
- Формат записи элементов словарей:
-
-
<Начальная_форма> ([Форма_слова] [,Форма_слова] … [,Форма_слова])
То есть, в файле должна быть записана начальная форма фразы (слово или словосочетание), затем в скобках через запятую перечислены формы этой фразы, используемые в суждениях.
-
-
-
- Предикат должен иметь положительный характер (не должен иметь отрицательной частицы «не»)
- При записи результата все компоненты новых суждений должны иметь либо начальную форму фразы, как она указана в словарях компонентов, либо ту же форму, что и в записи исходного суждения. Знаки препинания игнорируются. Выходные суждения записываются в верхнем регистре.
- Текстовые файлы должны кодироваться UTF-8 и иметь расширение .txt.
- Требование к структуре файловой системы: текстовые файлы для ввода и вывода данных должны находиться в том же каталоге, что и исполняемый файл. Текстовые файлы со словарями компонентов должны располагаться в подкаталоге Data в том же каталоге, что и исполняемый файл и именоваться в формате <Компонент>.txt
- При возникновении любой ошибки выполнение программы останавливается.
-
-
Классы эквивалентности.
Классы эквивалентности будем представлять в виде таблицы (см. Таблица 2)
Таблица 2. Классы эквивалентности
№ |
Ситуация |
Классы эквивалентности |
|
Допустимые |
Недопустимые |
||
1 |
Ввод данных |
Данные успешно считаны из файла |
|
Файл не найден, пользователь вводит данные |
|||
2 |
Синтаксический анализ |
Запись суждения соответствует формату, обнаружены все компоненты в словарях |
Отсутствует любой (любые) из компонентов суждения |
Все компоненты присутствуют, но не могут быть распознаны |
|||
Тип суждения не соответствует заявленному в задании |
|||
Файл со словарем для какого-либо компонента не обнаружен |
|||
3 |
Выполнение операций |
Операции выполнены успешно |
|
4 |
Вывод данных |
Данные успешно записаны в существующий файл |
|
Данные успешно записаны в созданный файл |
Покрытие тестами классов эквивалентности.
Тесты, покрывающие классы эквивалентности, приведены в нижеследующей таблице (Таблица 3).
Таблица 3. Используемые тесты
№ |
Значение входных данных |
Значение выходных данных |
Тип ошибки |
Причина |
1 |
Файл пустой |
Пустая строка |
Ошибки нет |
|
2 |
Файл не существует, пользователь вводит пустую строку |
Пустая строка |
Ошибки нет |
|
3 |
Все студенты учатся хорошо |
I > Входные данные: ВСЕ СТУДЕНТЫ УЧИТЬСЯ ХОРОШО I > Тип: Общеутвердительное I > Превращение: НИ ОДИН ИЗ ТЕХ КТО СТУДЕНТЫ НЕ УЧИТЬСЯ НЕ ХОРОШО I > Обращение: НЕКОТОРЫЕ ИЗ ТЕХ КТО УЧАТСЯ ХОРОШО СТУДЕНТЫ I > Противопоставление: НИ ОДИН ИЗ ТЕХ КТО НЕ УЧАТСЯ ХОРОШО НЕ СТУДЕНТЫ |
Ошибки нет |
|
4 |
Студенты учатся хорошо |
E > Квантор не определен |
Неверный формат ввода |
Отсутствует квантор суждения |
5 |
Почти все студенты учатся хорошо |
E > Не могу считать данные для объекта: Квантор, так как Не обнаружена требуемая форма |
Неверный формат ввода |
Квантор суждения не определен |
6 |
Все учатся хорошо |
E > Субъект не определен |
Неверный формат ввода |
Отсутствует субъект суждения |
7 |
Все студенты хорошо |
E > Не могу считать данные для объекта: Связка, так как Не обнаружена начальная форма |
Неверный формат ввода |
Отсутствует логическая связка |
8 |
Все студенты учатся |
E > Предикат не определен |
Неверный формат ввода |
Отсутствует предикат суждения |
9 |
Некоторые студенты учатся хорошо |
I > Входные данные: НЕКОТОРЫЕ СТУДЕНТЫ УЧИТЬСЯ ХОРОШО I > Тип: Частноутвердительное W > Данное суждение не является общеутвердительным или общеотрицательным |
Несоответствие типа |
Тип суждения не соответствует указанному в задании |
10 |
Все студенты учатся хорошо |
E > Не могу считать данные для объекта: Предикат, так как Файл со справочиком не обнаружен |
Файл не найден |
Отстутствует файл со справочником |
При выполнении тестов использовались следующие записи в словарях компонентов:
-
-
-
- Кванторы (Квантор.txt)
-
-
Все (Вся, Всё, Всех)
Некоторые (Некоторых, Некоторым, Некоторыми)
Ни один (Ни одного, Ни одному, Ни одна, Ни одно)
Ни один из (Ни одного из, Ни одному из, Ни одна из, Ни одно из)
Ни один из тех кто (Ни одного из тех кто, Ни одному из тех кто, Ни одна из тех кто, Ни одно из тех кто)
-
-
-
- Субъекты (Субъект.txt)
-
-
Студент (Студента, Студенту, Студентом, Студенте)
Студенты (Студентов, Студентам, Студентами, Студентах)
-
-
-
- Логические связки (Связка.txt)
-
-
Учиться (учусь, учимся, учишься, учится, учатся)
Быть (есть, был, была, было, были, будет, будут)
Бывать (бывает, бывают, бывал, бывала, бывало, бывали)
-
-
-
- Предикаты (Предикат.txt)
-
-
Хорошо ()
Плохо ()
Умный (умная, умное, умные, умного, умную, умных, умному, умной, умным, умными, умном, умных)
Используемые тесты покрывают следующие классы эквивалентности (см. Таблица 4):
Таблица 4. Соотношение классов эквивалентности и тестов
№ |
Описание класса эквивалентности |
Допустимый класс? |
Номер(-а) тестов |
1 |
Данные успешно считаны из файла |
+ |
1, 3-9 |
2 |
Файл не найден, пользователь вводит данные |
+ |
2-9 |
3 |
Запись суждения соответствует формату, обнаружены все компоненты в словарях |
+ |
3 |
4 |
Отсутствует любой (любые) из компонентов суждения |
– |
4,6-8 |
5 |
Все компоненты присутствуют, но не могут быть распознаны |
– |
5,7 |
6 |
Тип суждения не соответствует заявленному в задании |
– |
9 |
7 |
Файл со словарем для какого-либо компонента не обнаружен |
– |
10 |
8 |
Операции выполнены успешно |
+ |
3 |
9 |
Данные успешно записаны в существующий файл |
+ |
1-10 |
10 |
Данные успешно записаны в созданный файл |
+ |
1-10 |
Глава 3. Тестирование методом «белого ящика». Модульное тестирование
Модуль DictionaryParser.Parse().
Алгоритм и код
Программный код модуля приведен в листинге 1.
Листинг 1
1 |
public void Parse() |
|
2 |
{ |
|
3 |
using (FileStream stream = new FileStream(_filePath, FileMode.Open, FileAccess.Read)) |
1 |
4 |
{ |
|
5 |
StreamReader reader = new StreamReader(stream); |
2 |
6 |
while (!reader.EndOfStream) |
3 |
7 |
{ |
|
8 |
string line = reader.ReadLine().ToLower(); |
4 |
9 |
int openBracket; |
|
10 |
string initialForm = ""; |
|
11 |
try |
|
12 |
{ |
|
13 |
openBracket = line.IndexOf('('); |
5 |
14 |
if (openBracket == -1) |
6 |
15 |
throw new DictionaryException(_dictionaryType, |
7 |
16 |
DictionaryExceptionReason. НевозможноПроанализироватьВариантыФорм); |
|
17 |
initialForm = string.Concat(line .Where((c, i) => i < openBracket)).Trim().ToUpper(); |
8 |
18 |
} |
|
19 |
catch |
9 |
20 |
{ |
|
21 |
throw new DictionaryException(_dictionaryType, |
|
22 |
DictionaryExceptionReason. НеОбнаруженаНачальнаяФорма); |
|
23 |
} |
|
24 |
Dictionary[initialForm] = new List<string>() {initialForm}; |
10 |
25 |
Dictionary[initialForm].AddRange(line.Substring(openBracket + 1) |
|
26 |
.Split(new string[] {",", ")"}, |
|
27 |
StringSplitOptions. RemoveEmptyEntries) |
|
28 |
.Select(s => s.Trim().ToUpper()) |
|
29 |
.ToList()); |
|
30 |
} |
11 |
31 |
reader.Close(); |
12 |
32 |
} |
13 |
33 |
} |
Приведенный алгоритм использует язык интегрированных запросов для облегчения чтения и преобразования данных. Все считанные данные хранятся в свойстве Dictionary класса.
Подготовка тестовых вариантов
Для тестирования каждого из независимых путей были созданы тестовые варианты исходных данных. Сами данные (ИД), ожидаемый от них результат (ОЖ. РЕЗ.) и результат, полученный фактически при выполнении программы (Ф. РЕЗ.), приведены в Таблица 5. Тестирование выполняется для справочника «Субъекты».
Таблица 5. Тестовые варианты для метода базового пути
№ |
ИД |
ОЖ. РЕЗ. |
1 |
Файл Субъекты.txt отсутствует |
Выброшено исключение FileNotFoundException, которое затем перехвачено в методе Program.Main. |
2 |
Файл Субъекты.txt не содержит данных |
Свойство DictionaryParser.Dictionary инициализировано пустым словарем. |
3 |
Файл Субъекты.txt содержит одну или несколько корректных строк: Студент (Студента, Студенту, Студентом, Студенте) Студенты (Студентов, Студентам, Студентами, Студентах) |
Свойство DictionaryParser.Dictionary содержит словарь: СТУДЕНТ -> [0]: "СТУДЕНТ" [1]: "СТУДЕНТА" [2]: "СТУДЕНТУ" [3]: "СТУДЕНТОМ" [4]: "СТУДЕНТЕ" СТУДЕНТЫ -> [0]: "СТУДЕНТЫ" [1]: "СТУДЕНТОВ" [2]: "СТУДЕНТАМ" [3]: "СТУДЕНТАМИ" [4]: "СТУДЕНТАХ" |
4 |
Файл Субъекты.txt содержит одну или несколько некорректных строк: Студент Студента, Студенту, Студентом, Студенте) Примечание: отсутствует символ ‘(‘ |
Выброшено исключение DictionaryException, которое затем перехвачено в методе Program.Main. |
Тестирование ветвей и операторов отношений.
В рассматриваемом модуле есть три условных оператора, условия для которых записаны операторами 1, 3, 6. Метод ветвей и операторов отношений будем выполнять только в последнем случае, так как первый реализован средствами .NET Framework, а второй задает условие цикла и будет протестирован позднее:
if (openBracket == -1)
throw new DictionaryException(_dictionaryType,
DictionaryExceptionReason.НевозможноПроанализироватьВариантыФорм);
В этом случае, очевидно, достаточно рассмотреть два случая:
-
-
-
- Переменная openBracket имеет значение -1, то есть строка в файле имеет неверный формат (отсутствует символ ‘(‘ как разделитель начальной формы слова и используемых форм). Тогда будет сгенерировано исключение DictionaryException, которое затем будет обработано в главном цикле программы.
- Переменная openBracket имеет значение, отличное от -1. Тогда программа будет успешно продолжена.
-
-
Тестирование потоков данных.
Определим DU-цепочки и представим их в виде списка цепочек.
Получены следующие цепочки:
-
-
-
- [line, 4, 8]
- [line, 4, 10]
- [openBracket, 5, 6]
- [openBracket, 5, 10]
- [initialForm, 8, 10]
-
-
Исходя из полученного списка цепочек, необходимо проанализировать поведение трех переменных (локальных переменных метод). Так как значения переменной openBracket зависит от line, а значения initialForm – от line и openBracket, то достаточно рассмотреть все требуемые варианты переменной line:
-
-
-
- null
- “”
- Студент (Студента, Студенту, Студентом, Студенте)
- Студент Студента Студенту Студентом Студенте
-
-
Тестирование циклов.
В модуле представлен единственный цикл типа «ПОКА <условие> ВЫПОЛНЯТЬ <действие>».
Для тестировании этого цикла будем записывать в файл различные варианты справочников форм и проверим следующие варианты:
-
-
-
- Единственная строка в файле
- Две строки в файле
- Более двух строк в файле.
-
-
Корректность записанных строк не принципиальна, так как условие некорректной записи было протестировано выше.
Набор модульных тестов.
Каждый тест сопровождается XML-комментарием, который описывает ситуацию и требуемое поведение программы.
#region Parser itself
/// <summary>
/// Проверяет случай отсутствия файла со справочником: все исключения перехвачены
/// </summary>
[Test]
public static void Dictionary_FileNotFound_ExceptionCaught()
{
File.Move("Data/Субъект.txt", "Data/Субъект1.txt");
Assert.Throws<DictionaryException>(() => new DictionaryParser(DictionaryType.Субъект));
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
/// <summary>
/// Проверяет случай пустого файла: корректное завершение работы
/// </summary>
[Test]
public static void Dictionary_EmptyFile_NoExceptions()
{
File.Move("Data/Субъект.txt", "Data/Субъект1.txt");
File.Delete("Data/Субъект.txt");
var writer = File.CreateText("Data/Субъект.txt");
writer.Close();
DictionaryParser parser = null;
Assert.DoesNotThrow(() => parser = new DictionaryParser(DictionaryType.Субъект));
Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
CollectionAssert.AreEquivalent(parser.Dictionary, dictionary);
File.Delete("Data/Субъект.txt");
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
/// <summary>
/// Проверяет случай любого числа корректных строк в файле: корректное заверешение работы
/// </summary>
[Test]
public static void Dictionary_AllLinesParsedSuccessfully_NoExceptions()
{
CreateFake();
DictionaryParser parser = null;
Assert.DoesNotThrow(() => parser = new DictionaryParser(DictionaryType.Субъект));
Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
dictionary["СТУДЕНТ"] = new List<string>() {"СТУДЕНТ", "СТУДЕНТА", "СТУДЕНТУ", "СТУДЕНТОМ", "СТУДЕНТЕ"};
dictionary["СТУДЕНТЫ"] = new List<string>()
{"СТУДЕНТЫ", "СТУДЕНТОВ", "СТУДЕНТАМ", "СТУДЕНТАМИ", "СТУДЕНТАХ"};
foreach (var key in dictionary.Keys)
{
CollectionAssert.AreEquivalent(parser.Dictionary[key].OrderBy(x => x),
dictionary[key].OrderBy(x => x));
}
File.Delete("Data/Субъект.txt");
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
/// <summary>
/// Проверяет случай некорректной записи строки в файле: все исключения перехвачены
/// </summary>
[Test]
public static void Dictionary_InvalidRecord_ExceptionCaught()
{
File.Move("Data/Субъект.txt", "Data/Субъект1.txt");
File.Delete("Data/Субъект.txt");
var writer = File.CreateText("Data/Субъект.txt");
writer.WriteLine("Студент Студента, Студенту, Студентом, Студенте)");
writer.Close();
DictionaryParser parser = null;
Assert.Throws<DictionaryException>(() => parser = new DictionaryParser(DictionaryType.Субъект));
File.Delete("Data/Субъект.txt");
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
#endregion
#region Containing
/// <summary>
/// Проверяет случай наличия формы слова в словаре: корректное завершение работы
/// </summary>
[Test]
public static void Dictionary_ContainsSpecificForm_NoExceptions()
{
CreateFake();
DictionaryParser parser = new DictionaryParser(DictionaryType.Субъект);
Assert.AreEqual(true, parser.Contains("студентов"));
File.Delete("Data/Субъект.txt");
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
/// <summary>
/// Проверяет случай отсутствия формы слова в словаре: корректное завершение работы
/// </summary>
[Test]
public static void Dictionary_DoesNotContainSpecificForm_ExceptionCaught()
{
CreateFake();
DictionaryParser parser = new DictionaryParser(DictionaryType.Субъект);
Assert.AreEqual(false, parser.Contains("стьюдентс"));
File.Delete("Data/Субъект.txt");
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
#endregion
#region Initial form
/// <summary>
/// Проверяет случай наличия начальной формы слова в словаре: корректное завершение работы
/// </summary>
[Test]
public static void Dictionary_InitialFormRevealed_NoExceptions()
{
CreateFake();
DictionaryParser parser = new DictionaryParser(DictionaryType.Субъект);
Assert.AreEqual("студенты".ToUpper(), parser.GetInitialForm("студентов"));
File.Delete("Data/Субъект.txt");
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
/// <summary>
/// Проверяет случай отсутствия начальной формы слова в словаре: корректное завершение работы
/// </summary>
[Test]
public static void Dictionary_InitialFormDidNotReveal_ExceptionCaught()
{
CreateFake();
DictionaryParser parser = new DictionaryParser(DictionaryType.Субъект);
Assert.AreEqual("стьюдентов", parser.GetInitialForm("стьюдентов"));
File.Delete("Data/Субъект.txt");
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
/// <summary>
/// Проверяет случай отсутствия начальной формы слова в пустом файле: корректное завершение работы
/// </summary>
[Test]
public static void Dictionary_InitialFormDidNotRevealFromEmptyFile_ExceptionCaught()
{
File.Move("Data/Субъект.txt", "Data/Субъект1.txt");
File.Delete("Data/Субъект.txt");
var writer = File.CreateText("Data/Субъект.txt");
writer.Close();
DictionaryParser parser = new DictionaryParser(DictionaryType.Субъект);
Assert.AreEqual("стьюдентов", parser.GetInitialForm("стьюдентов"));
File.Delete("Data/Субъект.txt");
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
#endregion
/// <summary>
/// Вспомогательный метод для подготовки стандартных файлов
/// </summary>
private static void CreateFake()
{
File.Move("Data/Субъект.txt", "Data/Субъект1.txt");
File.Delete("Data/Субъект.txt");
var writer = File.CreateText("Data/Субъект.txt");
writer.WriteLine("Студент (Студента, Студенту, Студентом, Студенте)");
writer.WriteLine("Студенты (Студентов, Студентам, Студентами, Студентах)");
writer.Close();
}
Модуль String.Capitalize()
Алгоритм и код
Программный код модуля приведен в листинге 2.
Листинг 2
1 |
public static string Capitalize(this string s) |
|
2 |
{ |
|
3 |
if (String.IsNullOrEmpty(s)) |
1 |
4 |
return ""; |
2 |
5 |
||
6 |
string res = ""; |
3 |
7 |
res += s[0]; |
|
8 |
||
9 |
for (int i = 1; i < s.Length; i++) |
4 |
10 |
{ |
|
11 |
res += (_delimeters.Contains(s[i - 1]) |
5 |
12 |
? Char.ToUpper(s[i]) |
6 |
13 |
: s[i]); |
7 |
14 |
} |
8 |
15 |
res = res.Remove(" "); |
9 |
16 |
||
16 |
return res; |
10 |
16 |
} |
11 |
Перебирая строку по символам, этот метод исключает все пробелы и знаки препинания из строки и заменяет последующие за ними символы их заглавными вариациями.
Например, строка «Это строка из НЕСКОЛЬКИХ слов» будет преобразована к виду «ЭтоСтрокаИзНесколькихСтрок».
Данный метод является методом расширения для стандартного класса String из .NET Framework. Он используется в качестве вспомогательного для работы с перечислением Quntifier (Квантор) – см. полный программный код в Приложении (или электронном приложении к отчету).
Тестирование базового пути.
Подготовка тестовых вариантов
Для тестирования каждого из независимых путей были созданы тестовые варианты исходных данных. Сами данные (ИД), ожидаемый от них результат (ОЖ. РЕЗ.) и результат, полученный фактически при выполнении программы (Ф. РЕЗ.), приведены в Таблица 6.
Таблица 6. Тестовые варианты для метода базового пути
№ |
ИД |
ОЖ. РЕЗ. |
1 |
null |
Пустая строка |
2 |
Пустая строка |
Пустая строка |
3 |
Это другая строка |
ЭтоДругаяСтрока |
4 |
Это Еще Одна Строка |
ЭтоЕщеОднаСтрока |
Тестирование ветвей и операторов отношений.
В рассматриваемом модуле есть три условных оператора, условия для которых записаны операторами 1, 4 (условие выхода из цикла) и 5 (в формате тернарного оператора – логической операции). Выполним тестирование для последнего оператора:
res += (_delimeters.Contains(s[i - 1])
? Char.ToUpper(s[i]) : s[i]);
В этом случае, очевидно, достаточно рассмотреть два случая (для некоторого индекса символа в строке i):
-
-
-
- Предыдущий символ есть символ-разделитель, то есть пробел или знак препинания. Тогда к результирующей строке добавляем текущий символ, выведенный в верхний регистр.
- Иначе, добавляем сам символ.
-
-
Достаточными будут следующие тест со строкой, в которой присутствуют как символы-разделители, так и буквенно-числовые символы.
Тестирование потоков данных.
Выполнение тестов потоков данных не представляется целесообразным, так как единственная используемая локальная переменная (кроме переменной итерации) изменяется на каждой итерации цикла и будет протестирована позднее (см. раздел «Тестирование циклов»).
Тестирование циклов.
В модуле представлен единственный цикл типа «ДЛЯ КАЖДОГО <переменная_итерации> ВЫПОЛНЯТЬ <действие>».
Для тестирования этого цикла будем проверять следующие варианты, основанные на длине строки (так как условие выхода из цикла есть сравнение переменной итерации с этой величиной):
-
-
-
- Строка из единственного символа-разделителя;
- Строка из единственного символа число-буквенной последовательности;
- Строка из двух и более любых символов
-
-
Пустая строка не тестируется, так как в этом случае цикл не запускается.
Набор модульных тестов.
/// <summary>
/// Проверяет случай null-строки: корректное завершение работы (пустая строка)
/// </summary>
[Test]
public static void String_NullStringPassed_EmptyReturned()
{
string pass = null;
string res = pass.Capitalize();
Assert.AreEqual("", res);
}
/// <summary>
/// Проверяет случай пустой строки: корректное завершение работы (пустая строка)
/// </summary>
[Test]
public static void String_EmptyStringPassed_EmptyReturned()
{
string pass = "";
string res = pass.Capitalize();
Assert.AreEqual("", res);
}
/// <summary>
/// Проверяет случай строки, содержащей единственный символ-разделитель:
/// корректное завершение работы (пустая строка)
/// </summary>
[Test]
public static void String_DelimeterOnlyStringPassed_EmptyReturned()
{
string pass = " ";
string res = pass.Capitalize();
Assert.AreEqual("", res);
}
/// <summary>
/// Проверяет случай строки, содержащей единственный символ-НЕ разделитель:
/// корректное завершение работы (пустая строка)
/// </summary>
[Test]
public static void String_NotDelimeterOnlyStringPassed_CapitalizedStringReturned()
{
string pass = "X";
string res = pass.Capitalize();
Assert.AreEqual("X", res);
}
/// <summary>
/// Проверяет случай любой строки, пригодной к капитализации:
/// корректное завершение работы (катитализированная строка)
/// </summary>
[Test]
public static void String_AnyCorrectStringPassed_CapitalizedStringReturned()
{
string pass = "Это тест Строки";
string res = pass.Capitalize();
Assert.AreEqual("ЭтоТестСтроки", res);
}
Часть 4. Интеграционное тестирование
Определение модулей и способа тестирования.
Модульные тесты будут разрабатываться для следующих модулей:
-
-
-
- M1 = Opinion.ParseExpression() – модуль синтаксического анализа суждения
- M2 = DictionaryParser.Parse() – извлечение данных из файла словаря.
- M3 = DictionaryParser.Contains() – проверка на наличие формы слова в словаре
- M4 = DictionaryParser.GetInitialForm() – получение начальной формы слова на основе данных, хранящихся в словаре
-
-
Результат работы этих модулей используется в методе M5 = Opinion.CreateExpression().
Набор юнит-тестов.
В данном разделе будет приведен список юнит-тестов для модуля чтения и использования данных словаря (модули M2-M4).
Для модуля M2 (получение данных из файла):
-
-
-
- public static void Dictionary_FileNotFound_ExceptionCaught()
-
-
Проверка работы модуля чтения данных из файла при отсутствии файла
-
-
-
- public static void Dictionary_EmptyFile_NoExceptions()
-
-
Проверка работы модуля чтения данных из файла при файле, в котором не содержится данных
-
-
-
- public static void Dictionary_AllLinesParsedSuccessfully_NoExceptions()
-
-
Проверка работы модуля чтения данных из файла при файле с корректными данными
-
-
-
- public static void Dictionary_InvalidRecord_ExceptionCaught()
-
-
Проверка работы модуля чтения данных из файла при файле, данные в котором хранятся в некорректной форме
Подробно эти методы рассмотрены в разделе Часть 3. Тестирование методом «белого ящика». Модульное тестирование: Модуль DictionaryParser.Parse() .
Для модуля M3 (проверка наличия формы слова):
-
-
-
- public static void Dictionary_ContainsSpecificForm_NoExceptions()
-
-
Проверка работы модуля проверки наличия формы слова при файле, который содержит слово, переданное в качестве параметра.
-
-
-
- public static void Dictionary_DoesNotContainSpecificForm_ExceptionCaught()
-
-
Проверка работы модуля проверки наличия формы слова при файле, который не содержит слово, переданное в качестве параметра.
Для модуля M4 (получение начальной формы слова):
-
-
-
- public static void Dictionary_InitialFormRevealed_NoExceptions()
-
-
Проверка работы модуля получения начальной формы слова при файле, который содержит слово, переданное в качестве параметра.
-
-
-
- public static void Dictionary_InitialFormDidNotReveal_ExceptionCaught()
-
-
Проверка работы модуля получения начальной формы слова при файле, который не содержит слово, переданное в качестве параметра.
-
-
-
- public static void Dictionary_InitialFormDidNotRevealFromEmptyFile_ExceptionCaught()
-
-
Проверка работы модуля получения начальной формы слова при пустом файле.
Полный код тестов хранится в файле DictionaryParserTests.cs проекта Opinions.Test (подкаталог Tests).
Набор интеграционных тестов.
Основное предназначение интеграционных тестов – проверить взаимодействие модулей. В качестве тестируемой части программы был выбран метод CreateExpression() класса Opinion. Выбор был обусловлен двумя причинами:
-
-
-
- метод использует свойства класса, значения которым присваивается в методе ParseExpression (модуль M1) и явно вызывает метод Contains класса DictionaryParser (модуль M3);
- метод неявно вызывает метод GetInitialForm (модуль M4) класса DictionaryParser, а тот, в свою очередь, зависит от метода Parse того же класса (модуль M2).
-
-
Полный код метода приведен в Листинг 3.
Листинг 3
1 |
public string CreateExpression() |
2 |
{ |
3 |
this.Expression = this.ToString(); |
4 |
return this.Expression.Remove(new string[] {"(", ")", "[", "]"}).RemoveDoubleSymbols(' '); |
5 |
} |
6 |
|
7 |
|
8 |
public override string ToString() |
9 |
{ |
10 |
return string.Format("{0} ({1}) {2}{3} [{4}{5}]", |
11 |
this.Quantifier.ToString().Decapitalize().ToUpper(), |
12 |
new DictionaryParser(DictionaryType.Субъект).GetInitialForm(this.Subject), |
13 |
(this.IsLinkPositive) ? "" : (LocalizationHelper.GetString(Variables.Not) + " "), |
14 |
new DictionaryParser(DictionaryType.Связка).GetInitialForm(this.Link), |
15 |
(this.IsPredicatePositive) ? "" : (LocalizationHelper.GetString(Variables.Not) + " "), |
16 |
new DictionaryParser(DictionaryType.Предикат).GetInitialForm(this.Predicate)); |
17 |
} |
Все тесты расположены в файле OpinionCreateExpressionTests.cs проекта Opinions.Test (подкаталог Tests).
/// <summary>
/// Проверяет случай пустого суждения: корректное завершение работы
/// </summary>
[Test]
public static void CreateExpression_SuccessEmpty_ExceptionsCaught()
{
CommonOpinion opinion = new CommonOpinion("");
try
{
opinion.ParseExpression();
}
catch (InvalidOpinionFormatException e)
{
if (e.Reason != FormatExceptionReason.СубъектНеОпределен)
Assert.Fail("Subject is not defined Exception required");
}
Assert.Throws<NullReferenceException>(() => opinion.CreateExpression());
}
/// <summary>
/// Проверяет случай общеутвердительного суждения: корректное завершение работы
/// </summary>
[Test]
public static void CreateExpression_SucessPositive_NoExceptions()
{
CommonOpinion opinion;
try
{
opinion = new CommonOpinion("Все студенты учатся хорошо");
opinion.ParseExpression();
Assert.AreEqual("ВСЕ СТУДЕНТЫ УЧИТЬСЯ ХОРОШО", opinion.CreateExpression());
}
catch (Exception e)
{
Assert.Fail("Тест провален: " + e.Message);
}
}
/// <summary>
/// Проверяет случай отсутвия файла словаря:
/// все исключения перехвачены, корректное завершение работы
/// </summary>
[Test]
public static void CreateExpression_DictionaryDoesNotExist_ExceptionCaught()
{
File.Move("Data/Субъект.txt", "Data/Субъект1.txt");
CommonOpinion opinion = new CommonOpinion("Все студенты учатся хорошо");
Assert.Throws<DictionaryException>(opinion.ParseExpression);
File.Move("Data/Субъект1.txt", "Data/Субъект.txt");
}
/// <summary>
/// Проверяет случай некорректной формы слова в суждении:
/// все исключения перехвачены, корректное завершение работы
/// </summary>
[Test]
public static void CreateExpression_InvalidForm_ExceptionCaught()
{
CommonOpinion opinion = new CommonOpinion("Все стьюденты учатся хорошо");
try
{
opinion.ParseExpression();
Assert.Throws<InvalidOpinionFormatException>(() => opinion.CreateExpression());
}
catch (Exception e)
{
if (e is DictionaryException ||
(e is InvalidOpinionFormatException &&
(e as InvalidOpinionFormatException).Reason == FormatExceptionReason.СубъектНеОпределен))
Список источников
ЛОГИКА: УЧЕБНОЕ ПОСОБИЕ ДЛЯ ВУЗОВ
Автор/создатель: Завалишин Е.П.
http://window.edu.ru/catalog/pdf2txt/717/67717/41081
Логика: учебник для юридических вузов
Автор/создатель: Кириллов Вячеслав Иванович
https://fil.wikireading.ru/16521
Тестирование программного обеспечения. Базовый курс.
Автор/создатель: Святослав Куликов
https://careers.epam.by/content/dam/epam/by/book_epam_by/Software_Testing_Basics_2_izdanie.pdf
Тестирование ветвей и операторов отношений
http://csaa.ru/testirovanie-vetvej-i-operatorov-otnoshenij/
Теоретический материал: файловый ввод-вывод (Паскаль)
https://informatics.mccme.ru/moodle/mod/book/print.php?id=536
ЛОГИКА: ЗАДАЧИ И ИХ РЕШЕНИЕ: УЧЕБНО-МЕТОДИЧЕСКОЕ ПОСОБИЕ
Автор/создатель: Микиртумов И.Б.
http://window.edu.ru/catalog/pdf2txt/592/69592/44419
https://kpfu.ru/portal/docs/F455110931/UMK.po.Logike.pdf
Заключение
В ходе выполнения работы были закреплены методы тестирования программного обеспечения. На основе спецификации с использованием метода «черного ящика» были сформированы допустимые и недопустимые классы эквивалентности, то есть набор ситуаций, в которых программа ведет себя одинаковым (корректным/некорректным, соответственно) образом.
Несколько модулей программы (в терминах объектно-ориентированного программирования – методов класса) были протестированы методом «белого ящика». При этом для написания модульных тестов использовался фреймворк NUnit. Несколько объединенных, уже протестированных модулей были подвергнуты интеграционному тестированию для проверки их взаимодействия.
- Формы и системы оплаты труда на предприятии (Экономическая сущность , формы и системы оплаты труда)
- Общие положения по выполнению кассовых операций. Понятие операционной кассы на примере ПАО «Сбербанк»
- Социально-психологический климат организации (Понятие и сущность социально-психологического климата в организации)
- «Биологическая обратная связь как психотерапевтический метод» (Теоретические основы изучения биологической обратной связи как психотерапевтического метода)
- Порядок проведения приватизации(Общая характеристика приватизации имущества в Российской Федерации)
- Роль финансового рынка в мобилизации и распределении финансовых ресурсов.
- Оперативно-розыскная деятельность и права граждан (Сущность и значение, соблюдения прав и свобод человека и гражданина при осуществлении ОРД)
- Индивидуальное предпринимательство (Общая характеристика индивидуальной предпринимательской деятельности)
- «Аппарат государственной власти» (Понятие аппарата государственной власти (механизма государства))
- Разработка регламента выполнения процесса «Складской учет» (Предлагаемые мероприятия по улучшению процессов)
- Разработка проекта информационной системы для туристического агентства
- Отладка и тестирование программ: основные подходы и ограничения (Тестирование программ)