Compare commits
20 Commits
9c1fece35b
...
fourth-lab
| Author | SHA1 | Date | |
|---|---|---|---|
| 71de5751fe | |||
| ab268624bf | |||
| 7596e5632c | |||
| 035ea70161 | |||
| ff5b5bb44e | |||
| a7a3c197a1 | |||
| 99caecf319 | |||
| 4800f11a7e | |||
| cc6b784614 | |||
| a053739274 | |||
| bdb744e4e3 | |||
| 224ee7e1cc | |||
| 6e7938cc80 | |||
| 68d0b93f75 | |||
| dc7a279531 | |||
| de6a237048 | |||
| 65a1570293 | |||
| 46de5e92fe | |||
| 2973b7ca22 | |||
| 69983a244b |
39
decode.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
"""Упрощяет копипасту из отчётов с поломанной кодировкой"""
|
||||||
|
|
||||||
|
|
||||||
|
def fix_encoding(text):
|
||||||
|
result = []
|
||||||
|
for ch in text:
|
||||||
|
try:
|
||||||
|
decoded = ch.encode("latin1").decode("windows-1251")
|
||||||
|
result.append(decoded)
|
||||||
|
except (UnicodeEncodeError, UnicodeDecodeError):
|
||||||
|
result.append(ch)
|
||||||
|
fixed_text = "".join(result)
|
||||||
|
fixed_text = fixed_text.replace("ј", "ё")
|
||||||
|
fixed_text = fixed_text.replace("ѕ", "<<")
|
||||||
|
fixed_text = fixed_text.replace("ї", ">>")
|
||||||
|
fixed_text = fixed_text.replace("\x16", "--")
|
||||||
|
fixed_text = fixed_text.replace("\x10", "<<")
|
||||||
|
fixed_text = fixed_text.replace("\x11", ">>")
|
||||||
|
return fixed_text
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
while True:
|
||||||
|
print("Введите искажённый текст и нажмите enter:")
|
||||||
|
|
||||||
|
text_lines = []
|
||||||
|
while True:
|
||||||
|
line = input()
|
||||||
|
if line == "":
|
||||||
|
# Если строка пустая, это означает, что пользователь сделал
|
||||||
|
# двойной перенос строки.
|
||||||
|
break
|
||||||
|
text_lines.append(line)
|
||||||
|
|
||||||
|
corrupted_text = "\n".join(text_lines)
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(fix_encoding(corrupted_text))
|
||||||
|
print()
|
||||||
5
lab1/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
**/*
|
||||||
|
!.gitignore
|
||||||
|
!report.tex
|
||||||
|
!img
|
||||||
|
!img/**
|
||||||
821
lab1/report.tex
Normal file
@@ -0,0 +1,821 @@
|
|||||||
|
\documentclass[a4paper, final]{article}
|
||||||
|
%\usepackage{literat} % Нормальные шрифты
|
||||||
|
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||||
|
\usepackage{tabularx}
|
||||||
|
\usepackage[T2A]{fontenc}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[russian]{babel}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||||
|
\usepackage{ragged2e} %для растягивания по ширине
|
||||||
|
\usepackage{setspace} %для межстрочно го интервала
|
||||||
|
\usepackage{moreverb} %для работы с листингами
|
||||||
|
\usepackage{indentfirst} % для абзацного отступа
|
||||||
|
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||||
|
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||||
|
\usepackage{tikz}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{afterpage}
|
||||||
|
\usepackage{longtable}
|
||||||
|
\usepackage{float}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||||
|
\usepackage{pdflscape}
|
||||||
|
% \usepackage{lscape}
|
||||||
|
|
||||||
|
\usepackage{array}
|
||||||
|
\usepackage{multirow}
|
||||||
|
|
||||||
|
\renewcommand\verbatimtabsize{4\relax}
|
||||||
|
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||||
|
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||||
|
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||||
|
\usepackage{listings} %листинги
|
||||||
|
\usepackage{xcolor} % цвета
|
||||||
|
\usepackage{hyperref}% для гиперссылок
|
||||||
|
\usepackage{enumitem} %для перечислений
|
||||||
|
|
||||||
|
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||||
|
|
||||||
|
|
||||||
|
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||||
|
|
||||||
|
\hypersetup{colorlinks,
|
||||||
|
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||||
|
|
||||||
|
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||||
|
\lstloadlanguages{ SQL}
|
||||||
|
% включаем кириллицу и добавляем кое−какие опции
|
||||||
|
\lstset{tabsize=2,
|
||||||
|
breaklines,
|
||||||
|
basicstyle=\footnotesize,
|
||||||
|
columns=fullflexible,
|
||||||
|
flexiblecolumns,
|
||||||
|
numbers=left,
|
||||||
|
numberstyle={\footnotesize},
|
||||||
|
keywordstyle=\color{blue},
|
||||||
|
inputencoding=cp1251,
|
||||||
|
extendedchars=true
|
||||||
|
}
|
||||||
|
\lstdefinelanguage{MyC}{
|
||||||
|
language=SQL,
|
||||||
|
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||||
|
% identifierstyle=\color{black},
|
||||||
|
% morecomment=[n]{/**}{*/},
|
||||||
|
% commentstyle=\color{blue}\ttfamily,
|
||||||
|
% stringstyle=\color{red}\ttfamily,
|
||||||
|
% morestring=[b]",
|
||||||
|
% showstringspaces=false,
|
||||||
|
% morecomment=[l][\color{gray}]{//},
|
||||||
|
keepspaces=true,
|
||||||
|
escapechar=\%,
|
||||||
|
texcl=true
|
||||||
|
}
|
||||||
|
|
||||||
|
\textheight=24cm % высота текста
|
||||||
|
\textwidth=16cm % ширина текста
|
||||||
|
\oddsidemargin=0pt % отступ от левого края
|
||||||
|
\topmargin=-1.5cm % отступ от верхнего края
|
||||||
|
\parindent=24pt % абзацный отступ
|
||||||
|
\parskip=5pt % интервал между абзацами
|
||||||
|
\tolerance=2000 % терпимость к "жидким" строкам
|
||||||
|
\flushbottom % выравнивание высоты страниц
|
||||||
|
|
||||||
|
|
||||||
|
% Настройка листингов
|
||||||
|
\lstset{
|
||||||
|
language=C++,
|
||||||
|
extendedchars=\true,
|
||||||
|
inputencoding=utf8,
|
||||||
|
keepspaces=true,
|
||||||
|
% captionpos=b, % подписи листингов снизу
|
||||||
|
}
|
||||||
|
|
||||||
|
\begin{document} % начало документа
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||||
|
\begin{center}
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||||
|
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||||
|
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||||
|
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||||
|
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||||
|
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\large{Лабораторная работа №1}\\
|
||||||
|
\large{по дисциплине}\\
|
||||||
|
\large{<<Методы тестирования программного обеспечения>>}\\
|
||||||
|
\hfill \break
|
||||||
|
|
||||||
|
% \hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\small{
|
||||||
|
\begin{tabular}{lrrl}
|
||||||
|
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||||
|
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||||
|
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Курочкин М. А. \\\\
|
||||||
|
&&\hspace{4cm}
|
||||||
|
\end{tabular}
|
||||||
|
\begin{flushright}
|
||||||
|
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
|
||||||
|
\end{flushright}
|
||||||
|
}
|
||||||
|
|
||||||
|
\hfill \break
|
||||||
|
% \hfill \break
|
||||||
|
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||||
|
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||||
|
|
||||||
|
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\tableofcontents
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\section*{Введение}
|
||||||
|
|
||||||
|
\addcontentsline{toc}{section}{Введение}
|
||||||
|
|
||||||
|
В разработке ПО, помимо основной задачи — реализовать заявленную в спецификации функциональность, — существует не менее важная задача — обеспечить качество разработанного решения.
|
||||||
|
|
||||||
|
Тестирование программного обеспечения — проверка соответствия между реальным и ожидаемым поведением программы, осуществляемая на конечном наборе тестов.
|
||||||
|
|
||||||
|
На практике ни один метод тестирования не может выявить все ошибки в программе. Это связано с тем, что ресурсы проекта (деньги, время, персонал), в том числе и на тестирование, ограничены. Но все-таки, правильно проведенное тестирование позволяет обнаружить большинство ошибок, что позволит их оперативно исправить, и тем самым повысить качество программного обеспечения.
|
||||||
|
|
||||||
|
В данной лабораторной работе используется ручное тестирование — процесс проверки ПО, выполняемый специалистами без использования каких-либо специальных автоматизированных средств.
|
||||||
|
|
||||||
|
Ручное тестирование применяется не вместо компьютерного тестирования, а вместе с ним, что позволяет выявить ошибки в программе на более ранних стадиях.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\section {Постановка задачи}
|
||||||
|
Требуется: провести инспекцию кода, выступая в роли разработчика программы.
|
||||||
|
|
||||||
|
Для выполнения данной задачи необходимо:
|
||||||
|
\begin{itemize}
|
||||||
|
\item изучить методы ручного тестирования;
|
||||||
|
\item провести инспекцию кода, проанализировать ее результаты;
|
||||||
|
\item исправить программу в соответствии с рекомендациями специалиста по тестированию.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\section {Описание методов инспекции кода}
|
||||||
|
Существуют три основных метода ручного тестирования:
|
||||||
|
\begin{itemize}
|
||||||
|
\item инспекция кода;
|
||||||
|
\item сквозной просмотр;
|
||||||
|
\item тестирование удобства использования.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Эти методы могут применяться на любой стадии разработки ПО, причем как к отдельным готовым модулям или блокам, так и к приложению в целом.
|
||||||
|
|
||||||
|
Инспекция и сквозной просмотр включают в себя чтение или визуальную проверку исходного кода программы группой лиц. Оба метода предполагают выполнение определенной подготовительной работы. Завершающим этапом является обмен мнениями между участниками проверки на специальном заседании. Цель такого заседания — нахождение ошибок, но не их устранение (т.е. тестирование, а не отладка).~\cite{mayers}
|
||||||
|
|
||||||
|
\subsection{Инспекция кода}
|
||||||
|
|
||||||
|
Инспекция кода — это набор процедур и методик обнаружения ошибок путем анализа (чтения) кода группой специалистов.~\cite{mayers}
|
||||||
|
|
||||||
|
\subsubsection{Группа инспектирования кода}
|
||||||
|
|
||||||
|
Обычно в состав группы входят четыре человека, один из которых играет роль координатора. Координатор должен быть квалифицированным программистом, но не автором тестируемой программы, детальное знание которой от него не требуется. Вторым участником группы является программист, а остальными — проектировщик программы (если это не сам программист) и специалист по тестированию.~\cite{mayers}
|
||||||
|
|
||||||
|
В рамках выполнения лабораторной работы в инспекции кода участвовало всего три человека: программист, специалист по тестированию и координатор.
|
||||||
|
|
||||||
|
|
||||||
|
\subsubsection{Человеческий фактор}
|
||||||
|
|
||||||
|
Если программист воспринимает инспектирование своей программы как деятельность, направленную против него лично, и занимает оборонительную позицию, то процесс инспектирования не будет эффективным. Программист должен оставить самолюбие в стороне и рассматривать инспекцию только в позитивном и конструктивном ключе, не забывая о том, что целью инспекции является нахождение ошибок и, следовательно, улучшение качества программы.~\cite{mayers}
|
||||||
|
|
||||||
|
\subsection{Сквозной просмотр}
|
||||||
|
Тестирование программы методом сквозного просмотра также как и в инспекции кода включает в себя проверку программного кода группой лиц.
|
||||||
|
|
||||||
|
При сквозном просмотре код проверяется группой разработчиков (оптимально — 3-4 человека), лишь один из которых является автором программы. Таким образом, большую часть программы тестирует не ее создатель, а другие члены команды разработчиков, что согласуется со вторым принципом, согласно которому тестирование программистом собственной программы редко бывает эффективным.
|
||||||
|
|
||||||
|
Преимуществом сквозных просмотров, снижающим стоимость отладки (исправления ошибок), является возможность точной локализации ошибки. Кроме того, в процессе сквозного просмотра обычно удается выявить целую группу ошибок, которые впоследствии можно устранить все вместе. При тестировании программы на компьютере обычно проявляются лишь признаки ошибок (например, программа не может корректно завершиться или выводит бессмысленные результаты), а сами они обнаруживаются и устраняются по отдельности.~\cite{mayers}
|
||||||
|
|
||||||
|
\subsection{Проверка за столом}
|
||||||
|
|
||||||
|
Метод ручного тестирования «проверка за столом» может рассматриваться как инспекция или сквозной просмотр кода, выполняемые одним человеком, который вычитывает код программы, проверяет его, руководствуясь контрольным списком ошибок, и (или) прогоняет через логику программы тестовые данные.
|
||||||
|
|
||||||
|
Для большинства людей проверка за столом является относительно непродуктивной. Это объясняется прежде всего тем, что такая проверка представляет собой полностью неупорядоченный процесс. Вторая, более важная причина заключается в том, что проверка за
|
||||||
|
столом вступает в противоречие со вторым принципом тестирования, согласно которому
|
||||||
|
тестирование программистом собственных программ обычно оказывается неэффективным.
|
||||||
|
Следовательно, оптимальный вариант состоит в том, чтобы такую проверку выполнял человек, не являющийся автором программы (например, два программиста могут обмениваться
|
||||||
|
программами для взаимной проверки, а не проверять собственные программы), но даже в
|
||||||
|
этом случае такая проверка менее эффективна, чем сквозные просмотры или инспекции. В
|
||||||
|
основном именно по этой причине лучше, чтобы сквозные просмотры или инспекции осуществлялись в группе.~\cite{mayers}
|
||||||
|
|
||||||
|
\subsection{Рецензирование}
|
||||||
|
Рецензирование — это процедура анонимной оценки общих характеристик качества, обслуживаемости, расширяемости, удобства использования и ясности программного обеспечения. Цель данного метода — предоставить программисту возможность получить стороннюю
|
||||||
|
оценку результатов своего труда.
|
||||||
|
Общее руководство процессом осуществляет администратор, выбираемый из числа программистов. В свою очередь, администратор отбирает в группу рецензентов от 6 до 20 участников (6 — это необходимый минимум, обеспечивающий анонимность оценок). Предполагается, что все участники специализируются в одной области. Каждый из участников предоставляет для рецензирования две своих программы, одну из которых он считает наилучшей,
|
||||||
|
а вторую — наихудшей по качеству.
|
||||||
|
Когда будут собраны все программы, их распределяют случайным образом между участниками. Каждому участнику дают для рецензирования четыре программы. Две из них относятся
|
||||||
|
к категории «наилучших», а две — к категории «наихудших» программ, но рецензенту не
|
||||||
|
сообщают, какой именно является каждая из них. Любой участник тратит на просмотр одной программы 30 минут и заполняет ее оценочную анкету. После просмотра всех четырех
|
||||||
|
программ рецензент оценивает их относительное качество.
|
||||||
|
Рецензента также просят предоставить свои замечания к программе и дать рекомендации по
|
||||||
|
ее улучшению.
|
||||||
|
После просмотра всех программ каждому участнику передают анкеты с оценками двух его
|
||||||
|
программ. Кроме того, участники получают статистическую сводку, отражающую общие и
|
||||||
|
детализированные данные о рейтинге их собственных программ среди всего набора, а также
|
||||||
|
анализ того, насколько оценки, данные участником чужим программам, близки к оценкам
|
||||||
|
тех же программ со стороны других рецензентов~\cite{mayers}
|
||||||
|
|
||||||
|
|
||||||
|
\section{Технология инспекции кода}
|
||||||
|
|
||||||
|
\subsection{Состав группы}
|
||||||
|
|
||||||
|
Заседание происходило в следущем составе:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item секретарь Астафьев Игорь, исполняющий роль координатора;
|
||||||
|
\item специалист по тестированию Кондраев Дмитрий;
|
||||||
|
\item программист Тищенко Артём, он же проектировщик программы.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{Исходный код программы}
|
||||||
|
|
||||||
|
Код исходного файла программы представлен в листинге~\ref{lst:source}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Исходный код программы \texttt{bubble\_sort.cpp}.}, label={lst:source}]
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <locale>
|
||||||
|
#include <vector>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
struct BSTNode {
|
||||||
|
string word;
|
||||||
|
BSTNode* left;
|
||||||
|
BSTNode* right;
|
||||||
|
BSTNode(const string& w) : word(w), left(nullptr), right(nullptr) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BST {
|
||||||
|
BSTNode* root;
|
||||||
|
|
||||||
|
bool insertNode(BSTNode*& node, const string& w) {
|
||||||
|
if (!node) {
|
||||||
|
node = new BSTNode(w);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (w == node->word) return false;
|
||||||
|
if (w < node->word) return insertNode(node->left, w);
|
||||||
|
return insertNode(node->right, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeNode(BSTNode*& node, const string& w) {
|
||||||
|
if (!node) return false;
|
||||||
|
if (w < node->word) return removeNode(node->left, w);
|
||||||
|
if (w > node->word) return removeNode(node->right, w);
|
||||||
|
if (!node->left) {
|
||||||
|
BSTNode* temp = node->right;
|
||||||
|
delete node;
|
||||||
|
node = temp;
|
||||||
|
} else if (!node->right) {
|
||||||
|
BSTNode* temp = node->left;
|
||||||
|
delete node;
|
||||||
|
node = temp;
|
||||||
|
} else {
|
||||||
|
BSTNode* minNode = findMin(node->right);
|
||||||
|
node->word = minNode->word;
|
||||||
|
removeNode(node->right, minNode->word);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSTNode* findMin(BSTNode* node) {
|
||||||
|
if (!node) return nullptr;
|
||||||
|
while (node->left) node = node->left;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearTree(BSTNode*& node) {
|
||||||
|
if (!node) return;
|
||||||
|
clearTree(node->left);
|
||||||
|
clearTree(node->right);
|
||||||
|
delete node;
|
||||||
|
node = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inorder(BSTNode* node, vector<string>& result) const {
|
||||||
|
if (!node) return;
|
||||||
|
inorder(node->left, result);
|
||||||
|
result.push_back(node->word);
|
||||||
|
inorder(node->right, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
BST() : root(nullptr) {}
|
||||||
|
bool insert(const string& w) { return insertNode(root, w); }
|
||||||
|
bool remove(const string& w) { return removeNode(root, w); }
|
||||||
|
void clear() { clearTree(root); }
|
||||||
|
vector<string> getAllWords() const {
|
||||||
|
vector<string> result;
|
||||||
|
inorder(root, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
SetConsoleCP(1251);
|
||||||
|
SetConsoleOutputCP(1251);
|
||||||
|
setlocale(LC_ALL, "Russian");
|
||||||
|
BST dictionary;
|
||||||
|
|
||||||
|
cout << "Команды:\n"
|
||||||
|
<< "0 - очистить словарь;\n"
|
||||||
|
<< "1,<строка> - добавить строку;\n"
|
||||||
|
<< "2,<строка> - удалить строку;\n"
|
||||||
|
<< "3 - вывести все слова;\n"
|
||||||
|
<< "4 - завершить работу.\n\n";
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
string input;
|
||||||
|
if (!getline(cin, input)) break;
|
||||||
|
if (input.empty()) {
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int commaPos = input.find(',');
|
||||||
|
int command;
|
||||||
|
string cmdStr, word;
|
||||||
|
if (commaPos == (int)string::npos) {
|
||||||
|
cmdStr = input;
|
||||||
|
} else {
|
||||||
|
cmdStr = input.substr(0, commaPos);
|
||||||
|
if (commaPos + 1 < (int)input.size()) {
|
||||||
|
word = input.substr(commaPos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try { command = stoi(cmdStr); }
|
||||||
|
catch (...) {
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 0:
|
||||||
|
dictionary.clear();
|
||||||
|
cout << "Словарь очищен\n";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (word.empty()) {
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (dictionary.insert(word))
|
||||||
|
cout << "Строка \"" << word << "\" добавлена в словарь\n";
|
||||||
|
else
|
||||||
|
cout << "Строка \"" << word << "\" уже есть в словаре\n";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (word.empty()) {
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (dictionary.remove(word))
|
||||||
|
cout << "Строка \"" << word << "\" удалена из словаря\n";
|
||||||
|
else
|
||||||
|
cout << "Строка \"" << word << "\" не найдена в словаре\n";
|
||||||
|
break;
|
||||||
|
case 3: {
|
||||||
|
vector<string> allWords = dictionary.getAllWords();
|
||||||
|
if (allWords.empty()) {
|
||||||
|
cout << "Словарь пуст\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < (int)allWords.size(); i++) {
|
||||||
|
cout << allWords[i];
|
||||||
|
if (i + 1 < (int)allWords.size()) cout << ", ";
|
||||||
|
}
|
||||||
|
cout << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\subsection{Список вопросов и ответов на них}
|
||||||
|
|
||||||
|
Ниже представлен список вопросов, которые специалист по тестированию Кондраев задавал программисту Тищенко. После каждого вопроса приведен ответ. После некоторых ответов следуют замечания специалиста по тестированию.
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Программа реализует бинарное дерево поиска?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да.
|
||||||
|
|
||||||
|
\item На каком языке написана программа?
|
||||||
|
|
||||||
|
\textit{Ответ:} C++.
|
||||||
|
|
||||||
|
\item Какие типы переменных используются?
|
||||||
|
|
||||||
|
\textit{Ответ:} int, string, BSTNode*, BST, vector<string>, bool.
|
||||||
|
|
||||||
|
\item Все ли константные переменные объявлены при их инициализации?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да.
|
||||||
|
|
||||||
|
\item Где-то используется индексация массивов?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да, в выводе слов (например, \texttt{allWords[i]}) и обработке ввода.
|
||||||
|
|
||||||
|
\item Проверяется ли аргумент индексации что он целочисленный?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да, через приведение типа и проверку условий (например, \texttt{(int)allWords.size()}).
|
||||||
|
|
||||||
|
\item Корректно ли названы все методы и переменные?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да, кроме параметра \texttt{w} в методах BST.
|
||||||
|
|
||||||
|
\textbf{Замечание:} Параметры вроде \texttt{w} стоит переименовать в \texttt{word} для ясности.
|
||||||
|
|
||||||
|
\item Возможно ли деление на 0?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет.
|
||||||
|
|
||||||
|
\item Как выполняются проверки сравнения?
|
||||||
|
|
||||||
|
\textit{Ответ:} Сравниваются строки (например, \texttt{w == node->word}) и целые числа.
|
||||||
|
|
||||||
|
\item Есть ли операторы сравнения с целыми константами?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да (например, проверка \texttt{command == 0}).
|
||||||
|
|
||||||
|
\item Используются ли переменные double, float?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет.
|
||||||
|
|
||||||
|
\item Может ли какая-нибудь переменная типа int переполниться?
|
||||||
|
|
||||||
|
\textit{Ответ:} Теоретически возможно, но маловероятно (например, при очень большом количестве слов).
|
||||||
|
|
||||||
|
\item Соблюдены ли правила наследования объектов?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да, наследование не используется.
|
||||||
|
|
||||||
|
\item Все ли переменные объявлены?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да.
|
||||||
|
|
||||||
|
\item Правильно ли инициализированы массивы и строки?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да (например, \texttt{vector<string> result;} инициализируется пустым).
|
||||||
|
|
||||||
|
\item Удаляются ли неиспользуемые переменные из памяти?
|
||||||
|
|
||||||
|
\textit{Ответ:} Частично.
|
||||||
|
|
||||||
|
\textbf{Замечание:} Не хватает деструктора для класса \texttt{BST} для полной очистки памяти.
|
||||||
|
|
||||||
|
\item Присутствуют ли в программе потенциально бесконечные циклы?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет.
|
||||||
|
|
||||||
|
\item Может ли значение индекса выйти за размер массива?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет (используется \texttt{(int)allWords.size()}, что предотвращает выход за границы).
|
||||||
|
|
||||||
|
\item Проверяются ли входные данные на принадлежность к домену?
|
||||||
|
|
||||||
|
\textit{Ответ:} Частично.
|
||||||
|
|
||||||
|
\textbf{Замечание:} Не проверяется, содержит ли строка только допустимые символы (например, кириллицу).
|
||||||
|
|
||||||
|
\item Используются ли логические выражения?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да (например, \texttt{if (!node)}).
|
||||||
|
|
||||||
|
\item Используются ли файлы для ввода-вывода?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет.
|
||||||
|
|
||||||
|
\item У всех ли классов есть конструктор?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да (например, \texttt{BSTNode(const string\& w)}).
|
||||||
|
|
||||||
|
\item В цикле for корректно ли написана операция сравнения в цикле?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да (например, \texttt{for (int i = 0; i < (int)allWords.size(); i++)}).
|
||||||
|
|
||||||
|
\item Проверяется ли наличие элементов в дереве перед удалением?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет.
|
||||||
|
|
||||||
|
\textbf{Замечание:} Метод \texttt{remove} возвращает \texttt{false} при отсутствии элемента, но нет явной проверки на пустое дерево.
|
||||||
|
|
||||||
|
\item Совпадает ли число аргументов, передаваемых вызываемым модулям и число ожидаемых параметров?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да.
|
||||||
|
|
||||||
|
\item Делаются ли в программе попытки поправить входные аргументы?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да (например, обработка \texttt{stoi(cmdStr)} с исключениями).
|
||||||
|
|
||||||
|
\item Нет ли пропущенных функций?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет, все функции реализованы (вставка, удаление, очистка, вывод).
|
||||||
|
|
||||||
|
\item Выдаются ли предупреждения при компиляции?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет (при условии корректных настроек компилятора).
|
||||||
|
|
||||||
|
\item Выдаются ли ошибки при компиляции?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет.
|
||||||
|
|
||||||
|
\item Выполняются ли вычисления с присваиванием несовпадающих типов?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет.
|
||||||
|
|
||||||
|
\item Есть ли комментарии в программе?
|
||||||
|
|
||||||
|
\textit{Ответ:} Нет.
|
||||||
|
|
||||||
|
\textbf{Замечание:} Стоит добавить пояснения к методам класса \texttt{BST}.
|
||||||
|
|
||||||
|
\item Оформлен ли код в соответствии с некоторым регламентом?
|
||||||
|
|
||||||
|
\textit{Ответ:} Да (отступы, именование классов и методов в camelCase).
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
|
||||||
|
\section{Код программы с внесенными изменениями}
|
||||||
|
Результатом метода инспекции кода является список правок, которые необходимо внести в программу:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Переименовать параметр \texttt{w} в \texttt{word} для улучшения читаемости
|
||||||
|
\item Добавить деструктор для класса \texttt{BST}
|
||||||
|
\item Добавить проверку входных строк на наличие только буквенных символов
|
||||||
|
\item Заменить проверки вида \texttt{if (!node)} на \texttt{if (node == nullptr)}
|
||||||
|
\item Добавить комментарии к методам класса \texttt{BST}
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
Измененный код программы представлен в листинге~\ref{lst:new-code}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Модифицированная реализация BST}, label={lst:new-code}, language=C++]
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <locale>
|
||||||
|
#include <vector>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <cctype> // Добавлен новый заголовочный файл
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
struct BSTNode {
|
||||||
|
string word;
|
||||||
|
BSTNode* left;
|
||||||
|
BSTNode* right;
|
||||||
|
BSTNode(const string& w) : word(w), left(nullptr), right(nullptr) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BST {
|
||||||
|
BSTNode* root;
|
||||||
|
|
||||||
|
/* Рекурсивная вставка слова в дерево */
|
||||||
|
bool insertNode(BSTNode*& node, const string& word) { // Переименован параметр
|
||||||
|
if (node == nullptr) { // Изменена проверка на nullptr
|
||||||
|
node = new BSTNode(word);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (word == node->word) return false;
|
||||||
|
if (word < node->word) return insertNode(node->left, word);
|
||||||
|
return insertNode(node->right, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeNode(BSTNode*& node, const string& word) {
|
||||||
|
if (!node) return false;
|
||||||
|
if (word < node->word) return removeNode(node->left, word);
|
||||||
|
if (word > node->word) return removeNode(node->right, word);
|
||||||
|
if (!node->left) {
|
||||||
|
BSTNode* temp = node->right;
|
||||||
|
delete node;
|
||||||
|
node = temp;
|
||||||
|
} else if (!node->right) {
|
||||||
|
BSTNode* temp = node->left;
|
||||||
|
delete node;
|
||||||
|
node = temp;
|
||||||
|
} else {
|
||||||
|
BSTNode* minNode = findMin(node->right);
|
||||||
|
node->word = minNode->word;
|
||||||
|
removeNode(node->right, minNode->word);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSTNode* findMin(BSTNode* node) {
|
||||||
|
if (!node) return nullptr;
|
||||||
|
while (node->left) node = node->left;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearTree(BSTNode*& node) { // Рекурсивная очистка всех узлов дерева
|
||||||
|
if (!node) return;
|
||||||
|
clearTree(node->left);
|
||||||
|
clearTree(node->right);
|
||||||
|
delete node;
|
||||||
|
node = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inorder(BSTNode* node, vector<string>& result) const { // Рекурсивный обход дерева в порядке возврастания элементов
|
||||||
|
if (!node) return;
|
||||||
|
inorder(node->left, result);
|
||||||
|
result.push_back(node->word);
|
||||||
|
inorder(node->right, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавлен деструктор
|
||||||
|
~BST() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
BST() : root(nullptr) {}
|
||||||
|
|
||||||
|
// Добавлена проверка входных данных
|
||||||
|
bool validateWord(const string& word) {
|
||||||
|
for (char c : word) {
|
||||||
|
if (!isalpha(c) && static_cast<unsigned char>(c) < 128) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool insert(const string& word) {
|
||||||
|
if (!validateWord(word)) return false;
|
||||||
|
return insertNode(root, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove(const string& word) {
|
||||||
|
return removeNode(root, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
clearTree(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> getAllWords() const {
|
||||||
|
vector<string> result;
|
||||||
|
inorder(root, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Добавлена проверка кириллических символов
|
||||||
|
SetConsoleCP(1251);
|
||||||
|
SetConsoleOutputCP(1251);
|
||||||
|
setlocale(LC_ALL, "Russian");
|
||||||
|
|
||||||
|
BST dictionary;
|
||||||
|
cout << "Команды:\n"
|
||||||
|
<< "0 - очистить словарь;\n"
|
||||||
|
<< "1,<строка> - добавить строку;\n"
|
||||||
|
<< "2,<строка> - удалить строку;\n"
|
||||||
|
<< "3 - вывести все слова;\n"
|
||||||
|
<< "4 - завершить работу.\n\n";
|
||||||
|
|
||||||
|
/* Основной цикл обработки ввода */
|
||||||
|
while (true) {
|
||||||
|
string input;
|
||||||
|
if (!getline(cin, input)) break;
|
||||||
|
if (input.empty()) {
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int commaPos = input.find(',');
|
||||||
|
int command;
|
||||||
|
string cmdStr, word;
|
||||||
|
if (commaPos == (int)string::npos) {
|
||||||
|
cmdStr = input;
|
||||||
|
} else {
|
||||||
|
cmdStr = input.substr(0, commaPos);
|
||||||
|
if (commaPos + 1 < (int)input.size()) {
|
||||||
|
word = input.substr(commaPos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
command = stoi(cmdStr);
|
||||||
|
} catch (...) {
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 0:
|
||||||
|
dictionary.clear();
|
||||||
|
cout << "Словарь очищен\n";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (word.empty() || !dictionary.validateWord(word)) { // Добавлена проверка
|
||||||
|
cout << "Недопустимые символы в строке!\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (dictionary.insert(word))
|
||||||
|
cout << "Строка \"" << word << "\" добавлена в словарь\n";
|
||||||
|
else
|
||||||
|
cout << "Строка \"" << word << "\" уже есть в словаре\n";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (word.empty()) {
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (dictionary.remove(word))
|
||||||
|
cout << "Строка \"" << word << "\" удалена из словаря\n";
|
||||||
|
else
|
||||||
|
cout << "Строка \"" << word << "\" не найдена в словаре\n";
|
||||||
|
break;
|
||||||
|
case 3: {
|
||||||
|
vector<string> allWords = dictionary.getAllWords();
|
||||||
|
if (allWords.empty()) {
|
||||||
|
cout << "Словарь пуст\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < (int)allWords.size(); i++) {
|
||||||
|
cout << allWords[i];
|
||||||
|
if (i + 1 < (int)allWords.size()) cout << ", ";
|
||||||
|
}
|
||||||
|
cout << "\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
cout << "Некорректный ввод!\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Список изменений:
|
||||||
|
\begin{itemize}
|
||||||
|
\item В строке 21: Переименован параметр \texttt{w} в \texttt{word}.
|
||||||
|
\item В строке 22: Добавлена явная проверка на nullptr вместо неявного приведения к bool.
|
||||||
|
\item В строке 73: Добавлен явный деструктор для очистки памяти.
|
||||||
|
\item В строках 81-88: Реализована проверка на допустимые символы (латиница и кириллица).
|
||||||
|
\item В строках 20, 57, 65: Добавлены поясняющие комментарии к методам.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\section{Обобщение работы группы}
|
||||||
|
В ходе работы группы в программе были выявлены следующие недостатки: отсутствовала проверка корректности входных данных (например, на допустимые символы в строках); некоторые параметры методов имели неудачные названия (например, параметр \texttt{w} вместо \texttt{word}); сложные для понимания фрагменты кода, такие как рекурсивные методы работы с деревом, не были снабжены комментариями.
|
||||||
|
|
||||||
|
Сделан вывод, что код в целом соответствует стандартам оформления, что делает его удобным для чтения и анализа. Однако для улучшения читаемости и надёжности были предложены и внедрены изменения.
|
||||||
|
|
||||||
|
Получен опыт проведения ручного тестирования методом инспекции кода, совместного разбора кода в группе, а также формулирования ответов на вопросы о реализации программы.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Заключение}
|
||||||
|
\addcontentsline{toc}{section}{Заключение}
|
||||||
|
|
||||||
|
В ходе выполнения данной лабораторной работы был изучен метод ручного тестирования
|
||||||
|
— инспекция кода. Было проведено инспекционное собрание, на котором участвовали секретарь, программист и специалист по тестированию.
|
||||||
|
По итогу собрания был составлен протокол заседания, далее программистом был проведен
|
||||||
|
анализ составленного протокола и внесены необходимые изменения в код программы.
|
||||||
|
В результате выполненной работы, можно сделать вывод, что методы ручного тестирования, в частности инспекция кода, являются эффективными для выявления ошибок в логике
|
||||||
|
программы, а также для выявления несоответствий требованиям.
|
||||||
|
|
||||||
|
В результате работы я пришёл к выводу, что методы ручного тестирования, особенно инспекция кода, эффективны: удалось улучшить читаемость кода, добавить поясняющие комментарии и исправить некоторые недочёты. В частности, было добавлено считывание словаря из файла, выход из бесконечного цикла и обработка пустых строк при вводе для повышения удобства и безопасности.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Список литературы}
|
||||||
|
\addcontentsline{toc}{section}{Список литературы}
|
||||||
|
|
||||||
|
\vspace{-1.5cm}
|
||||||
|
\begin{thebibliography}{0}
|
||||||
|
\bibitem{mayers}
|
||||||
|
Майерс, Г. Искусство тестирования программ. -- Санкт-Петербург: Диалектика, 2012 г.
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
7
lab2/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
**/*
|
||||||
|
!.gitignore
|
||||||
|
!report.tex
|
||||||
|
!img
|
||||||
|
!img/**
|
||||||
|
!programm
|
||||||
|
!programm/*.py
|
||||||
BIN
lab2/img/img1.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
lab2/img/img2.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
lab2/img/programm1/block-schema-1.png
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
lab2/img/programm1/bounds.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
lab2/img/programm1/classes-test-dop.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
lab2/img/programm1/classes-test-nedop.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
lab2/img/programm1/classes.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
lab2/img/programm1/diagram.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
lab2/img/programm1/table-decisions.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
lab2/img/programm1/table-operators.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
lab2/img/programm2/block-schema.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
lab2/img/programm2/bounds.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
lab2/img/programm2/classes-test-dop.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
lab2/img/programm2/classes-test-nedop.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
lab2/img/programm2/classes.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
lab2/img/programm2/comb.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
lab2/img/programm2/condition-coverage.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
lab2/img/programm2/decision-coverage.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
lab2/img/programm2/diagram.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
lab2/img/programm2/table-decisions.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
lab2/img/programm2/table-operators.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
929
lab2/report.tex
Normal file
@@ -0,0 +1,929 @@
|
|||||||
|
\documentclass[a4paper, final]{article}
|
||||||
|
%\usepackage{literat} % Нормальные шрифты
|
||||||
|
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||||
|
\usepackage{tabularx}
|
||||||
|
\usepackage[T2A]{fontenc}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[russian]{babel}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||||
|
\usepackage{ragged2e} %для растягивания по ширине
|
||||||
|
\usepackage{setspace} %для межстрочно го интервала
|
||||||
|
\usepackage{moreverb} %для работы с листингами
|
||||||
|
\usepackage{indentfirst} % для абзацного отступа
|
||||||
|
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||||
|
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||||
|
\usepackage{tikz}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{afterpage}
|
||||||
|
\usepackage{longtable}
|
||||||
|
\usepackage{float}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||||
|
\usepackage{pdflscape}
|
||||||
|
% \usepackage{lscape}
|
||||||
|
|
||||||
|
\usepackage{array}
|
||||||
|
\usepackage{multirow}
|
||||||
|
|
||||||
|
\renewcommand\verbatimtabsize{4\relax}
|
||||||
|
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||||
|
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||||
|
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||||
|
\usepackage{listings} %листинги
|
||||||
|
\usepackage{xcolor} % цвета
|
||||||
|
\usepackage{hyperref}% для гиперссылок
|
||||||
|
\usepackage{enumitem} %для перечислений
|
||||||
|
|
||||||
|
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||||
|
|
||||||
|
|
||||||
|
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||||
|
|
||||||
|
\hypersetup{colorlinks,
|
||||||
|
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||||
|
|
||||||
|
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||||
|
\lstloadlanguages{ SQL}
|
||||||
|
% включаем кириллицу и добавляем кое−какие опции
|
||||||
|
\lstset{tabsize=2,
|
||||||
|
breaklines,
|
||||||
|
basicstyle=\footnotesize,
|
||||||
|
columns=fullflexible,
|
||||||
|
flexiblecolumns,
|
||||||
|
numbers=left,
|
||||||
|
numberstyle={\footnotesize},
|
||||||
|
keywordstyle=\color{blue},
|
||||||
|
inputencoding=cp1251,
|
||||||
|
extendedchars=true
|
||||||
|
}
|
||||||
|
\lstdefinelanguage{MyC}{
|
||||||
|
language=SQL,
|
||||||
|
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||||
|
% identifierstyle=\color{black},
|
||||||
|
% morecomment=[n]{/**}{*/},
|
||||||
|
% commentstyle=\color{blue}\ttfamily,
|
||||||
|
% stringstyle=\color{red}\ttfamily,
|
||||||
|
% morestring=[b]",
|
||||||
|
% showstringspaces=false,
|
||||||
|
% morecomment=[l][\color{gray}]{//},
|
||||||
|
keepspaces=true,
|
||||||
|
escapechar=\%,
|
||||||
|
texcl=true
|
||||||
|
}
|
||||||
|
|
||||||
|
\textheight=24cm % высота текста
|
||||||
|
\textwidth=16cm % ширина текста
|
||||||
|
\oddsidemargin=0pt % отступ от левого края
|
||||||
|
\topmargin=-1.5cm % отступ от верхнего края
|
||||||
|
\parindent=24pt % абзацный отступ
|
||||||
|
\parskip=5pt % интервал между абзацами
|
||||||
|
\tolerance=2000 % терпимость к "жидким" строкам
|
||||||
|
\flushbottom % выравнивание высоты страниц
|
||||||
|
|
||||||
|
|
||||||
|
% Настройка листингов
|
||||||
|
\lstset{
|
||||||
|
language=python,
|
||||||
|
extendedchars=\true,
|
||||||
|
inputencoding=utf8,
|
||||||
|
keepspaces=true,
|
||||||
|
% captionpos=b, % подписи листингов снизу
|
||||||
|
}
|
||||||
|
|
||||||
|
\begin{document} % начало документа
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||||
|
\begin{center}
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||||
|
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||||
|
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||||
|
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||||
|
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||||
|
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\large{Лабораторная работа №2}\\
|
||||||
|
\large{<<Тестирование ПО методом белого и чёрного ящика>>}\\
|
||||||
|
\large{по дисциплине}\\
|
||||||
|
\large{<<Методы тестирования программного обеспечения>>}\\
|
||||||
|
\hfill \break
|
||||||
|
|
||||||
|
% \hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\small{
|
||||||
|
\begin{tabular}{lrrl}
|
||||||
|
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||||
|
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||||
|
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Курочкин М. А. \\\\
|
||||||
|
&&\hspace{4cm}
|
||||||
|
\end{tabular}
|
||||||
|
\begin{flushright}
|
||||||
|
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
|
||||||
|
\end{flushright}
|
||||||
|
}
|
||||||
|
|
||||||
|
\hfill \break
|
||||||
|
% \hfill \break
|
||||||
|
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||||
|
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||||
|
|
||||||
|
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\tableofcontents
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Введение}
|
||||||
|
\addcontentsline{toc}{section}{Введение}
|
||||||
|
Одним из ключевых этапов в современной разработке программного обеспечения является тестирование. Существует несколько видов тестирования: модульное, интеграционное, функциональное, системное и приемочное. В данной работе будет рассмотрено модульное тестирование.
|
||||||
|
|
||||||
|
Модульное или unit-тестирование является основным видом тестирования, с которого практически всегда начинается проверка корректности работы программы. Этот вид тестирования включает проверку отдельных блоков ПО: классов, модулей и функций. Цель модульного тестирования -- выявить ошибки, то есть несоответствия поведения модуля его спецификации.
|
||||||
|
|
||||||
|
Существует два основных подхода к unit-тестированию: методы <<чёрного>> и <<белого ящика>>.
|
||||||
|
|
||||||
|
В данной работе будут рассмотрены оба метода. Также данные приёмы будут применены для тестирования двух программ:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item вычисление факториала числа;
|
||||||
|
\item возведение числа в степень.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Каждая из программ будет протестирована обоими методами с целью сравнения подходов.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Постановка задачи}
|
||||||
|
Цель работы: провести тестирование двух программ методами <<черного ящика>> и <<белого ящика>>. Для достижения цели, были выделены следующие задачи:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Изучить методы модульного тестирования, в частности методы <<белого ящика>> и <<черного ящика>>.
|
||||||
|
\item Разработать тесты для двух программ, и провести их тестирование используя оба метода на каждой программе.
|
||||||
|
\item Проанализировать результаты тестирования.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section {Описание методов тестирования}
|
||||||
|
\subsection{Метод черного ящика}
|
||||||
|
Метод черного ящика или тестирование управляемое данными, тестирование управляемое входом и выходом -- это метод тестирования, в котором программа рассматривается как <<черный ящик>>, внутреннее
|
||||||
|
поведение и структура которого не имеют никакого значения. Вместо этого все внимание фокусируется на выяснении обстоятельств, при которых поведение программы не соответствует спецификации.
|
||||||
|
|
||||||
|
Единственной предоставляемой информацией о программе является
|
||||||
|
ее спецификация, полностью описывающая поведение программы при
|
||||||
|
различных входных данных.
|
||||||
|
|
||||||
|
При таком подходе тестовые данные выбираются исключительно на
|
||||||
|
основе спецификаций требований (без привлечения каких-либо знаний о
|
||||||
|
внутренней структуре программы).
|
||||||
|
|
||||||
|
Чтобы в рамках данного метода обнаружить все ошибки в программе, необходимо выполнить так называемое исчерпывающее входное тестирование (exhaustive input testing), т.е. перебрать все возможные комбинации входных данных
|
||||||
|
|
||||||
|
\subsubsection{Эквивалентное разбиение}
|
||||||
|
Класс эквивалентности -- конечный набор входных данных, позволяющий допускать, что тестирования представительного значения данного класса эквивалентно тестированию любого другого значения принадлежащего тому же классу.
|
||||||
|
|
||||||
|
Разбитие области входных данных на конечное число связано с проблемой, того, что для осуществления исчерпывающего тестирования на
|
||||||
|
всех входных наборах данных, неосуществимо, а также преследует следующие цели:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item уменьшение более чем на единицу числа других тестов, которые
|
||||||
|
должны быть разработаны для достижения поставленной цели
|
||||||
|
<<обеспечить приемлемое тестирование>>;
|
||||||
|
\item покрытие значительной части других возможных тестов, т.е. предоставление некой информации относительно наличия или отсутствия
|
||||||
|
ошибок в ситуациях, не охватываемых данным конкретным набором входных значений.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsubsection*{Определение классов эквивалентности}
|
||||||
|
Определение классов эквивалентности сводится к последовательному рассмотрению каждого из входных условий (обычно это предложение
|
||||||
|
или фраза, приведенные в спецификации) и разбиению его на две или
|
||||||
|
несколько групп.
|
||||||
|
|
||||||
|
Определяется два типа классов:
|
||||||
|
\begin{itemize}
|
||||||
|
\item допустимые классы эквивалентности -- допустимые входные данные программы;
|
||||||
|
\item недопустимые классы эквивалентности -- все остальные возможные состояния условий (т.е. недопустимые входные значения).
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Если есть вероятность, что не для всех значений из одного класса эквивалентности будет одинаковый результат, то следует разбить класс на
|
||||||
|
более мелкие подклассы.
|
||||||
|
|
||||||
|
Определенные классы эквивалентности используются для составления тестов.
|
||||||
|
|
||||||
|
\subsubsection*{Разработка тестов}
|
||||||
|
|
||||||
|
Определение тестов по построенным классам эквивалентности состоит из трех этапов:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item назначить каждому классу эквивалентности уникальный номер;
|
||||||
|
\item записать новые тесты, охватывающие как можно большее количество оставшихся неохваченными допустимых классов эквивалентности, пока не будут покрыты все допустимые классы;
|
||||||
|
\item записать новые тесты, каждый из которых охватывает один и только один из оставшихся неохваченными недопустимых классов эквивалентности, пока не будут покрыты все недопустимые классы.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\subsubsection{Анализ граничных значений}
|
||||||
|
Граничные условия -- это ситуации, возникающие в области граничных значений входных эквивалентности. Анализ граничных значений отличается от методики разбиения на классы эквивалентности в следующем отношении:
|
||||||
|
вместо того чтобы выбирать любой элемент класса эквивалентности в качестве представителя всего класса, анализ граничных значений требует выбирать такой элемент или элементы, которые обеспечивают тестирование каждой границы класса.
|
||||||
|
|
||||||
|
\subsubsection{Причинно-следственные диаграммы}
|
||||||
|
Метод причинно-следственных диаграмм или метод диаграмм Исикавы -- метод, позволяющий выбирать высокорезультативные тесты.
|
||||||
|
Его дополнительным преимуществом является то, что он позволяет обнаруживать неполноту и неоднозначность исходных спецификаций.
|
||||||
|
|
||||||
|
Метод позволяет решить проблему того, что метод анализа граничных значений и разбиения данных на классы эквивалентности не исследует комбинации входных условий.
|
||||||
|
|
||||||
|
Причинно-следственная диаграмма представляет собой формальный
|
||||||
|
язык, на который транслируется спецификация, написанная на естественном языке.
|
||||||
|
|
||||||
|
Для построения тестов используется процесс, включающий несколько этапов:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Спецификация разбивается на части, с которыми легче работать.
|
||||||
|
\item В спецификации определяются причины и следствия.
|
||||||
|
|
||||||
|
Причина -- это отдельное входное условие или класс эквивалентности входных условий.
|
||||||
|
Следствие -- это выходное условие или преобразование системы
|
||||||
|
Причины и следствия определяются путем последовательного (слово за словом) чтения спецификации и подчеркивания тех слов или
|
||||||
|
фраз, которые описывают причины и следствия. Каждой причине и каждому следствию присваивается уникальный номер.
|
||||||
|
\item Семантическое содержание спецификации анализируется и преобразуется в булев граф, связывающий причины и следствия. Полу-
|
||||||
|
ченный граф называется причинно-следственной диаграммой.
|
||||||
|
\item Диаграмма снабжается примечаниями, задающими ограничения и
|
||||||
|
описывающими комбинации причин и (или) следствий, реализация
|
||||||
|
которых невозможна из-за синтаксических или внешних ограничений. Нотация отображений ограничений представлена на Рис.~\ref{fig:img1}.
|
||||||
|
\item Путем методичного прослеживания состояний условий диаграмма преобразуется в таблицу решений с ограниченными входами
|
||||||
|
(limited-entry decision table). Каждый столбец таблицы решений соответствует тесту.
|
||||||
|
\item Столбцы таблицы решений преобразуются в тесты.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
Базовая нотация причинно-следственных диаграмм представлена на
|
||||||
|
Рис.~\ref{fig:img1}. Каждый узел диаграммы может находиться в двух состояниях: 0
|
||||||
|
или 1, где 0 представляет состояние <<отсутствует>>, а 1 -- <<присутствует>>.
|
||||||
|
\begin{itemize}
|
||||||
|
\item Функция тождество устанавливает, что если а равно 1, то и b равно 1; в противном случае b равно 0.
|
||||||
|
\item Функция not устанавливает, что если а равно 1, то b равно 0; в
|
||||||
|
противном случае b равно 1.
|
||||||
|
\item Функция or устанавливает, что если а, или b, или с равно 1, то d
|
||||||
|
равно 1; в противном случае d равно 0.
|
||||||
|
\item Функция and устанавливает, что если и а, и b равны 1, то с равно
|
||||||
|
1; в противном случае с равно 0.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.5\linewidth]{img/img1.png}
|
||||||
|
\caption{ Базовая нотация, используемая в причинно-следственных диаграммах.}
|
||||||
|
\label{fig:img1}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Также для установления связи между входными условиями могут использоваться следующие ограничения: Существуют следующие ограничения.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Ограничение требует, чтобы всегда выполнялось условие, в соответствии с которым только или только b может быть равно 1 ( и b
|
||||||
|
не могут быть равны 1 одновременно, но обе величины могут быть
|
||||||
|
равны 0).
|
||||||
|
\item Ограничение I требует, чтобы по крайней мере одна из величин, a,
|
||||||
|
b или , была равна 1 (, b и не могут быть равны 0 одновременно).
|
||||||
|
\item Ограничение требует, чтобы одна и только одна из величин, a или
|
||||||
|
b,была равна 1.
|
||||||
|
\item Ограничение R требует, чтобы a было равно 1, только если b равно 1 (т.е. не может быть равно 1, если b равно 0). На Рис.~\ref{fig:img2}
|
||||||
|
представлены символы ограничений.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.6\linewidth]{img/img2.png}
|
||||||
|
\caption{Базовая нотация ограничений, используемая в причинно-следственных диаграммах.}
|
||||||
|
\label{fig:img2}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
При составлении причинно-следственной диаграммы возможно введение ограничения не использующегося в базовой нотации и характерного только для рассматриваемого случая. В таком случае, вводимое
|
||||||
|
ограничение всегда сопровождается пояснительной подписью.
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Метод белого ящика}
|
||||||
|
Метод белого ящика -- метод тестирования основанный на анализе внутренней структуры кода. Для данного тестирования используется спецификация программы, описывающая её поведение и блоксхема, по которой тестировщик может осуществлять тестирование на
|
||||||
|
уровне отдельных модулей, функций и логики программы.
|
||||||
|
Тестирование методом белого ящика имеет и другое название, отражающее суть данной методологии: тестирование с управляемой логикой
|
||||||
|
программы (logic-driving testing). При использовании данной методологии, тестировщик подбирает тестовые данные путём анализа логики программы с учетом её спецификации.
|
||||||
|
Исчерпывающее тестирование методом <<белого ящика>> практически
|
||||||
|
невозможно в следствии необходимости перебирать огромное количество
|
||||||
|
данных. Поэтому для упрощения тестирования применяются следующие
|
||||||
|
эвристические методы:
|
||||||
|
|
||||||
|
\subsubsection{Покрытие операторов}
|
||||||
|
Критерий покрытия операторов/инструкций заключается в построении тестов, в которых каждая инструкция выполнялась как минимум один раз. Является необходимым но недостаточным критерием
|
||||||
|
для тестирования программы методом <<белого ящика>>
|
||||||
|
|
||||||
|
\subsubsection{Покрытие решений}
|
||||||
|
Критерий покрытие решений (decision coverage) или покрытие
|
||||||
|
ветвлений заключается в построении тестов таким образом, чтобы каждое условие в программе хотя бы раз принимало как значение true (истина), так и false (ложь). Иными словами, каждая логическая ветвь каждой инструкции ветвления в программе должна быть выполнена хотя бы
|
||||||
|
один раз.
|
||||||
|
Обычно покрытие решений удовлетворяет критерию покрытия операторов/инструкций. Однако это правило не действует по крайней мере
|
||||||
|
в трех перечисленных ниже случаях.
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Программы, не содержащие точек ветвления.
|
||||||
|
\item Программы или подпрограммы (методы), имеющие несколько точек входа. В таком случае некоторые инструкции будут выполняться лишь тогда, когда выполнение программы начинается с определенной точки входа.
|
||||||
|
\item Инструкции в блоках ON. Выполнение всех ветвей не обязательно
|
||||||
|
приведет к выполнению всех блоков ON.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\subsubsection{Покрытие решений и условий}
|
||||||
|
Согласно этому критерию набор тестов является достаточно полным, если выполняются следующие требования:
|
||||||
|
\begin{itemize}
|
||||||
|
\item каждое условие в решении принимает каждое возможное значение
|
||||||
|
по крайней мере один раз;
|
||||||
|
\item каждый возможный исход решения проверяется по крайней мере
|
||||||
|
один раз;
|
||||||
|
\item каждой точке входа управление передается по крайней мере один
|
||||||
|
раз.
|
||||||
|
\end{itemize}
|
||||||
|
Однако, несмотря на кажущуюся возможность охвата им всех возможных исходов решений для всех условий, это зачастую не обеспечивается
|
||||||
|
из-за маскирования одних условий
|
||||||
|
|
||||||
|
\subsubsection{Комбинаторное покрытие условий}
|
||||||
|
Комбинаторное покрытие условий (multiple-condition covering) --
|
||||||
|
критерий, требующий создания такого количества тестов, при котором
|
||||||
|
каждая возможная комбинация результатов вычисления условий в каждом решении и каждая точка входа проверяются по крайней мере один
|
||||||
|
раз.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Тестирование программы №1}
|
||||||
|
\subsection{Формальное описание программы}
|
||||||
|
\textbf{Название:} «Вычисление факториала числа».
|
||||||
|
|
||||||
|
\textbf{Дано:}
|
||||||
|
\begin{itemize}
|
||||||
|
\item $N$ --- целое положительное число.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
|
\textbf{Требуется:} Вычислить факториал числа $N$ и вывести результат на экран.
|
||||||
|
|
||||||
|
\textbf{Ограничения:}
|
||||||
|
\begin{itemize}
|
||||||
|
\item $1 \leq N \leq 65$;
|
||||||
|
\item $N$ --- целое.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\textbf{Спецификация}
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
%\caption{Спецификация}
|
||||||
|
%\label{.}
|
||||||
|
\footnotesize
|
||||||
|
\begin{tabularx}{\textwidth}{|X|X|X|}
|
||||||
|
\hline
|
||||||
|
\textbf{Входные данные} & \textbf{Выходные данные} & \textbf{Реакция программы} \\
|
||||||
|
\hline
|
||||||
|
$N = -5$ & ''Ошибка! Введите положительное целое число:'' & Вывод на экран сообщения: ''Ошибка! Введите положительное целое число:''. Ожидание корректного ввода $N$. \\
|
||||||
|
\hline
|
||||||
|
$N = 5.7$ & ''Ошибка! Введите целое число, а не дробное:'' & Вывод на экран сообщения: ''Ошибка! Введите целое число, а не дробное:''. Ожидание корректного ввода $N$. \\
|
||||||
|
\hline
|
||||||
|
$N =$ ''пять'' & ''Ошибка! Введите целое число, а не строку:'' & Вывод на экран сообщения: ''Ошибка! Введите целое число, а не строку:''. Ожидание корректного ввода $N$. \\
|
||||||
|
\hline
|
||||||
|
$N = 70$ & ''Ошибка! Введите целое положительное число, не более 65:'' & Вывод на экран сообщения: ''Ошибка! Введите целое положительное число, не более 65:''. Ожидание корректного ввода $N$. \\
|
||||||
|
\hline
|
||||||
|
$N = 5$ & 120 & Вывод на экран значения факториала для заданного числа $N$. Завершение программы. \\
|
||||||
|
\hline
|
||||||
|
$N = 0$ & 1 & Вывод на экран значения факториала для заданного числа $N$. Завершение программы. \\
|
||||||
|
\hline
|
||||||
|
$N = 1$ & 1 & Вывод на экран значения факториала для заданного числа $N$. Завершение программы. \\
|
||||||
|
\hline
|
||||||
|
\end{tabularx}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\textbf{Блок-схема}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm1/block-schema-1.png}
|
||||||
|
\caption{Блок схема программы №1.}
|
||||||
|
\label{fig:programm1/block-schema-1}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsection{Тестирование методом «белого ящика»}
|
||||||
|
Алгоритм составления тестов методом «белого» ящика предполагает обход всех
|
||||||
|
возможных путей в теле программы и проверку выполнения каждого оператора не
|
||||||
|
менее одного раза. Для этого на блок-схеме программы, которая изображена на Рис.~\ref{fig:programm1/block-schema-1}, все возможные пути обозначены символами латинского алфавита от~A~до~I.
|
||||||
|
|
||||||
|
Условия в ветвлениях программы:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item $N > 65$;
|
||||||
|
\item $N < 0$;
|
||||||
|
\item $N$ строка?
|
||||||
|
\item $N$ дробное?
|
||||||
|
\item $k \neq N$
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
|
||||||
|
\subsubsection{Покрытие операторов}
|
||||||
|
Критерием покрытия является выполнение каждого оператора программы хотя
|
||||||
|
бы один раз. Это необходимое, но не достаточное условие для приемлемого тестирования по принципу белого ящика.
|
||||||
|
|
||||||
|
Для покрытия всех операторов был составлен набор тестов из 6 тестов (Рис.~\ref{fig:programm1/table-operators}).
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm1/table-operators.png}
|
||||||
|
\caption{Набор тестов для покрытия операторов программы.}
|
||||||
|
\label{fig:programm1/table-operators}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
При тестировании покрытия операторов был составлен тест (№3), который программа не проходит. Программа не может пройти тест №3, так как была допущена ошибка при составлении блок-схемы программы. При вводе строки программа завершается при попытке сравнить строку с числом 65, хотя по спецификации должна выводить строку <<Ошибка! Введите целое число, а не строку>>.
|
||||||
|
|
||||||
|
|
||||||
|
\subsubsection{Покрытие решений}
|
||||||
|
В соответствии с этим критерием необходимо составить такой набор тестов, при котором каждое условие в программе примет как истинное, так и ложное значения. Таким образом, к тестам, составленным для метода покрытия операторов, необходимо добавить тесты, которые будут проверять все возможные переходы.
|
||||||
|
|
||||||
|
Тесты, покрывающие все решения программы представлены на Рис.~\ref{fig:programm1/table-operators-2}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm1/table-operators.png}
|
||||||
|
\caption{Набор тестов для покрытия решений программы.}
|
||||||
|
\label{fig:programm1/table-operators-1}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Покрыть истинную ветку условия 3 (путь B->D->E) невозможно из-за экстренного завершения программы при вводе строкового значения N.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Покрытие условий}
|
||||||
|
В соответствии с этим критерием количество тестов должно быть таким, чтобы
|
||||||
|
все возможные результаты каждого условия в решении выполнялись по крайней мере
|
||||||
|
один раз.
|
||||||
|
|
||||||
|
Тесты, покрывающие все условия программы представлены на Рис.~\ref{fig:programm1/table-operators-2}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm1/table-operators.png}
|
||||||
|
\caption{Набор тестов для покрытия решений программы.}
|
||||||
|
\label{fig:programm1/table-operators-2}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Покрыть истинную ветку условия 3 (путь B->D->E) невозможно из-за экстренного завершения программы при вводе строкового значения N.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Покрытие решений и условий}
|
||||||
|
Согласно этому критерию набор тестов является достаточно полным, если удовлетворяются следующие требования: каждое условие в решении принимает каждое
|
||||||
|
возможное значение по крайней мере один раз, каждый возможный исход решения
|
||||||
|
проверяется по крайней мере один раз и каждой точке входа управление передается
|
||||||
|
по крайней мере один раз.
|
||||||
|
|
||||||
|
Тесты, написанные ранее, обеспечивают покрытие решений и условий (Рис.~\ref{fig:programm1/table-operators-3}).
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm1/table-operators.png}
|
||||||
|
\caption{Набор тестов для покрытия решений программы.}
|
||||||
|
\label{fig:programm1/table-operators-3}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Покрыть истинную ветку условия 3 (путь B->D->E) невозможно из-за экстренного завершения программы при вводе строкового значения N.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Комбинаторное покрытие условий}
|
||||||
|
Этот критерий требует создания такого набора тестов, при котором каждая возможная комбинация результатов вычисления условий в каждом решении и
|
||||||
|
каждая точка входа проверяются по крайней мере один раз.
|
||||||
|
|
||||||
|
Совокупность всех ранее написанных тестов дает покрытие условий и решений (Рис.~\ref{fig:programm1/table-operators-4}).
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm1/table-operators.png}
|
||||||
|
\caption{Набор тестов для покрытия решений программы.}
|
||||||
|
\label{fig:programm1/table-operators-4}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Покрыть истинную ветку условия 3 (путь B->D->E) невозможно из-за экстренного завершения программы при вводе строкового значения N.
|
||||||
|
|
||||||
|
\subsubsection{Результаты тестирования методом <<белого ящика>>}
|
||||||
|
В результате тестирования методом «белого ящика» было составлено 6 тестов,
|
||||||
|
из которых были пройдены 5 и не пройден 1. При строковом значении $N$ программа
|
||||||
|
должна вывести сообщение ”Ошибка! Введите целое число, а не строку:” и ожидать
|
||||||
|
повторного ввода значения $N$, однако она экстренно завершается с кодом -1 на первом блоке ветвления ($N$ > 65) при попытке сравнить строку с числом, что означает
|
||||||
|
некорректно составленную программу и спецификацию.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsection{Тестирование методом <<чёрного ящика>>}
|
||||||
|
\subsubsection{Разбиение на классы эквивалентности}
|
||||||
|
Составлено разбиение на классы эквивалентности исходя из ограничений для
|
||||||
|
программы, представленное на Рис.~\ref{fig:programm1/classes}. Тесты для допустимых классов эквивалентности представлены на Рис.~\ref{fig:programm1/classes-test-dop} и тесты для недопустимых классов эквивалентности
|
||||||
|
— на Рис.~\ref{fig:programm1/classes-test-nedop}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.7\linewidth]{img/programm1/classes.png}
|
||||||
|
\caption{Разбиение на классы эквивалентности.}
|
||||||
|
\label{fig:programm1/classes}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.9\linewidth]{img/programm1/classes-test-dop.png}
|
||||||
|
\caption{Тесты для допустимых классов эквивалентности.}
|
||||||
|
\label{fig:programm1/classes-test-dop}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.9\linewidth]{img/programm1/classes-test-nedop.png}
|
||||||
|
\caption{Тесты для недопустимых классов эквивалентности.}
|
||||||
|
\label{fig:programm1/classes-test-nedop}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
При тестировании недопустимых классов эквивалентности был составлен тест, который программа не проходит. Программа не может пройти тест №3, так как была допущена ошибка при составлении блок-схемы программы. При вводе строки программа завершается при попытке сравнить строку с числом 65, хотя по спецификации должна выводить строку <<Ошибка! Введите целое число, а не строку>>.
|
||||||
|
|
||||||
|
\subsubsection{Анализ граничных условий}
|
||||||
|
В программе можно выделить следующие граничные условия:
|
||||||
|
\begin{itemize}
|
||||||
|
\item $N \geq 0$;
|
||||||
|
\item $N \leq 65$.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Для каждой из границ определим тесты, соответствующие:
|
||||||
|
\begin{itemize}
|
||||||
|
\item граничному целому числу (верхнему/нижнему);
|
||||||
|
\item целому числу, выходящему за границу (верхнюю/нижнюю) на единицу;
|
||||||
|
\item дробному числу, на 0.001 выходящему за границу (верхнюю/нижнюю).
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Составленные тесты представлены на Рис.~\ref{fig:programm1/bounds}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.8\linewidth]{img/programm1/bounds.png}
|
||||||
|
\caption{Тесты граничных условий.}
|
||||||
|
\label{fig:programm1/bounds}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
При тестировании граничных условий был составлен тест, который программа не проходит. Программа не может пройти тест №1 (Рис.~\ref{fig:programm1/bounds}), так как была допущена ошибка при составлении блок-схемы программы. При вводе числа 0 программа зависает в бесконечном цикле (из-за условия <<k != N>>), хотя по спецификации должна выводить число 1.
|
||||||
|
|
||||||
|
\subsubsection{Причинно-следственная диаграмма}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.6\linewidth]{img/programm1/diagram.png}
|
||||||
|
\caption{Причинно следственная диаграмма.}
|
||||||
|
\label{fig:programm1/diagram}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\textbf{Причины:}
|
||||||
|
\begin{enumerate}
|
||||||
|
\item $N$ -- число;
|
||||||
|
\item $N$ -- целое;
|
||||||
|
\item $N \geq 0$;
|
||||||
|
\item $N \leq 65$.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\textbf{Промежуточные причины:}
|
||||||
|
\begin{enumerate}
|
||||||
|
\item[1.1] $N$ -- целое и $0 \leq N \leq 65$.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\textbf{Следствия:}
|
||||||
|
\begin{enumerate}
|
||||||
|
\item[2.1] Программа выводит сообщение об ошибке ($N$ -- строка) и заново запрашивает число;
|
||||||
|
\item[2.2] Программа выводит сообщение об ошибке ($N$ -- дробное) и заново запрашивает число;
|
||||||
|
\item[2.3] Программа выводит значение факториала для числа $N$;
|
||||||
|
\item[2.4] Программа выводит сообщение об (ошибке $N < 0$) и заново запрашивает число;
|
||||||
|
\item[2.5] Программа выводит сообщение об (ошибке $N > 65$) и заново запрашивает число.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
На Рис.~\ref{fig:programm1/diagram} представлена причинно-следственная диаграмма.
|
||||||
|
|
||||||
|
Таблица решений для диаграммы представлена на Рис.~\ref{fig:programm1/table-decisions}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm1/table-decisions.png}
|
||||||
|
\caption{Таблица решений.}
|
||||||
|
\label{fig:programm1/table-decisions}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Все тесты для таблицы решений уже были покрыты ранее при рассмотрении
|
||||||
|
классов эквивалентности и граничных условий.
|
||||||
|
|
||||||
|
\subsubsection{Результаты тестирования методом <<чёрного ящика>>}
|
||||||
|
В результате тестирования методом чёрного ящика было составлено 11 тестов, из которых не пройдено 2. Ошибки, из-за которых тесты не были пройдены, связаны с некорректной проверкой входных значений и неверно составленной спецификацией: программа экстренно завершается при вводе вместо числа $N$ строки, а также входит в бесконечный цикл при $N = 0$, что не соответствует поведению, описанному в спецификакции.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Тестирование программы №2}
|
||||||
|
\subsection{Формальное описание программы}
|
||||||
|
\textbf{Название:} «Алгоритм быстрого возведения в степень».
|
||||||
|
|
||||||
|
\textbf{Дано:}
|
||||||
|
\begin{itemize}
|
||||||
|
\item $n$ --- целое положительное число.
|
||||||
|
\item $k$ --- целое положительное число.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
|
\textbf{Требуется:} Возвести число $n$ в степень $k$ и вывести результат на экран.
|
||||||
|
|
||||||
|
\textbf{Ограничения:}
|
||||||
|
\begin{itemize}
|
||||||
|
\item $n$ --- целое.
|
||||||
|
\item $k$ --- целое.
|
||||||
|
\item $1 \leq n \leq 15$;
|
||||||
|
\item $1 \leq k \leq 15$;
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\textbf{Спецификация}
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
%\caption{Спецификация}
|
||||||
|
%\label{.}
|
||||||
|
\footnotesize
|
||||||
|
\begin{tabularx}{\textwidth}{|X|X|X|}
|
||||||
|
\hline
|
||||||
|
\textbf{Входные данные} & \textbf{Выходные данные} & \textbf{Реакция программы} \\
|
||||||
|
\hline
|
||||||
|
$n = -5, k = 10$ & ''Ошибка! Введите числа от 1 до 15.'' & Вывод на экран сообщения: ''Ошибка! Введите числа от 1 до 15.''. Ожидание корректного ввода $n$ и $k$. \\
|
||||||
|
\hline
|
||||||
|
$n = 5, k = -10$ & ''Ошибка! Введите числа от 1 до 15.'' & Вывод на экран сообщения: ''Ошибка! Введите числа от 1 до 15.''. Ожидание корректного ввода $n$ и $k$. \\
|
||||||
|
\hline
|
||||||
|
$n = 25, k = 10$ & ''Ошибка! Введите числа от 1 до 15.'' & Вывод на экран сообщения: ''Ошибка! Введите числа от 1 до 15.''. Ожидание корректного ввода $n$ и $k$. \\
|
||||||
|
\hline
|
||||||
|
$n = 5.7, k = 10$ & ''Ошибка! n должно быть целым числом.'' & Вывод на экран сообщения: ''Ошибка! k должно быть целым числом.''. Ожидание корректного ввода $n$ и $k$. \\
|
||||||
|
\hline
|
||||||
|
$n =$ ''строка'' $, k = 10$ & ''Ошибка! n должно быть целым числом.'' & Вывод на экран сообщения: ''Ошибка! k должно быть целым числом.''. Ожидание корректного ввода $n$ и $k$. \\
|
||||||
|
\hline
|
||||||
|
$n = 2, k = 10$ & 1024 & Вывод на экран значения $2^10$. Завершение программы. \\
|
||||||
|
\hline
|
||||||
|
\end{tabularx}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\textbf{Блок-схема}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.75\linewidth]{img/programm2/block-schema.png}
|
||||||
|
\caption{Блок схема программы №2.}
|
||||||
|
\label{fig:programm2/block-schema}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsection{Тестирование методом «белого ящика»}
|
||||||
|
Алгоритм составления тестов методом «белого» ящика предполагает обход всех
|
||||||
|
возможных путей в теле программы и проверку выполнения каждого оператора не
|
||||||
|
менее одного раза. Для этого на блок-схеме программы, которая изображена на Рис.~\ref{fig:programm2/block-schema}, все возможные пути обозначены символами латинского алфавита от~A~до~I.
|
||||||
|
|
||||||
|
Условия в ветвлениях программы:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item $n$ целое число?
|
||||||
|
\item $k$ целое число?
|
||||||
|
\item $n < 1$;
|
||||||
|
\item $n > 15$;
|
||||||
|
\item $k < 1$;
|
||||||
|
\item $k > 15$;
|
||||||
|
\item $k \mod 2 = 1$;
|
||||||
|
\item $k > 0$.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
|
||||||
|
\subsubsection{Покрытие операторов}
|
||||||
|
Критерием покрытия является выполнение каждого оператора программы хотя
|
||||||
|
бы один раз. Это необходимое, но не достаточное условие для приемлемого тестирования по принципу белого ящика.
|
||||||
|
|
||||||
|
Для покрытия всех операторов был составлен набор тестов из 4 тестов (Рис.~\ref{fig:programm2/table-operators}).
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm2/table-operators.png}
|
||||||
|
\caption{Набор тестов для покрытия операторов программы.}
|
||||||
|
\label{fig:programm2/table-operators}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Для покрытия всех операторов программы достаточно было составить тесты для четырёх путей: A, BC, BDE, BDFHI.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Покрытие решений}
|
||||||
|
В соответствии с этим критерием необходимо составить такой набор тестов, при котором каждое условие в программе примет как истинное, так и ложное значения. Таким образом, к тестам, составленным для метода покрытия операторов, необходимо добавить тесты, которые будут проверять все возможные переходы.
|
||||||
|
|
||||||
|
Тесты, покрывающие все решения программы представлены на Рис.~\ref{fig:programm2/decision-coverage}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm2/decision-coverage.png}
|
||||||
|
\caption{Набор тестов для покрытия решений программы.}
|
||||||
|
\label{fig:programm2/decision-coverage}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsubsection{Покрытие условий}
|
||||||
|
В соответствии с этим критерием количество тестов должно быть таким, чтобы
|
||||||
|
все возможные результаты каждого условия в решении выполнялись по крайней мере
|
||||||
|
один раз.
|
||||||
|
|
||||||
|
Тесты, покрывающие все условия программы представлены на Рис.~\ref{fig:programm2/condition-coverage}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm2/condition-coverage.png}
|
||||||
|
\caption{Набор тестов для покрытия условий программы.}
|
||||||
|
\label{fig:programm2/condition-coverage}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Покрытие решений и условий}
|
||||||
|
Согласно этому критерию набор тестов является достаточно полным, если удовлетворяются следующие требования: каждое условие в решении принимает каждое
|
||||||
|
возможное значение по крайней мере один раз, каждый возможный исход решения
|
||||||
|
проверяется по крайней мере один раз и каждой точке входа управление передается
|
||||||
|
по крайней мере один раз.
|
||||||
|
|
||||||
|
Тесты, написанные ранее, обеспечивают покрытие решений и условий (Рис.~\ref{fig:programm2/condition-coverage-2}).
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm2/condition-coverage.png}
|
||||||
|
\caption{Набор тестов для покрытия решений и условий программы.}
|
||||||
|
\label{fig:programm2/condition-coverage-2}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Комбинаторное покрытие условий}
|
||||||
|
Этот критерий требует создания такого набора тестов, при котором каждая возможная комбинация результатов вычисления условий в каждом решении и
|
||||||
|
каждая точка входа проверяются по крайней мере один раз.
|
||||||
|
|
||||||
|
Тесты, обеспечивающие комбинаторное покрытие условий, представлены на Рис.~\ref{fig:programm2/comb}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=1\linewidth]{img/programm2/comb.png}
|
||||||
|
\caption{Набор тестов для комбинаторного покрытия условий программы.}
|
||||||
|
\label{fig:programm2/comb}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Покрыть истинную ветку условия 3 (путь B->D->E) невозможно из-за экстренного завершения программы при вводе строкового значения N.
|
||||||
|
|
||||||
|
\subsubsection{Результаты тестирования методом <<белого ящика>>}
|
||||||
|
В результате тестирования методом «белого ящика» было составлено 11 тестов. Программа прошла все 11 тестов. Это может говорить как о корректности программы, так и о том, что данный метод просто не подходит для тестирования данной программы, так как не рассматривает какую-либо ситуацию, в которой бы возникла ошибка.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsection{Тестирование методом <<чёрного ящика>>}
|
||||||
|
\subsubsection{Разбиение на классы эквивалентности}
|
||||||
|
Составлено разбиение на классы эквивалентности исходя из ограничений для
|
||||||
|
программы, представленное на Рис.~\ref{fig:programm2/classes}. Тесты для допустимых классов эквивалентности представлены на Рис.~\ref{fig:programm2/classes-test-dop} и тесты для недопустимых классов эквивалентности
|
||||||
|
— на Рис.~\ref{fig:programm2/classes-test-nedop}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.6\linewidth]{img/programm2/classes.png}
|
||||||
|
\caption{Разбиение на классы эквивалентности.}
|
||||||
|
\label{fig:programm2/classes}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.75\linewidth]{img/programm2/classes-test-dop.png}
|
||||||
|
\caption{Тесты для допустимых классов эквивалентности.}
|
||||||
|
\label{fig:programm2/classes-test-dop}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.75\linewidth]{img/programm2/classes-test-nedop.png}
|
||||||
|
\caption{Тесты для недопустимых классов эквивалентности.}
|
||||||
|
\label{fig:programm2/classes-test-nedop}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Программа прошла все 9 тестов, составленных при рассмотрении классов эквивалентности входных данных.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Анализ граничных условий}
|
||||||
|
В программе можно выделить следующие граничные условия:
|
||||||
|
\begin{itemize}
|
||||||
|
\item $n \geq 1$;
|
||||||
|
\item $k \leq 15$;
|
||||||
|
\item $n \geq 1$;
|
||||||
|
\item $k \leq 15$.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Для каждой из границ определим тесты, соответствующие:
|
||||||
|
\begin{itemize}
|
||||||
|
\item граничному целому числу (верхнему/нижнему);
|
||||||
|
\item целому числу, выходящему за границу (верхнюю/нижнюю) на единицу;
|
||||||
|
% \item дробному числу, на 0.001 выходящему за границу (верхнюю/нижнюю).
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Составленные тесты представлены на Рис.~\ref{fig:programm2/bounds}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.8\linewidth]{img/programm2/bounds.png}
|
||||||
|
\caption{Тесты граничных условий.}
|
||||||
|
\label{fig:programm2/bounds}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
При анализе граничных условий был составлен набор из 8 тестов. Программа прошла все 8 тестов.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsubsection{Причинно-следственная диаграмма}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.6\linewidth]{img/programm2/diagram.png}
|
||||||
|
\caption{Причинно следственная диаграмма.}
|
||||||
|
\label{fig:programm2/diagram}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\textbf{Причины:}
|
||||||
|
\begin{enumerate}
|
||||||
|
\item $n$ -- целое число;
|
||||||
|
% \item $n \geq 1$;
|
||||||
|
\item $1 \leq n \leq 15$;
|
||||||
|
% \item $k \geq 1$;
|
||||||
|
\item $1 \leq k \leq 15$.
|
||||||
|
\item $k$ -- целое число;
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\textbf{Промежуточные причины:}
|
||||||
|
\begin{enumerate}
|
||||||
|
\item[1.1] $n$ -- целое и $1 \leq n \leq 15$.
|
||||||
|
\item[1.2] $k$ -- целое и $1 \leq k \leq 15$.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\textbf{Следствия:}
|
||||||
|
\begin{enumerate}
|
||||||
|
\item[2.1] Программа выводит сообщение об ошибке ''Ошибка! n должно быть целым числом'' и заново запрашивает числа $n$ и $k$;
|
||||||
|
\item[2.2] Программа выводит значение $n^k$ и завершает работу;
|
||||||
|
\item[2.3] Программа выводит сообщение об ошибке ''Ошибка! Введите числа от 1 до 15'' и заново запрашивает числа $n$ и $k$;
|
||||||
|
\item[2.4] Программа выводит сообщение об ошибке ''Ошибка! k должно быть целым числом'' и заново запрашивает числа $n$ и $k$;
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
На Рис.~\ref{fig:programm2/diagram} представлена причинно-следственная диаграмма.
|
||||||
|
|
||||||
|
Таблица решений для диаграммы представлена на Рис.~\ref{fig:programm2/table-decisions}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.5\linewidth]{img/programm2/table-decisions.png}
|
||||||
|
\caption{Таблица решений.}
|
||||||
|
\label{fig:programm2/table-decisions}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Все тесты для таблицы решений уже были покрыты ранее при рассмотрении
|
||||||
|
классов эквивалентности и граничных условий.
|
||||||
|
|
||||||
|
\subsubsection{Результаты тестирования методом <<чёрного ящика>>}
|
||||||
|
В результате тестирования методом чёрного ящика было составлено 17 тестов. Программа прошла все тесты. Это может свидетельствовать как о корректности программы, так и о том, что данный метод просто не подходит для данной программы, так как не рассматривает какую-либо ситуацию, в которой бы возникла ошибка.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Заключение}
|
||||||
|
\addcontentsline{toc}{section}{Заключение}
|
||||||
|
В ходе выполнения данной лабораторной работы были изучены методологии модульного тестирования: метод <<белого ящика>> и метод <<чёрного ящика>>.
|
||||||
|
|
||||||
|
При помощи изученных методологий были спроектированы тесты для программы вычисления факториала числа и возведения числа в степень. При составлении тестов использовались методы:3
|
||||||
|
\begin{itemize}
|
||||||
|
\item Разбиения на классы эквивалентности;
|
||||||
|
\item Анализа граничных значений;
|
||||||
|
\item Причинно-следственной диаграммы;
|
||||||
|
\item Критерия покрытия операторов;
|
||||||
|
\item Критерия покрытия решений;
|
||||||
|
\item Критерия покрытия условий;
|
||||||
|
\item Критерия покрытия решений и условий;
|
||||||
|
\item Критерия комбинаторного покрытия условий.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Тестирование проводилось с помощью комбинированного подхода: сначала проводилось тестирование методом «белого ящика», затем добавлялись тесты, основанные на методе «черного ящика».
|
||||||
|
|
||||||
|
% Тестирование первой программы показало, что 11 тестов из 21, составленных методом чёрного ящика не были пройдены программой, и все 5 тестов, составленных методом белого ящика, были пройдены. Таким образом, сочетанием методов <<чёрного>> и <<белого>> ящиков удалось выявить ошибки в спецификации первой программы.
|
||||||
|
|
||||||
|
При тестировании первой программы методом чёрного ящика не было пройдено 2 теста, методом белого ящика --- 1 тест. Ошибки, из-за которых тесты не были пройдены, связаны с некорректной проверкой входных значений и неверно составленной спецификацией.
|
||||||
|
|
||||||
|
При тестировании второй программы методом чёрного ящика было составлено 17 тестов, методом белого ящика --- 11 тестов. В результате тестирования не удалось составить тест, который программа бы не прошла. Это может свидетельствовать как о корректности программы, так и о том, что данные методы просто не подходят для тестирования данной программы, так как не рассматривают какую-либо ситуацию, в которой бы возникла ошибка.
|
||||||
|
|
||||||
|
Более эффективным методом для проверки программ оказался метод <<чёрного ящика>>. С его помощью удалось составить два теста, которые программа не смогла пройти. С помощью метода <<белого ящика>> удалось составить только один такой тест.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Список литературы}
|
||||||
|
\addcontentsline{toc}{section}{Список литературы}
|
||||||
|
|
||||||
|
\vspace{-1.5cm}
|
||||||
|
\begin{thebibliography}{0}
|
||||||
|
\bibitem{mayers}
|
||||||
|
Майерс, Г. Искусство тестирования программ. -- Санкт-Петербург: Диалектика, 2012 г.
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
7
lab3/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
**/*
|
||||||
|
!.gitignore
|
||||||
|
!report.tex
|
||||||
|
!img
|
||||||
|
!img/**
|
||||||
|
!programm
|
||||||
|
!programm/*.py
|
||||||
178
lab3/programm/passgen.py
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
max_password_length = 100 # Максимальная длина пароля
|
||||||
|
|
||||||
|
|
||||||
|
def get_valid_int(prompt, min_value=0, max_value=None):
|
||||||
|
while True:
|
||||||
|
user_input = input(prompt).strip()
|
||||||
|
if not user_input:
|
||||||
|
print("Ошибка: ввод не должен быть пустым. Попробуйте снова.")
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
value = int(user_input)
|
||||||
|
if value < min_value:
|
||||||
|
print(
|
||||||
|
f"Ошибка: значение должно быть не меньше {min_value}. Попробуйте снова."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if max_value and value > max_value:
|
||||||
|
print(
|
||||||
|
f"Ошибка: значение должно быть не больше {max_value}. Попробуйте снова."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
return value
|
||||||
|
except ValueError:
|
||||||
|
print("Ошибка: введите корректное целое число.")
|
||||||
|
|
||||||
|
|
||||||
|
def get_yes_no(prompt):
|
||||||
|
while True:
|
||||||
|
user_input = input(prompt).strip().lower()
|
||||||
|
if user_input in ["yes", "y"]:
|
||||||
|
return True
|
||||||
|
if user_input in ["no", "n"]:
|
||||||
|
return False
|
||||||
|
print("Ошибка: введите 'yes' (или 'y') или 'no' (или 'n').")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_input():
|
||||||
|
"""Запрашивает у пользователя параметры генерации пароля с проверкой ввода."""
|
||||||
|
global max_password_length
|
||||||
|
|
||||||
|
length = get_valid_int(
|
||||||
|
f"Введите длину пароля (1-{max_password_length}): ",
|
||||||
|
min_value=1,
|
||||||
|
max_value=max_password_length,
|
||||||
|
)
|
||||||
|
|
||||||
|
use_lower = get_yes_no("Использовать строчные буквы? (yes/y, no/n): ")
|
||||||
|
use_upper = get_yes_no("Использовать заглавные буквы? (yes/y, no/n): ")
|
||||||
|
use_digits = get_yes_no("Использовать цифры? (yes/y, no/n): ")
|
||||||
|
use_special = get_yes_no("Использовать спецсимволы (!@#$%^&*)? (yes/y, no/n): ")
|
||||||
|
|
||||||
|
# Проверяем, что хотя бы один тип символов выбран
|
||||||
|
if not (use_lower or use_upper or use_digits or use_special):
|
||||||
|
print("Ошибка: необходимо выбрать хотя бы один тип символов.")
|
||||||
|
return get_user_input() # Повторный ввод всех данных
|
||||||
|
|
||||||
|
# Запрашиваем минимальное количество каждого типа символов
|
||||||
|
min_lower = (
|
||||||
|
get_valid_int("Минимальное количество строчных букв: ", 0) if use_lower else 0
|
||||||
|
)
|
||||||
|
min_upper = (
|
||||||
|
get_valid_int("Минимальное количество заглавных букв: ", 0) if use_upper else 0
|
||||||
|
)
|
||||||
|
min_digits = get_valid_int("Минимальное количество цифр: ", 0) if use_digits else 0
|
||||||
|
min_special = (
|
||||||
|
get_valid_int("Минимальное количество спецсимволов: ", 0) if use_special else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
length,
|
||||||
|
use_lower,
|
||||||
|
use_upper,
|
||||||
|
use_digits,
|
||||||
|
use_special,
|
||||||
|
min_lower,
|
||||||
|
min_upper,
|
||||||
|
min_digits,
|
||||||
|
min_special,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_input(length, min_lower, min_upper, min_digits, min_special):
|
||||||
|
"""Проверяет, что длина пароля больше суммы минимальных значений."""
|
||||||
|
total_required = min_lower + min_upper + min_digits + min_special
|
||||||
|
if total_required > length:
|
||||||
|
print(
|
||||||
|
f"Ошибка: сумма минимальных значений ({total_required}) превышает длину пароля ({length})."
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def generate_mandatory_chars(
|
||||||
|
min_lower,
|
||||||
|
min_upper,
|
||||||
|
min_digits,
|
||||||
|
min_special,
|
||||||
|
lower_chars,
|
||||||
|
upper_chars,
|
||||||
|
digit_chars,
|
||||||
|
special_chars,
|
||||||
|
):
|
||||||
|
"""Генерирует обязательные символы пароля."""
|
||||||
|
password = (
|
||||||
|
random.choices(lower_chars, k=min_lower)
|
||||||
|
+ random.choices(upper_chars, k=min_upper)
|
||||||
|
+ random.choices(digit_chars, k=min_digits)
|
||||||
|
+ random.choices(special_chars, k=min_special)
|
||||||
|
)
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def fill_password(password, length, all_chars):
|
||||||
|
"""Дополняет пароль случайными символами до нужной длины."""
|
||||||
|
remaining_length = length - len(password)
|
||||||
|
password += random.choices(all_chars, k=remaining_length)
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def shuffle_password(password):
|
||||||
|
"""Перемешивает символы пароля случайным образом."""
|
||||||
|
random.shuffle(password)
|
||||||
|
return "".join(password)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_password(
|
||||||
|
length,
|
||||||
|
use_lower,
|
||||||
|
use_upper,
|
||||||
|
use_digits,
|
||||||
|
use_special,
|
||||||
|
min_lower,
|
||||||
|
min_upper,
|
||||||
|
min_digits,
|
||||||
|
min_special,
|
||||||
|
):
|
||||||
|
"""Генерирует пароль с учётом заданных параметров."""
|
||||||
|
lower_chars = string.ascii_lowercase if use_lower else ""
|
||||||
|
upper_chars = string.ascii_uppercase if use_upper else ""
|
||||||
|
digit_chars = string.digits if use_digits else ""
|
||||||
|
special_chars = "!@#$%^&*" if use_special else ""
|
||||||
|
|
||||||
|
all_chars = lower_chars + upper_chars + digit_chars + special_chars
|
||||||
|
|
||||||
|
while not validate_input(length, min_lower, min_upper, min_digits, min_special):
|
||||||
|
print("Пожалуйста, введите параметры заново.")
|
||||||
|
return generate_password(*get_user_input())
|
||||||
|
|
||||||
|
password = generate_mandatory_chars(
|
||||||
|
min_lower,
|
||||||
|
min_upper,
|
||||||
|
min_digits,
|
||||||
|
min_special,
|
||||||
|
lower_chars,
|
||||||
|
upper_chars,
|
||||||
|
digit_chars,
|
||||||
|
special_chars,
|
||||||
|
)
|
||||||
|
password = fill_password(password, length, all_chars)
|
||||||
|
password = shuffle_password(password)
|
||||||
|
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Основная функция программы."""
|
||||||
|
max_password_length = 100
|
||||||
|
user_data = get_user_input()
|
||||||
|
password = generate_password(*user_data)
|
||||||
|
print("Сгенерированный пароль:", password)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
173
lab3/programm/passgen_refactor.py
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
"""
|
||||||
|
Модуль для генерации безопасных паролей с разными настройками.
|
||||||
|
Пользователь может задавать длину, типы символов и минимальное количество каждого типа.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
MAX_PASSWORD_LENGTH = 100 # Максимальная длина пароля
|
||||||
|
|
||||||
|
|
||||||
|
def get_valid_int(prompt, min_value=0, max_value=None):
|
||||||
|
"""Запрашивает у пользователя целое число, проверяя корректность ввода."""
|
||||||
|
while True:
|
||||||
|
user_input = input(prompt).strip()
|
||||||
|
if not user_input:
|
||||||
|
print("Ошибка: ввод не должен быть пустым. Попробуйте снова.")
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
value = int(user_input)
|
||||||
|
if value < min_value:
|
||||||
|
print(
|
||||||
|
f"Ошибка: значение должно быть не меньше {min_value}. Попробуйте снова."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if max_value and value > max_value:
|
||||||
|
print(
|
||||||
|
f"Ошибка: значение должно быть не больше {max_value}. Попробуйте снова."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
return value
|
||||||
|
except ValueError:
|
||||||
|
print("Ошибка: введите корректное целое число.")
|
||||||
|
|
||||||
|
|
||||||
|
def get_yes_no(prompt):
|
||||||
|
"""Запрашивает у пользователя 'yes'/'y' или 'no'/'n', проверяя корректность ввода."""
|
||||||
|
while True:
|
||||||
|
user_input = input(prompt).strip().lower()
|
||||||
|
if user_input in ["yes", "y"]:
|
||||||
|
return True
|
||||||
|
if user_input in ["no", "n"]:
|
||||||
|
return False
|
||||||
|
print("Ошибка: введите 'yes' (или 'y') или 'no' (или 'n').")
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_input():
|
||||||
|
"""Запрашивает у пользователя параметры генерации пароля с проверкой ввода."""
|
||||||
|
settings = {
|
||||||
|
"length": get_valid_int(
|
||||||
|
f"Введите длину пароля (1-{MAX_PASSWORD_LENGTH}): ",
|
||||||
|
min_value=1,
|
||||||
|
max_value=MAX_PASSWORD_LENGTH,
|
||||||
|
),
|
||||||
|
"use_lower": get_yes_no("Использовать строчные буквы? (yes/y, no/n): "),
|
||||||
|
"use_upper": get_yes_no("Использовать заглавные буквы? (yes/y, no/n): "),
|
||||||
|
"use_digits": get_yes_no("Использовать цифры? (yes/y, no/n): "),
|
||||||
|
"use_special": get_yes_no(
|
||||||
|
"Использовать спецсимволы (!@#$%^&*)? (yes/y, no/n): "
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверяем, что хотя бы один тип символов выбран
|
||||||
|
if not any(
|
||||||
|
[
|
||||||
|
settings["use_lower"],
|
||||||
|
settings["use_upper"],
|
||||||
|
settings["use_digits"],
|
||||||
|
settings["use_special"],
|
||||||
|
]
|
||||||
|
):
|
||||||
|
print("Ошибка: необходимо выбрать хотя бы один тип символов.")
|
||||||
|
return get_user_input() # Повторный ввод всех данных
|
||||||
|
|
||||||
|
# Запрашиваем минимальное количество каждого типа символов
|
||||||
|
settings["min_lower"] = (
|
||||||
|
get_valid_int("Минимальное количество строчных букв: ", 0)
|
||||||
|
if settings["use_lower"]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
settings["min_upper"] = (
|
||||||
|
get_valid_int("Минимальное количество заглавных букв: ", 0)
|
||||||
|
if settings["use_upper"]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
settings["min_digits"] = (
|
||||||
|
get_valid_int("Минимальное количество цифр: ", 0)
|
||||||
|
if settings["use_digits"]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
settings["min_special"] = (
|
||||||
|
get_valid_int("Минимальное количество спецсимволов: ", 0)
|
||||||
|
if settings["use_special"]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
|
def validate_input(settings):
|
||||||
|
"""Проверяет, что длина пароля больше суммы минимальных значений."""
|
||||||
|
total_required = sum(
|
||||||
|
[
|
||||||
|
settings["min_lower"],
|
||||||
|
settings["min_upper"],
|
||||||
|
settings["min_digits"],
|
||||||
|
settings["min_special"],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if total_required > settings["length"]:
|
||||||
|
print(
|
||||||
|
f"Ошибка: сумма минимальных значений ({total_required})"
|
||||||
|
f" превышает длину пароля ({settings['length']})."
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def generate_mandatory_chars(settings, char_sets):
|
||||||
|
"""Генерирует обязательные символы пароля."""
|
||||||
|
password = (
|
||||||
|
random.choices(char_sets["lower"], k=settings["min_lower"])
|
||||||
|
+ random.choices(char_sets["upper"], k=settings["min_upper"])
|
||||||
|
+ random.choices(char_sets["digits"], k=settings["min_digits"])
|
||||||
|
+ random.choices(char_sets["special"], k=settings["min_special"])
|
||||||
|
)
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def fill_password(password, length, all_chars):
|
||||||
|
"""Дополняет пароль случайными символами до нужной длины."""
|
||||||
|
remaining_length = length - len(password)
|
||||||
|
password += random.choices(all_chars, k=remaining_length)
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def shuffle_password(password):
|
||||||
|
"""Перемешивает символы пароля случайным образом."""
|
||||||
|
random.shuffle(password)
|
||||||
|
return "".join(password)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_password(settings):
|
||||||
|
"""Генерирует пароль с учётом заданных параметров."""
|
||||||
|
char_sets = {
|
||||||
|
"lower": string.ascii_lowercase if settings["use_lower"] else "",
|
||||||
|
"upper": string.ascii_uppercase if settings["use_upper"] else "",
|
||||||
|
"digits": string.digits if settings["use_digits"] else "",
|
||||||
|
"special": "!@#$%^&*" if settings["use_special"] else "",
|
||||||
|
}
|
||||||
|
|
||||||
|
all_chars = "".join(char_sets.values())
|
||||||
|
|
||||||
|
while not validate_input(settings):
|
||||||
|
print("Пожалуйста, введите параметры заново.")
|
||||||
|
return generate_password(get_user_input())
|
||||||
|
|
||||||
|
password = generate_mandatory_chars(settings, char_sets)
|
||||||
|
password = fill_password(password, settings["length"], all_chars)
|
||||||
|
password = shuffle_password(password)
|
||||||
|
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Основная функция программы."""
|
||||||
|
user_settings = get_user_input()
|
||||||
|
password = generate_password(user_settings)
|
||||||
|
print("Сгенерированный пароль:", password)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
882
lab3/report.tex
Normal file
@@ -0,0 +1,882 @@
|
|||||||
|
\documentclass[a4paper, final]{article}
|
||||||
|
%\usepackage{literat} % Нормальные шрифты
|
||||||
|
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||||
|
\usepackage{tabularx}
|
||||||
|
\usepackage[T2A]{fontenc}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[russian]{babel}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||||
|
\usepackage{ragged2e} %для растягивания по ширине
|
||||||
|
\usepackage{setspace} %для межстрочно го интервала
|
||||||
|
\usepackage{moreverb} %для работы с листингами
|
||||||
|
\usepackage{indentfirst} % для абзацного отступа
|
||||||
|
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||||
|
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||||
|
\usepackage{tikz}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{afterpage}
|
||||||
|
\usepackage{longtable}
|
||||||
|
\usepackage{float}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||||
|
\usepackage{pdflscape}
|
||||||
|
% \usepackage{lscape}
|
||||||
|
|
||||||
|
\usepackage{array}
|
||||||
|
\usepackage{multirow}
|
||||||
|
|
||||||
|
\renewcommand\verbatimtabsize{4\relax}
|
||||||
|
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||||
|
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||||
|
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||||
|
\usepackage{listings} %листинги
|
||||||
|
\usepackage{xcolor} % цвета
|
||||||
|
\usepackage{hyperref}% для гиперссылок
|
||||||
|
\usepackage{enumitem} %для перечислений
|
||||||
|
|
||||||
|
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||||
|
|
||||||
|
|
||||||
|
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||||
|
|
||||||
|
\hypersetup{colorlinks,
|
||||||
|
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||||
|
|
||||||
|
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||||
|
\lstloadlanguages{ SQL}
|
||||||
|
% включаем кириллицу и добавляем кое−какие опции
|
||||||
|
\lstset{tabsize=2,
|
||||||
|
breaklines,
|
||||||
|
basicstyle=\footnotesize,
|
||||||
|
columns=fullflexible,
|
||||||
|
flexiblecolumns,
|
||||||
|
numbers=left,
|
||||||
|
numberstyle={\footnotesize},
|
||||||
|
keywordstyle=\color{blue},
|
||||||
|
inputencoding=cp1251,
|
||||||
|
extendedchars=true
|
||||||
|
}
|
||||||
|
\lstdefinelanguage{MyC}{
|
||||||
|
language=SQL,
|
||||||
|
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||||
|
% identifierstyle=\color{black},
|
||||||
|
% morecomment=[n]{/**}{*/},
|
||||||
|
% commentstyle=\color{blue}\ttfamily,
|
||||||
|
% stringstyle=\color{red}\ttfamily,
|
||||||
|
% morestring=[b]",
|
||||||
|
% showstringspaces=false,
|
||||||
|
% morecomment=[l][\color{gray}]{//},
|
||||||
|
keepspaces=true,
|
||||||
|
escapechar=\%,
|
||||||
|
texcl=true
|
||||||
|
}
|
||||||
|
|
||||||
|
\textheight=24cm % высота текста
|
||||||
|
\textwidth=16cm % ширина текста
|
||||||
|
\oddsidemargin=0pt % отступ от левого края
|
||||||
|
\topmargin=-1.5cm % отступ от верхнего края
|
||||||
|
\parindent=24pt % абзацный отступ
|
||||||
|
\parskip=5pt % интервал между абзацами
|
||||||
|
\tolerance=2000 % терпимость к "жидким" строкам
|
||||||
|
\flushbottom % выравнивание высоты страниц
|
||||||
|
|
||||||
|
|
||||||
|
% Настройка листингов
|
||||||
|
\lstset{
|
||||||
|
language=python,
|
||||||
|
extendedchars=\true,
|
||||||
|
inputencoding=utf8,
|
||||||
|
keepspaces=true,
|
||||||
|
% captionpos=b, % подписи листингов снизу
|
||||||
|
}
|
||||||
|
|
||||||
|
\begin{document} % начало документа
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||||
|
\begin{center}
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||||
|
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||||
|
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||||
|
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||||
|
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||||
|
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\large{Лабораторная работа №3}\\
|
||||||
|
\large{<<Статический анализ кода приложений>>}\\
|
||||||
|
\large{по дисциплине}\\
|
||||||
|
\large{<<Методы тестирования программного обеспечения>>}\\
|
||||||
|
\hfill \break
|
||||||
|
|
||||||
|
% \hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\small{
|
||||||
|
\begin{tabular}{lrrl}
|
||||||
|
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||||
|
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||||
|
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Курочкин М. А. \\\\
|
||||||
|
&&\hspace{4cm}
|
||||||
|
\end{tabular}
|
||||||
|
\begin{flushright}
|
||||||
|
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
|
||||||
|
\end{flushright}
|
||||||
|
}
|
||||||
|
|
||||||
|
\hfill \break
|
||||||
|
% \hfill \break
|
||||||
|
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||||
|
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||||
|
|
||||||
|
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\tableofcontents
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\section{Постановка задачи}
|
||||||
|
|
||||||
|
Задачи лабораторной работы:
|
||||||
|
\begin{itemize}
|
||||||
|
\item изучить методы статического тестирование;
|
||||||
|
\item провести статическое тестирование программы;
|
||||||
|
\item проанализировать полученный результат;
|
||||||
|
\item рассмотреть рекомендации статического анализатора и при необходимости внести изменения в программу.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section {Статическое тестирование}
|
||||||
|
Статическое тестирование — это оценка элемента тестирования, при которой не происходит выполнения кода, и которая может быть проведена вручную или с помощью инструментариев. Объектом тестирования может быть документация или исходный код, а сам процесс возможен на любом этапе жизненного цикла ПО (Согласно ГОСТ Р 56920-2024 (раздел 5.5.2)).
|
||||||
|
|
||||||
|
Статическое тестирование включает в себя:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Проверку документации: требования, спецификации, архитектурные решения.
|
||||||
|
\item Анализ исходного кода: поиск синтаксических ошибок, нарушений стандартов
|
||||||
|
кодирования и потенциальных уязвимостей.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Цель статического тестирования — выявление дефектов на ранних стадиях разработки, что снижает затраты на их исправление. Как отмечает Гленфорд Майерс
|
||||||
|
в книге «Искусство тестирования», до 60\% ошибок можно обнаружить до запуска
|
||||||
|
программы. Это делает статическое тестирование критически важным инструментом для повышения качества ПО.
|
||||||
|
|
||||||
|
\subsection{Основные формы статического тестирования}
|
||||||
|
Используются специальные инструменты -- «статические анализаторы», которые автоматически:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Проверяют соответствие кода стандартам кодирования (Code Style, Coding
|
||||||
|
Guidelines).
|
||||||
|
\item Ищут потенциальные ошибки (например, неиспользуемые переменные, некорректные приведения типов, опасные конструкции).
|
||||||
|
\item Указывают на потенциальные уязвимости в безопасности (например, возможности для SQL-инъекций, потенциальные переполнения буфера).
|
||||||
|
\item Анализируют потоки данных, чтобы понять, где значения могут принимать
|
||||||
|
нежелательные (NullPointerException и др.) значения.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Разница между статическим анализатором и инспекцией кода за столом}
|
||||||
|
\subsubsection*{Способ выполнения}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Статический анализатор: запускается автоматически на исходном коде и выдает отчёт об обнаруженных проблемах. Анализатор следует набору заранее
|
||||||
|
заданных правил (линейный и/или межпроцедурный анализ, анализ потока
|
||||||
|
данных и т. д.).
|
||||||
|
\item Инспекция кода за столом: проводится людьми (разработчиками, тестировщиками). Участники встречи просматривают код построчно (или анализируют его логические куски) и обсуждают архитектурные, логические, стилевые и
|
||||||
|
другие аспекты.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsubsection*{Область охвата}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Статический анализатор:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Ориентирован в основном на типичные ошибки и «сигнализирует» о
|
||||||
|
потенциальных проблемах, отклонениях от правил кодирования, уязвимостях в безопасности.
|
||||||
|
\item Хорошо справляется с рутинным поиском большого количества распространённых проблем (например, неиспользуемые переменные, неочевидные «if» без «else», выход за границы массива и т. п.).
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\item Инспекция кода:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Позволяет вскрыть более сложные логические ошибки, несоответствие
|
||||||
|
требованиям, некорректную бизнес-логику.
|
||||||
|
\item Во время обсуждения могут выявиться проблемы, которые невозможно уловить статическим анализатором: «Почему этот алгоритм выбран
|
||||||
|
именно так?», «Соответствует ли это бизнес-требованиям?», «Оптимальна ли структура данных?», «Легко ли будет поддерживать этот код?».
|
||||||
|
\end{itemize}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsubsection*{Глубина и виды обнаруживаемых дефектов}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Статический анализатор: находит скорее «структурные» и «синтаксические»
|
||||||
|
дефекты и уязвимости (неиспользуемые переменные, неправильные операции
|
||||||
|
с памятью, отсутствие проверок). Может не понимать, «хорош ли» сам алгоритм.
|
||||||
|
\item Инспекция кода: ориентирована на логику, архитектуру, читаемость, потенциальные проблемы взаимодействия модулей. Тут важны не только дефекты
|
||||||
|
самого кода, но и соответствует ли он требованиям или лучшим практикам
|
||||||
|
проектирования.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsubsection*{Скорость и автоматизация}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Статический анализатор: работает быстро (особенно если хорошо интегрирован в CI/CD); выдаёт отчёты сразу после запуска.
|
||||||
|
\item Инспекция кода: процесс требует участия людей и времени на обсуждение.
|
||||||
|
Однако именно в этом процессе выявляются «глубинные» проблемы, которые
|
||||||
|
не найдёт автоматизированный инструмент.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsubsection*{Результаты и интерпретация}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Статический анализатор:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Даёт отчёты (логи, списки ошибок/предупреждений) -- но они нуждаются в
|
||||||
|
интерпретации человеком, поскольку есть ложные срабатывания (false
|
||||||
|
positives).
|
||||||
|
\item Для принятия решения о серьёзности проблемы часто всё равно приходится просматривать код.
|
||||||
|
\end{itemize}
|
||||||
|
\item Инспекция кода:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Часто приводит не только к обнаружению ошибок, но и к улучшению
|
||||||
|
совместной экспертизы в команде.
|
||||||
|
\item Может завершиться рекомендациями по рефакторингу, изменению архитектуры, или даже пересмотром требований.
|
||||||
|
\end{itemize}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section {Описание приложения и среды разработки}
|
||||||
|
Название программы: Генератор паролей.
|
||||||
|
|
||||||
|
Задача программы: Сгенерировать пароль с параметрами, заданными пользователем.
|
||||||
|
|
||||||
|
Дано:
|
||||||
|
\begin{itemize}
|
||||||
|
\item число -- длина генерируемого пароля;
|
||||||
|
\item строка <<yes>> или <<no>>, если пользователь введёт строку <<yes>, то в пароле будут использоваться строчные буквы;
|
||||||
|
\item строка <<yes>> или <<no>>, если пользователь введёт строку <<yes>, то в пароле будут использоваться заглавные буквы;
|
||||||
|
\item строка <<yes>> или <<no>>, если пользователь введёт строку <<yes>, то в пароле будут использоваться цифры;
|
||||||
|
\item строка <<yes>> или <<no>>, если пользователь введёт строку <<yes>, то в пароле будут использоваться спецсимволы;
|
||||||
|
\item число -- минимальное количество строчных букв в пароле;
|
||||||
|
\item число -- минимальное количество заглавных букв в пароле;
|
||||||
|
\item число -- минимальное количество цифр в пароле;
|
||||||
|
\item число -- минимальное количество спецсимволов;
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Ограничения:
|
||||||
|
\begin{itemize}
|
||||||
|
\item допустимая длина пароля -- от 1 до 100 символов;
|
||||||
|
\item минимальное количество строчных букв -- 0;
|
||||||
|
\item минимальное количество заглавных букв -- 0;
|
||||||
|
\item минимальное количество цифр -- 0;
|
||||||
|
\item минимальное количество спецсимволов -- 0;
|
||||||
|
\item сумма минимального количества строчных букв, заглавных букв, цифр и спецсимволов не может превышать длину пароля.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Программа была написана на языке Python. В качестве среды разработки использовалось лицензионное программное обеспечение для редактирования текста -- Microsoft Visual Studio Code.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsection{Спецификация тестируемой программы}
|
||||||
|
|
||||||
|
Спецификация программы представлена в таблице~\ref{tbl:spec}.
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\caption{Спецификация.}
|
||||||
|
\footnotesize
|
||||||
|
\begin{tabularx}{\textwidth}{|X|X|X|}
|
||||||
|
\hline
|
||||||
|
\textbf{Входные данные} & \textbf{Выходные данные} & \textbf{Комментарий} \\
|
||||||
|
\hline
|
||||||
|
10 y y y y 1 1 1 1 & 6KoL4Tfn*M & Программа сгенерировала и вывела на экран пароль с заданными параметрами. \\
|
||||||
|
\hline
|
||||||
|
-10 y y y y 1 1 1 1 & Ошибка: значение должно быть не меньше 1. Попробуйте снова. & Программа вывела на экран сообщение о некорректном пользовательском вводе. \\
|
||||||
|
\hline
|
||||||
|
10 abc y y y 1 1 1 1 & Ошибка: введите 'yes' (или 'y') или 'no' (или 'n'). & Программа вывела на экран сообщение о некорректном пользовательском вводе. \\
|
||||||
|
\hline
|
||||||
|
10 abc y y y -1 1 1 1 & Ошибка: значение должно быть не меньше 0. Попробуйте снова. & Программа вывела на экран сообщение о некорректном пользовательском вводе. \\
|
||||||
|
\hline
|
||||||
|
10 abc y y y 5 5 5 5 & Ошибка: сумма минимальных значений (103) превышает длину пароля (10). Пожалуйста, введите параметры заново. & Программа вывела на экран сообщение о некорректном пользовательском вводе. \\
|
||||||
|
\hline
|
||||||
|
\end{tabularx}
|
||||||
|
\label{tbl:spec}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\subsection{Исходный код программы тестируемой программы}
|
||||||
|
|
||||||
|
Исходный код программы представлен в листинге~\ref{lst:code}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Исходный код.}, label={lst:code}]
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
max_password_length = 100 # Максимальная длина пароля
|
||||||
|
|
||||||
|
|
||||||
|
def get_valid_int(prompt, min_value=0, max_value=None):
|
||||||
|
while True:
|
||||||
|
user_input = input(prompt).strip()
|
||||||
|
if not user_input:
|
||||||
|
print("Ошибка: ввод не должен быть пустым. Попробуйте снова.")
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
value = int(user_input)
|
||||||
|
if value < min_value:
|
||||||
|
print(
|
||||||
|
f"Ошибка: значение должно быть не меньше {min_value}. Попробуйте снова."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if max_value and value > max_value:
|
||||||
|
print(
|
||||||
|
f"Ошибка: значение должно быть не больше {max_value}. Попробуйте снова."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
return value
|
||||||
|
except ValueError:
|
||||||
|
print("Ошибка: введите корректное целое число.")
|
||||||
|
|
||||||
|
|
||||||
|
def get_yes_no(prompt):
|
||||||
|
while True:
|
||||||
|
user_input = input(prompt).strip().lower()
|
||||||
|
if user_input in ["yes", "y"]:
|
||||||
|
return True
|
||||||
|
if user_input in ["no", "n"]:
|
||||||
|
return False
|
||||||
|
print("Ошибка: введите 'yes' (или 'y') или 'no' (или 'n').")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_input():
|
||||||
|
"""Запрашивает у пользователя параметры генерации пароля с проверкой ввода."""
|
||||||
|
global max_password_length
|
||||||
|
|
||||||
|
length = get_valid_int(
|
||||||
|
f"Введите длину пароля (1-{max_password_length}): ",
|
||||||
|
min_value=1,
|
||||||
|
max_value=max_password_length,
|
||||||
|
)
|
||||||
|
|
||||||
|
use_lower = get_yes_no("Использовать строчные буквы? (yes/y, no/n): ")
|
||||||
|
use_upper = get_yes_no("Использовать заглавные буквы? (yes/y, no/n): ")
|
||||||
|
use_digits = get_yes_no("Использовать цифры? (yes/y, no/n): ")
|
||||||
|
use_special = get_yes_no("Использовать спецсимволы (!@#$%^&*)? (yes/y, no/n): ")
|
||||||
|
|
||||||
|
# Проверяем, что хотя бы один тип символов выбран
|
||||||
|
if not (use_lower or use_upper or use_digits or use_special):
|
||||||
|
print("Ошибка: необходимо выбрать хотя бы один тип символов.")
|
||||||
|
return get_user_input() # Повторный ввод всех данных
|
||||||
|
|
||||||
|
# Запрашиваем минимальное количество каждого типа символов
|
||||||
|
min_lower = (
|
||||||
|
get_valid_int("Минимальное количество строчных букв: ", 0) if use_lower else 0
|
||||||
|
)
|
||||||
|
min_upper = (
|
||||||
|
get_valid_int("Минимальное количество заглавных букв: ", 0) if use_upper else 0
|
||||||
|
)
|
||||||
|
min_digits = get_valid_int("Минимальное количество цифр: ", 0) if use_digits else 0
|
||||||
|
min_special = (
|
||||||
|
get_valid_int("Минимальное количество спецсимволов: ", 0) if use_special else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
length,
|
||||||
|
use_lower,
|
||||||
|
use_upper,
|
||||||
|
use_digits,
|
||||||
|
use_special,
|
||||||
|
min_lower,
|
||||||
|
min_upper,
|
||||||
|
min_digits,
|
||||||
|
min_special,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_input(length, min_lower, min_upper, min_digits, min_special):
|
||||||
|
"""Проверяет, что длина пароля больше суммы минимальных значений."""
|
||||||
|
total_required = min_lower + min_upper + min_digits + min_special
|
||||||
|
if total_required > length:
|
||||||
|
print(
|
||||||
|
f"Ошибка: сумма минимальных значений ({total_required}) превышает длину пароля ({length})."
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def generate_mandatory_chars(
|
||||||
|
min_lower,
|
||||||
|
min_upper,
|
||||||
|
min_digits,
|
||||||
|
min_special,
|
||||||
|
lower_chars,
|
||||||
|
upper_chars,
|
||||||
|
digit_chars,
|
||||||
|
special_chars,
|
||||||
|
):
|
||||||
|
"""Генерирует обязательные символы пароля."""
|
||||||
|
password = (
|
||||||
|
random.choices(lower_chars, k=min_lower)
|
||||||
|
+ random.choices(upper_chars, k=min_upper)
|
||||||
|
+ random.choices(digit_chars, k=min_digits)
|
||||||
|
+ random.choices(special_chars, k=min_special)
|
||||||
|
)
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def fill_password(password, length, all_chars):
|
||||||
|
"""Дополняет пароль случайными символами до нужной длины."""
|
||||||
|
remaining_length = length - len(password)
|
||||||
|
password += random.choices(all_chars, k=remaining_length)
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def shuffle_password(password):
|
||||||
|
"""Перемешивает символы пароля случайным образом."""
|
||||||
|
random.shuffle(password)
|
||||||
|
return "".join(password)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_password(
|
||||||
|
length,
|
||||||
|
use_lower,
|
||||||
|
use_upper,
|
||||||
|
use_digits,
|
||||||
|
use_special,
|
||||||
|
min_lower,
|
||||||
|
min_upper,
|
||||||
|
min_digits,
|
||||||
|
min_special,
|
||||||
|
):
|
||||||
|
"""Генерирует пароль с учётом заданных параметров."""
|
||||||
|
lower_chars = string.ascii_lowercase if use_lower else ""
|
||||||
|
upper_chars = string.ascii_uppercase if use_upper else ""
|
||||||
|
digit_chars = string.digits if use_digits else ""
|
||||||
|
special_chars = "!@#$%^&*" if use_special else ""
|
||||||
|
|
||||||
|
all_chars = lower_chars + upper_chars + digit_chars + special_chars
|
||||||
|
|
||||||
|
while not validate_input(length, min_lower, min_upper, min_digits, min_special):
|
||||||
|
print("Пожалуйста, введите параметры заново.")
|
||||||
|
return generate_password(*get_user_input())
|
||||||
|
|
||||||
|
password = generate_mandatory_chars(
|
||||||
|
min_lower,
|
||||||
|
min_upper,
|
||||||
|
min_digits,
|
||||||
|
min_special,
|
||||||
|
lower_chars,
|
||||||
|
upper_chars,
|
||||||
|
digit_chars,
|
||||||
|
special_chars,
|
||||||
|
)
|
||||||
|
password = fill_password(password, length, all_chars)
|
||||||
|
password = shuffle_password(password)
|
||||||
|
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Основная функция программы."""
|
||||||
|
max_password_length = 100
|
||||||
|
user_data = get_user_input()
|
||||||
|
password = generate_password(*user_data)
|
||||||
|
print("Сгенерированный пароль:", password)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
|
||||||
|
\section{Статический анализ кода приложения}
|
||||||
|
\subsection{Описание выбранного инструмента для статического анализа кода}
|
||||||
|
|
||||||
|
В качестве инструмента статического анализа кода проекта был выбран Pylint
|
||||||
|
версии 3.3.6. Pylint - это программа для проверки исходного кода, ошибок и качества
|
||||||
|
для языка программирования Python. Он назван в соответствии с общепринятым в
|
||||||
|
Python соглашением о префиксе «py» и отсылкой к программе lint для программирования на C. Он следует стилю, рекомендованному PEP 8, руководством по стилю
|
||||||
|
Python. Он похож на Pychecker и Pyflakes, но включает в себя следующие функции:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Проверка длины каждой строки;
|
||||||
|
\item Проверка правильности формирования имен переменных в соответствии со
|
||||||
|
стандартом кодирования проекта;
|
||||||
|
\item Проверка того, что заявленные интерфейсы действительно реализованы.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Pylint классифицирует свои сообщения об ошибках и предупреждениях по категориям, каждая из которых обозначается соответствующим префиксом. Основные
|
||||||
|
виды сообщений следующие
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item C (Convention): Сообщения, связанные со стилем оформления кода (например,
|
||||||
|
нарушение соглашений PEP8), именованием переменных, форматированием и
|
||||||
|
т.д.
|
||||||
|
\item R (Refactor): Рекомендации по рефакторингу кода для улучшения читаемости,
|
||||||
|
структуры и поддерживаемости. Эти сообщения помогают улучшить архитектуру кода.
|
||||||
|
\item W (Warning): Предупреждения о потенциальных проблемах, которые могут
|
||||||
|
привести к ошибкам или неожиданному поведению во время выполнения. Например, возможное использование необъявленной переменной.
|
||||||
|
\item E (Error): Ошибки, которые приведут к сбоям выполнения программы, такие как: синтаксические ошибки, вызов функций с неккоректным списком параметров или с неккоректными типами параметров.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{Разбор Pylint правил из группы «Convention»}
|
||||||
|
Эти проверки нацелены на то, чтобы код соответствовал принятым соглашениям о стиле (например, PEP8) и был легко читаемым.
|
||||||
|
\begin{itemize}
|
||||||
|
\item Именование:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Проверяется, чтобы имена классов, функций, переменных, модулей и
|
||||||
|
констант соответствовали принятым стандартам.
|
||||||
|
\item Например, классы должны именоваться в стиле CamelCase (например, MyClass), а функции и переменные – в snake\_case (например,
|
||||||
|
my\_function, my\_variable).
|
||||||
|
\item Также проверяются длина имен и их осмысленность, чтобы они точно
|
||||||
|
отражали назначение объекта в коде.
|
||||||
|
\end{itemize}
|
||||||
|
\item Стиль оформления:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Длина строк: Pylint следит за тем, чтобы строки не превышали установленную длину (обычно 79 или 99 символов в зависимости от конфигурации).
|
||||||
|
\item Отступы и пробелы: Контролируется корректное использование отступов (обычно 4 пробела), пробелов вокруг операторов, после запятых и
|
||||||
|
т.д.
|
||||||
|
\item Разбиение на строки: Рекомендуется правильно разбивать длинные выражения или вызовы функций на несколько строк для лучшей читаемости.
|
||||||
|
\item Пустые строки: Проверяется количество пустых строк между функциями и классами для поддержания визуальной структуры кода.
|
||||||
|
\end{itemize}
|
||||||
|
\item Документация:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Docstrings: Pylint обращает внимание на наличие строк документации
|
||||||
|
(docstrings) в модулях, классах, функциях и методах.
|
||||||
|
\item Формат документации: Документация должна быть оформлена согласно принятым стандартам (например, в формате reStructuredText или
|
||||||
|
Google style), чтобы обеспечить понятное описание функционала и параметров.
|
||||||
|
\end{itemize}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{ Разбор Pylint правил из группы «Refactor»}
|
||||||
|
Эта группа сообщений направлена на улучшение структуры кода, его упрощение
|
||||||
|
и повышение поддерживаемости
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Сложность функций:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Цикломатическая сложность: Анализируется количество ветвлений,
|
||||||
|
циклов и условных операторов. Функции с высокой сложностью могут
|
||||||
|
быть трудными для тестирования и отладки.
|
||||||
|
\item Слишком длинные функции: Если функция слишком большая или содержит множество аргументов, Pylint может рекомендовать её разбить
|
||||||
|
на более мелкие части.
|
||||||
|
\end{itemize}
|
||||||
|
\item Дублирование кода:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Проверка на повторяющиеся участки кода, что может указывать на возможность объединения логики в одну функцию или класс.
|
||||||
|
\item Цель – уменьшить количество повторений, чтобы изменение в одной
|
||||||
|
части кода не требовало повторения исправлений в нескольких местах.
|
||||||
|
\end{itemize}
|
||||||
|
\item Структурные проблемы:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Обнаружение слишком больших классов или методов, которые выполняют сразу несколько задач
|
||||||
|
\item Рекомендации по разделению ответственности (например, принцип
|
||||||
|
единственной ответственности из SOLID) для улучшения модульности
|
||||||
|
и тестируемости кода.
|
||||||
|
\end{itemize}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{Разбор Pylint правил из группы «Warning»}
|
||||||
|
Эта категория охватывает сообщения, которые указывают на потенциальные
|
||||||
|
проблемы, не являющиеся критическими ошибками, но способными привести к
|
||||||
|
неожиданному поведению.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Неиспользуемые элементы:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Переменные: Если переменная объявлена, но не используется, Pylint сообщает об этом, что помогает избежать загромождения кода.
|
||||||
|
\item Импорты: Неиспользуемые модули или функции, импортированные в
|
||||||
|
начале файла, могут быть отмечены для удаления.
|
||||||
|
\item Аргументы функций: Иногда функция принимает аргументы, которые
|
||||||
|
не используются в теле, что может быть сигналом к тому, что интерфейс
|
||||||
|
функции следует пересмотреть.
|
||||||
|
\end{itemize}
|
||||||
|
\item Подозрительные конструкции:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Использование переменных до объявления: Если переменная используется до того, как ей было присвоено значение, это может привести к
|
||||||
|
ошибкам.
|
||||||
|
\item Использование изменяемых значений по умолчанию: Применение изменяемых объектов (например, списков или словарей) в качестве значений
|
||||||
|
по умолчанию в параметрах функций может привести к неожиданным эффектам.
|
||||||
|
\end{itemize}
|
||||||
|
\item Ошибки логики:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Порой конструкция кода может быть синтаксически корректной, но её
|
||||||
|
поведение может быть неочевидным или потенциально приводить к логическим ошибкам (например, некорректное сравнение или неверное использование операторов).
|
||||||
|
\end{itemize}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsection{Разбор Pylint правил из группы «Error»}
|
||||||
|
Эти проверки сигнализируют о проблемах, которые приведут к сбоям выполнения программы.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Синтаксические ошибки:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Ошибки в написании кода (например, забытые двоеточия, скобки,
|
||||||
|
неправильное построение конструкции) могут привести к невозможности интерпретировать код.
|
||||||
|
\item Такие ошибки выявляются на этапе статического анализа до выполнения программы.
|
||||||
|
\end{itemize}
|
||||||
|
\item Ошибки времени выполнения:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Обращение к несуществующим атрибутам: Если код пытается получить
|
||||||
|
доступ к атрибуту, которого нет у объекта, Pylint укажет на эту проблему.
|
||||||
|
\item Неправильное использование функций: Например, вызов метода на объекте неподходящего типа или передача неверных аргументов, что в итоге
|
||||||
|
вызовет исключение при выполнении.
|
||||||
|
\end{itemize}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Процесс тестирования}
|
||||||
|
\subsection{Подготовка}
|
||||||
|
|
||||||
|
Перед использованием PyLint необходимо установить Python и PIP с официального сайта. Затем создать виртуальное окружение с помощью команды \texttt{virtualenv venv}.
|
||||||
|
|
||||||
|
Чтобы установить PyLint, достаточно выполнить команду \texttt{pip install pylint} внутри виртуального окружения.
|
||||||
|
|
||||||
|
Для запуска статического анализа достаточно выполнить команду \texttt{pylint file.py}, где \texttt{file.py} -- это название файла с исходным кодом. При запуске PyLint без дополнительных параметров, статический анализатор будет искать все типы ошибок, перечисленные в предыдущем разделе.
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Результат работы анализатора}
|
||||||
|
|
||||||
|
Полный вывод команды \texttt{pylint passgen.py} представлен в листинге~\ref{lst:res}. Статический анализатор вывел 12 сообщений о различных проблемах в коде. Они связаны с несоответствием стиля именования, отсутствием документации в модуле и функциях, а также слишком длинными строками. Кроме того, есть предупреждения о неинициализированной глобальной переменной, переопределении имени переменной во внутренней области видимости, а также о слишком большом количестве аргументов в функциях. PyLint также даёт общую оценку кода. Для моей программы он вывел оценку 8.48 из 10, что означает, что код в целом неплох, но его можно улучшить.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Результат выполнения команды \texttt{pylint passgen.py}.}, label={lst:res}]
|
||||||
|
************* Module passgen
|
||||||
|
passgen.py:91:0: C0301: Line too long (103/100) (line-too-long)
|
||||||
|
passgen.py:1:0: C0114: Missing module docstring (missing-module-docstring)
|
||||||
|
passgen.py:4:0: C0103: Constant name "max_password_length" doesn't conform to UPPER_CASE naming style (invalid-name)
|
||||||
|
passgen.py:7:0: C0116: Missing function or method docstring (missing-function-docstring)
|
||||||
|
passgen.py:30:0: C0116: Missing function or method docstring (missing-function-docstring)
|
||||||
|
passgen.py:43:4: W0602: Using global for 'max_password_length' but no assignment is done (global-variable-not-assigned)
|
||||||
|
passgen.py:97:0: R0913: Too many arguments (8/5) (too-many-arguments)
|
||||||
|
passgen.py:97:0: R0917: Too many positional arguments (8/5) (too-many-positional-arguments)
|
||||||
|
passgen.py:130:0: R0913: Too many arguments (9/5) (too-many-arguments)
|
||||||
|
passgen.py:130:0: R0917: Too many positional arguments (9/5) (too-many-positional-arguments)
|
||||||
|
passgen.py:171:4: W0621: Redefining name 'max_password_length' from outer scope (line 4) (redefined-outer-name)
|
||||||
|
passgen.py:171:4: W0612: Unused variable 'max_password_length' (unused-variable)
|
||||||
|
|
||||||
|
-------------------------------------------
|
||||||
|
Your code has been rated at 8.48/10 (previous run: 8.48/10, +0.00)
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\subsection{Результат улучшения кода}
|
||||||
|
|
||||||
|
В соответствии с рекомендациями PyLint в исходный код были добавлены комментарии с документацией для всего модуля и функций, слишком длинные строки были разбиты на более короткие и удобочитаемые, неинициализированная глобальная переменная была удалена, была удалена одна переменная из локальной области видимости, перекрывавшая глобальную переменную, имена всех переменных были приведены в соответствие с принятыми соглашениями языка Python.
|
||||||
|
|
||||||
|
После внесения перечисленных выше изменений статический анализатор PyLint был запущен ещё раз на обновлённом файле с исходным кодом. Результат запуска представлен в листинге~\ref{lst:res-new}. В этот раз PyLint не вывел никаких сообщений.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Результат работы PyLint на обновлённом файле с исходным кодом.}, label={lst:res-new}]
|
||||||
|
-------------------------------------
|
||||||
|
Your code has been rated at 10.00/10
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Обновлённый код программы представлен в листинге~\ref{lst:code-new}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Обновлённый код программы.}, label={lst:code-new}]
|
||||||
|
"""
|
||||||
|
Модуль для генерации безопасных паролей с разными настройками.
|
||||||
|
Пользователь может задавать длину, типы символов и минимальное количество каждого типа.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
MAX_PASSWORD_LENGTH = 100 # Максимальная длина пароля
|
||||||
|
|
||||||
|
|
||||||
|
def get_valid_int(prompt, min_value=0, max_value=None):
|
||||||
|
"""Запрашивает у пользователя целое число, проверяя корректность ввода."""
|
||||||
|
while True:
|
||||||
|
user_input = input(prompt).strip()
|
||||||
|
if not user_input:
|
||||||
|
print("Ошибка: ввод не должен быть пустым. Попробуйте снова.")
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
value = int(user_input)
|
||||||
|
if value < min_value:
|
||||||
|
print(
|
||||||
|
f"Ошибка: значение должно быть не меньше {min_value}. Попробуйте снова."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if max_value and value > max_value:
|
||||||
|
print(
|
||||||
|
f"Ошибка: значение должно быть не больше {max_value}. Попробуйте снова."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
return value
|
||||||
|
except ValueError:
|
||||||
|
print("Ошибка: введите корректное целое число.")
|
||||||
|
|
||||||
|
|
||||||
|
def get_yes_no(prompt):
|
||||||
|
"""Запрашивает у пользователя 'yes'/'y' или 'no'/'n', проверяя корректность ввода."""
|
||||||
|
while True:
|
||||||
|
user_input = input(prompt).strip().lower()
|
||||||
|
if user_input in ["yes", "y"]:
|
||||||
|
return True
|
||||||
|
if user_input in ["no", "n"]:
|
||||||
|
return False
|
||||||
|
print("Ошибка: введите 'yes' (или 'y') или 'no' (или 'n').")
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_input():
|
||||||
|
"""Запрашивает у пользователя параметры генерации пароля с проверкой ввода."""
|
||||||
|
settings = {
|
||||||
|
"length": get_valid_int(
|
||||||
|
f"Введите длину пароля (1-{MAX_PASSWORD_LENGTH}): ",
|
||||||
|
min_value=1,
|
||||||
|
max_value=MAX_PASSWORD_LENGTH,
|
||||||
|
),
|
||||||
|
"use_lower": get_yes_no("Использовать строчные буквы? (yes/y, no/n): "),
|
||||||
|
"use_upper": get_yes_no("Использовать заглавные буквы? (yes/y, no/n): "),
|
||||||
|
"use_digits": get_yes_no("Использовать цифры? (yes/y, no/n): "),
|
||||||
|
"use_special": get_yes_no(
|
||||||
|
"Использовать спецсимволы (!@#$%^&*)? (yes/y, no/n): "
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Проверяем, что хотя бы один тип символов выбран
|
||||||
|
if not any(
|
||||||
|
[
|
||||||
|
settings["use_lower"],
|
||||||
|
settings["use_upper"],
|
||||||
|
settings["use_digits"],
|
||||||
|
settings["use_special"],
|
||||||
|
]
|
||||||
|
):
|
||||||
|
print("Ошибка: необходимо выбрать хотя бы один тип символов.")
|
||||||
|
return get_user_input() # Повторный ввод всех данных
|
||||||
|
|
||||||
|
# Запрашиваем минимальное количество каждого типа символов
|
||||||
|
settings["min_lower"] = (
|
||||||
|
get_valid_int("Минимальное количество строчных букв: ", 0)
|
||||||
|
if settings["use_lower"]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
settings["min_upper"] = (
|
||||||
|
get_valid_int("Минимальное количество заглавных букв: ", 0)
|
||||||
|
if settings["use_upper"]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
settings["min_digits"] = (
|
||||||
|
get_valid_int("Минимальное количество цифр: ", 0)
|
||||||
|
if settings["use_digits"]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
settings["min_special"] = (
|
||||||
|
get_valid_int("Минимальное количество спецсимволов: ", 0)
|
||||||
|
if settings["use_special"]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
|
def validate_input(settings):
|
||||||
|
"""Проверяет, что длина пароля больше суммы минимальных значений."""
|
||||||
|
total_required = sum(
|
||||||
|
[
|
||||||
|
settings["min_lower"],
|
||||||
|
settings["min_upper"],
|
||||||
|
settings["min_digits"],
|
||||||
|
settings["min_special"],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if total_required > settings["length"]:
|
||||||
|
print(
|
||||||
|
f"Ошибка: сумма минимальных значений ({total_required})"
|
||||||
|
f" превышает длину пароля ({settings['length']})."
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def generate_mandatory_chars(settings, char_sets):
|
||||||
|
"""Генерирует обязательные символы пароля."""
|
||||||
|
password = (
|
||||||
|
random.choices(char_sets["lower"], k=settings["min_lower"])
|
||||||
|
+ random.choices(char_sets["upper"], k=settings["min_upper"])
|
||||||
|
+ random.choices(char_sets["digits"], k=settings["min_digits"])
|
||||||
|
+ random.choices(char_sets["special"], k=settings["min_special"])
|
||||||
|
)
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def fill_password(password, length, all_chars):
|
||||||
|
"""Дополняет пароль случайными символами до нужной длины."""
|
||||||
|
remaining_length = length - len(password)
|
||||||
|
password += random.choices(all_chars, k=remaining_length)
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def shuffle_password(password):
|
||||||
|
"""Перемешивает символы пароля случайным образом."""
|
||||||
|
random.shuffle(password)
|
||||||
|
return "".join(password)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_password(settings):
|
||||||
|
"""Генерирует пароль с учётом заданных параметров."""
|
||||||
|
char_sets = {
|
||||||
|
"lower": string.ascii_lowercase if settings["use_lower"] else "",
|
||||||
|
"upper": string.ascii_uppercase if settings["use_upper"] else "",
|
||||||
|
"digits": string.digits if settings["use_digits"] else "",
|
||||||
|
"special": "!@#$%^&*" if settings["use_special"] else "",
|
||||||
|
}
|
||||||
|
|
||||||
|
all_chars = "".join(char_sets.values())
|
||||||
|
|
||||||
|
while not validate_input(settings):
|
||||||
|
print("Пожалуйста, введите параметры заново.")
|
||||||
|
return generate_password(get_user_input())
|
||||||
|
|
||||||
|
password = generate_mandatory_chars(settings, char_sets)
|
||||||
|
password = fill_password(password, settings["length"], all_chars)
|
||||||
|
password = shuffle_password(password)
|
||||||
|
|
||||||
|
return password
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Основная функция программы."""
|
||||||
|
user_settings = get_user_input()
|
||||||
|
password = generate_password(user_settings)
|
||||||
|
print("Сгенерированный пароль:", password)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Заключение}
|
||||||
|
\addcontentsline{toc}{section}{Заключение}
|
||||||
|
|
||||||
|
В ходе выполнения данной лабораторной работы было проведено статистическое тестирование для программы: <<Генератор паролей>>.
|
||||||
|
|
||||||
|
Были найдены следующие проблемы:
|
||||||
|
\begin{itemize}
|
||||||
|
\item 5 нарушений правил оформления исходного кода;
|
||||||
|
\item 3 предупреждения о возможных ошибках в исходном коде;
|
||||||
|
\item 4 рекомендации по рефакторингу исходного кода.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
В результате проделанной работы программный код был исправлен в соответствии с рекомендациями статистического анализатора. На примере небольшой программы была наглядно продемонстрирована польза от использования статических анализаторов кода. Были сделаны выводы о том, что статистическое тестирование позволяет выявить некорректность в первую очередь в стиле оформления программы. На примере PyLint был получен первый опыт использования статических анализаторов кода.
|
||||||
|
|
||||||
|
В процессе статистического тестирования были выявлены некоторые недостатки, которых не удалось обнаружить во время инспекции за столом. В основно они связаны с оформлением кода. Например, при инспекции кода не было замечаний по поводу отсутствующих комментариев с документацией. Также была пропущена достаточно серьёзная неточность -- переменная из внешней области видимости перекрывалась переменной из локальной области видимости. Однако во время инспекции была обнаружена логическая ошибка при формировании пароля, состоящего только из заглавных символов. На основе полученных результатов можно сделать вывод, что статистическое тестирование является хорошим дополнением к инспекции за столом.
|
||||||
|
К тому же статическое тестирование не столь трудозатрано и его можно автоматизировать.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Список литературы}
|
||||||
|
\addcontentsline{toc}{section}{Список литературы}
|
||||||
|
|
||||||
|
\vspace{-1.5cm}
|
||||||
|
\begin{thebibliography}{0}
|
||||||
|
\bibitem{mayers}
|
||||||
|
Майерс, Г. Искусство тестирования программ. -- Санкт-Петербург: Диалектика, 2012 г.
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
1
lab4/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
other
|
||||||
9
lab4/calctests/.gitattributes
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# https://help.github.com/articles/dealing-with-line-endings/
|
||||||
|
#
|
||||||
|
# Linux start script should use lf
|
||||||
|
/gradlew text eol=lf
|
||||||
|
|
||||||
|
# These are Windows script files and should use crlf
|
||||||
|
*.bat text eol=crlf
|
||||||
|
|
||||||
5
lab4/calctests/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Ignore Gradle project-specific cache directory
|
||||||
|
.gradle
|
||||||
|
|
||||||
|
# Ignore Gradle build output directory
|
||||||
|
build
|
||||||
3
lab4/calctests/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"java.configuration.updateBuildConfiguration": "interactive"
|
||||||
|
}
|
||||||
45
lab4/calctests/app/build.gradle
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This file was generated by the Gradle 'init' task.
|
||||||
|
*
|
||||||
|
* This generated file contains a sample Java application project to get you started.
|
||||||
|
* For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.8/userguide/building_java_projects.html in the Gradle documentation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
// Apply the application plugin to add support for building a CLI application in Java.
|
||||||
|
id 'application'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
// Use Maven Central for resolving dependencies.
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Use JUnit Jupiter for testing.
|
||||||
|
testImplementation libs.junit.jupiter
|
||||||
|
|
||||||
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
|
|
||||||
|
// This dependency is used by the application.
|
||||||
|
implementation libs.guava
|
||||||
|
|
||||||
|
implementation files('./libs/calculator-1.0.jar')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply a specific Java toolchain to ease working on different environments.
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
// Define the main class for the application.
|
||||||
|
mainClass = 'dev.tishenko.calctests.App'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('test') {
|
||||||
|
// Use JUnit Platform for unit tests.
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
BIN
lab4/calctests/app/libs/calculator-1.0.jar
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* This source file was generated by the Gradle 'init' task
|
||||||
|
*/
|
||||||
|
package dev.tishenko.calctests;
|
||||||
|
|
||||||
|
public class App {
|
||||||
|
public String getGreeting() {
|
||||||
|
return "Hello World!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(new App().getGreeting());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* This source file was generated by the Gradle 'init' task
|
||||||
|
*/
|
||||||
|
package dev.tishenko.calctests;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import com.epam.tat.module4.Calculator;
|
||||||
|
|
||||||
|
class CalculatorTest {
|
||||||
|
private static final double DELTA = 0.0001;
|
||||||
|
private Calculator calculator;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
calculator = new Calculator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"1, 2, 3",
|
||||||
|
"5, 0, 5",
|
||||||
|
"-7, 7, 0",
|
||||||
|
"9223372036854775806, 1, 9223372036854775807"
|
||||||
|
})
|
||||||
|
void testLongSum(long a, long b, long expected) {
|
||||||
|
assertEquals(expected, calculator.sum(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"1.5, 2.5, 4.0",
|
||||||
|
"5.5, 0.0, 5.5",
|
||||||
|
"-3.5, 3.5, 0.0",
|
||||||
|
"0.1, 0.2, 0.3"
|
||||||
|
})
|
||||||
|
void testDoubleSum(double a, double b, double expected) {
|
||||||
|
assertEquals(expected, calculator.sum(a, b), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"2.5, 3.0, 7.5",
|
||||||
|
"0.0, 5.5, 0.0",
|
||||||
|
"-2.5, 3.0, -7.5",
|
||||||
|
"-2.5, -3.0, 7.5"
|
||||||
|
})
|
||||||
|
void testDoubleMult(double a, double b, double expected) {
|
||||||
|
assertEquals(expected, calculator.mult(a, b), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"2, 3, 6",
|
||||||
|
"0, 5, 0",
|
||||||
|
"-2, 3, -6",
|
||||||
|
"-2, -3, 6",
|
||||||
|
"1000, 1000, 1000000"
|
||||||
|
})
|
||||||
|
void testLongMult(long a, long b, long expected) {
|
||||||
|
assertEquals(expected, calculator.mult(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(doubles = { 4.0, 0.0, -4.0, 1000000.0 })
|
||||||
|
void testSqrt(double value) {
|
||||||
|
double expected = Math.sqrt(value);
|
||||||
|
assertEquals(expected, calculator.sqrt(value), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(doubles = { 0, Math.PI / 6, Math.PI / 4, -Math.PI / 3, 10 })
|
||||||
|
void testTg(double angle) {
|
||||||
|
double expected = Math.tan(angle);
|
||||||
|
double actual = calculator.tg(angle);
|
||||||
|
assertEquals(expected, actual, 0.0001);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
lab4/calctests/gradle/libs.versions.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# This file was generated by the Gradle 'init' task.
|
||||||
|
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
|
||||||
|
|
||||||
|
[versions]
|
||||||
|
guava = "33.0.0-jre"
|
||||||
|
junit-jupiter = "5.10.2"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
|
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
|
||||||
BIN
lab4/calctests/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
7
lab4/calctests/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
249
lab4/calctests/gradlew
vendored
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
92
lab4/calctests/gradlew.bat
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
14
lab4/calctests/settings.gradle
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* This file was generated by the Gradle 'init' task.
|
||||||
|
*
|
||||||
|
* The settings file is used to specify which projects to include in your build.
|
||||||
|
* For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.8/userguide/multi_project_builds.html in the Gradle documentation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
// Apply the foojay-resolver plugin to allow automatic download of JDKs
|
||||||
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = 'calctests'
|
||||||
|
include('app')
|
||||||
7
lab4/report/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
**/*
|
||||||
|
!.gitignore
|
||||||
|
!report.tex
|
||||||
|
!img
|
||||||
|
!img/**
|
||||||
|
!programm
|
||||||
|
!programm/*.py
|
||||||
BIN
lab4/report/img/selenium-tests.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
lab4/report/img/test1.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
lab4/report/img/test2.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
lab4/report/img/test3.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
lab4/report/img/test4.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
845
lab4/report/report.tex
Normal file
@@ -0,0 +1,845 @@
|
|||||||
|
\documentclass[a4paper, final]{article}
|
||||||
|
%\usepackage{literat} % Нормальные шрифты
|
||||||
|
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
|
||||||
|
\usepackage{tabularx}
|
||||||
|
\usepackage[T2A]{fontenc}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[russian]{babel}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
|
||||||
|
\usepackage{ragged2e} %для растягивания по ширине
|
||||||
|
\usepackage{setspace} %для межстрочно го интервала
|
||||||
|
\usepackage{moreverb} %для работы с листингами
|
||||||
|
\usepackage{indentfirst} % для абзацного отступа
|
||||||
|
\usepackage{moreverb} %для печати в листинге исходного кода программ
|
||||||
|
\usepackage{pdfpages} %для вставки других pdf файлов
|
||||||
|
\usepackage{tikz}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{afterpage}
|
||||||
|
\usepackage{longtable}
|
||||||
|
\usepackage{float}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% \usepackage[paper=A4,DIV=12]{typearea}
|
||||||
|
\usepackage{pdflscape}
|
||||||
|
% \usepackage{lscape}
|
||||||
|
|
||||||
|
\usepackage{array}
|
||||||
|
\usepackage{multirow}
|
||||||
|
|
||||||
|
\renewcommand\verbatimtabsize{4\relax}
|
||||||
|
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
|
||||||
|
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
|
||||||
|
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
|
||||||
|
\usepackage{listings} %листинги
|
||||||
|
\usepackage{xcolor} % цвета
|
||||||
|
\usepackage{hyperref}% для гиперссылок
|
||||||
|
\usepackage{enumitem} %для перечислений
|
||||||
|
|
||||||
|
\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}
|
||||||
|
|
||||||
|
|
||||||
|
\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях
|
||||||
|
|
||||||
|
\hypersetup{colorlinks,
|
||||||
|
allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)
|
||||||
|
|
||||||
|
% подгружаемые языки — подробнее в документации listings (это всё для листингов)
|
||||||
|
\lstloadlanguages{ Java}
|
||||||
|
% включаем кириллицу и добавляем кое−какие опции
|
||||||
|
\lstset{tabsize=2,
|
||||||
|
breaklines,
|
||||||
|
basicstyle=\footnotesize,
|
||||||
|
columns=fullflexible,
|
||||||
|
flexiblecolumns,
|
||||||
|
numbers=left,
|
||||||
|
numberstyle={\footnotesize},
|
||||||
|
keywordstyle=\color{blue},
|
||||||
|
inputencoding=cp1251,
|
||||||
|
extendedchars=true
|
||||||
|
}
|
||||||
|
\lstdefinelanguage{MyC}{
|
||||||
|
language=Java,
|
||||||
|
% ndkeywordstyle=\color{darkgray}\bfseries,
|
||||||
|
% identifierstyle=\color{black},
|
||||||
|
% morecomment=[n]{/**}{*/},
|
||||||
|
% commentstyle=\color{blue}\ttfamily,
|
||||||
|
% stringstyle=\color{red}\ttfamily,
|
||||||
|
% morestring=[b]",
|
||||||
|
% showstringspaces=false,
|
||||||
|
% morecomment=[l][\color{gray}]{//},
|
||||||
|
keepspaces=true,
|
||||||
|
escapechar=\%,
|
||||||
|
texcl=true
|
||||||
|
}
|
||||||
|
|
||||||
|
\textheight=24cm % высота текста
|
||||||
|
\textwidth=16cm % ширина текста
|
||||||
|
\oddsidemargin=0pt % отступ от левого края
|
||||||
|
\topmargin=-1.5cm % отступ от верхнего края
|
||||||
|
\parindent=24pt % абзацный отступ
|
||||||
|
\parskip=5pt % интервал между абзацами
|
||||||
|
\tolerance=2000 % терпимость к "жидким" строкам
|
||||||
|
\flushbottom % выравнивание высоты страниц
|
||||||
|
|
||||||
|
|
||||||
|
% Настройка листингов
|
||||||
|
\lstset{
|
||||||
|
language=Java,
|
||||||
|
extendedchars=\true,
|
||||||
|
inputencoding=utf8,
|
||||||
|
keepspaces=true,
|
||||||
|
% captionpos=b, % подписи листингов снизу
|
||||||
|
}
|
||||||
|
|
||||||
|
\begin{document} % начало документа
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% НАЧАЛО ТИТУЛЬНОГО ЛИСТА
|
||||||
|
\begin{center}
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||||
|
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||||
|
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||||
|
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||||
|
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||||
|
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\large{Лабораторная работа №4}\\
|
||||||
|
\large{<<Автоматизированное тестирование>>}\\
|
||||||
|
\large{по дисциплине}\\
|
||||||
|
\large{<<Методы тестирования программного обеспечения>>}\\
|
||||||
|
\hfill \break
|
||||||
|
|
||||||
|
% \hfill \break
|
||||||
|
\hfill \break
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
\small{
|
||||||
|
\begin{tabular}{lrrl}
|
||||||
|
\!\!\!Студент, & \hspace{2cm} & & \\
|
||||||
|
\!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
|
||||||
|
\!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Курочкин М. А. \\\\
|
||||||
|
&&\hspace{4cm}
|
||||||
|
\end{tabular}
|
||||||
|
\begin{flushright}
|
||||||
|
<<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
|
||||||
|
\end{flushright}
|
||||||
|
}
|
||||||
|
|
||||||
|
\hfill \break
|
||||||
|
% \hfill \break
|
||||||
|
\begin{center} \small{Санкт-Петербург, 2025} \end{center}
|
||||||
|
\thispagestyle{empty} % выключаем отображение номера для этой страницы
|
||||||
|
|
||||||
|
% КОНЕЦ ТИТУЛЬНОГО ЛИСТА
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\tableofcontents
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
|
||||||
|
\section{Постановка задачи}
|
||||||
|
|
||||||
|
Данная лабораторная работа делится на две части. В первой части необходимо разработать набор юнит-тестов к предоставленной библиотеке calculator.jar, которая содержит методы для проведения разнообразных операций над числами. Во второй части необходимо протестировать сайт в Chrome путем написания двух тестов, проверяющих корректность отображения страниц. Использовать для тестирования Selenuim WebDriver.
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section {Средства автоматизации тестирования}
|
||||||
|
\subsection{JUinit}
|
||||||
|
\textit{JUnit} — это фреймворк, созданный для тестирования программного обеспечения на языке Java. Он предназначен для разработки и выполнения автоматизированных тестов, что дает разработчикам возможность проверять правильность функционирования своего кода и обнаруживать ошибки на ранних стадиях разработки.
|
||||||
|
|
||||||
|
|
||||||
|
\subsubsection{Основные особенности JUnit}
|
||||||
|
\begin{itemize}
|
||||||
|
\item\textbf{ Модульное тестирование:} JUnit поддерживает тестирование отдельных модулей, позволяя разработчикам проверять работу отдельных компонентов (методов и классов) независимо друг от друга.
|
||||||
|
\item\textbf{ Аннотации:} JUnit использует аннотации, такие как \texttt{@Test}, \texttt{@Before}, \texttt{@After}, которые упрощают написание тестов и делают код более читабельным. Например, аннотация \texttt{@Before }позволяет выполнять определенный код перед каждым тестом, а @After — после его завершения.
|
||||||
|
\item \textbf{Ассерты: }JUnit предоставляет различные методы ассертов, такие как assertEquals, assertTrue, assertNotNull, которые помогают проверять ожидаемые результаты тестов.
|
||||||
|
\item \textbf{Группировка тестов:} Тесты могут быть сгруппированы с помощью аннотации @Suite, что позволяет запускать их вместе.
|
||||||
|
\item \textbf{ Интеграция:} JUnit просто интегрируется с различными инструментами и фреймворками, такими как Maven, Gradle и широко используемые IDE, включая Eclipse и IntelliJ IDEA, что упрощает процесс тестирования.
|
||||||
|
|
||||||
|
\end{itemize}
|
||||||
|
\subsubsection{ Преимущества использования JUnit}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Упрощение процесса тестирования:} JUnit делает написание тестов более удобным и структурированным.
|
||||||
|
\item \textbf{Автоматизация:} Позволяет автоматизировать тесты, что снижает вероятность ошибок и повышает качество кода.\par
|
||||||
|
\item \textbf{Поддержка непрерывной интеграции:} JUnit хорошо совместим с системами непрерывной интеграции, позволяя запускать тесты автоматически при каждом изменении кода.
|
||||||
|
\item \textbf{Снижение затрат на отладку:} Регулярное тестирование помогает выявлять проблемы на ранних стадиях, что уменьшает расходы на их исправление.
|
||||||
|
\end{itemize}
|
||||||
|
\subsubsection{Функционал библиотеки}
|
||||||
|
JUnit имеет широкий функционал для проверки совпадения ожидаемого резульата, и результата полученного тестируемым методом.\par
|
||||||
|
\vspace{5pt}
|
||||||
|
Класс \texttt{junit.framework.Assert} предоставляет набор статических методов для проверки различных условий в тестах.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{assertEquals(expected, actual)} - проверяет равенство двух значений. Если значения не равны, тест завершается с ошибкой. Имеет перегрузки для различных типов данных.
|
||||||
|
|
||||||
|
\item \texttt{assertFalse(condition)} - проверяет, что переданное булево значение является \texttt{false}. Если значение \texttt{true}, тест завершается с ошибкой.
|
||||||
|
|
||||||
|
\item \texttt{assertNotNull(object)} - проверяет, что объект не является \texttt{null}. Если объект \texttt{null}, тест завершается с ошибкой.
|
||||||
|
|
||||||
|
\item \texttt{assertNull(object)} - проверяет, что объект является \texttt{null}. Если объект не \texttt{null}, тест завершается с ошибкой.
|
||||||
|
|
||||||
|
\item \texttt{assertNotSame(unexpected, actual)} - проверяет, что два объекта не ссылаются на один и тот же экземпляр. Если ссылки идентичны, тест завершается с ошибкой.
|
||||||
|
|
||||||
|
\item \texttt{assertSame(expected, actual)} - проверяет, что два объекта ссылаются на один и тот же экземпляр. Если ссылки разные, тест завершается с ошибкой.
|
||||||
|
|
||||||
|
\item \texttt{assertTrue(condition)} - проверяет, что переданное булево значение является \texttt{true}. Если значение \texttt{false}, тест завершается с ошибкой.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Класс \texttt{junit.framework.TestCase} наследуется от \texttt{junit.framework.Assert} и предоставляет базовую функциональность для создания тестовых случаев.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{run()} - основной метод, выполняющий тест. Содержит логику запуска и выполнения тестового случая.
|
||||||
|
|
||||||
|
\item \texttt{setUp()} - метод, выполняемый перед каждым тестом. Используется для инициализации тестового окружения и подготовки данных. В аннотационной версии JUnit 4+ заменён на \texttt{@Before}.
|
||||||
|
|
||||||
|
\item \texttt{tearDown()} - метод, выполняемый после каждого теста. Используется для очистки ресурсов после выполнения теста. В аннотационной версии JUnit 4+ заменён на \texttt{@After}.
|
||||||
|
\end{itemize}
|
||||||
|
\subsubsection{Этапы написания тестов}
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \textbf{Реализация теста}: Написание тестового метода и аннотирование его с помощью. \texttt{@Test}
|
||||||
|
\item \textbf{Настройка и очистка} Использование аннотаций \texttt{@Before} и \texttt{@After}
|
||||||
|
для выполнения операйи перед и после теста.
|
||||||
|
\item \textbf{Запуск тестов:} Использование встроенных средств IDE или командной строки для выполнения тестов.
|
||||||
|
\end{enumerate}
|
||||||
|
\subsection{Selenium WebDriver}
|
||||||
|
\textit{Selenium WebDriver } --- это фреймворк с открытым исходным кодом для автоматизации тестирования веб-приложений. Он предоставляет программный интерфейс для взаимодействия с браузерами, позволяя эмулировать действия пользователя на веб-страницах.
|
||||||
|
\textbf{Основные особенности Selenium WebDriver}:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Кроссбраузерность:} Поддержка всех популярных браузеров, включая Chrome,
|
||||||
|
Firefox, Safari, Edge и Opera.
|
||||||
|
\item \textbf{Многоязычность:} Возможность написания тестовых скриптов на различных
|
||||||
|
языках программирования — Java, Python, C\#, Ruby, JavaScript, PHP и Perl.
|
||||||
|
\item \textbf{Прямое взаимодействие:} WebDriver напрямую отправляет команды браузеру и
|
||||||
|
получает результаты, что обеспечивает более точное воспроизведение
|
||||||
|
пользовательских действий.
|
||||||
|
\item \textbf{Кроссплатформенность:} Возможность запуска тестов на различных операционных
|
||||||
|
системах (Windows, MacOS, Linux).
|
||||||
|
\item \textbf{Параллельное выполнение:} Поддержка одновременного запуска тестов в
|
||||||
|
разных браузерах для ускорения тестирования.
|
||||||
|
\end{itemize}\par
|
||||||
|
|
||||||
|
Архитектура Selenium WebDriver состоит из четырех основных компонентов:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Selenium Client Libraries:} Набор библиотек для различных языков
|
||||||
|
программирования, позволяющих писать и запускать тесты на предпочитаемом языке.
|
||||||
|
JSON Wire Protocol: REST API на основе JSON, обеспечивающий передачу
|
||||||
|
информации между клиентом и сервером через HTTP.
|
||||||
|
\item \textbf{Browser Drivers:} Специфичные для каждого браузера драйверы (ChromeDriver,
|
||||||
|
GeckoDriver для Firefox и др.), которые получают команды и выполняют их
|
||||||
|
в соответствующем браузере.
|
||||||
|
\item \textbf{Browsers:} Сами браузеры, в которых выполняются тестовые сценарии.
|
||||||
|
\end{itemize}
|
||||||
|
\textbf{Этапы работы с WebDriver}:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \textbf{Инициализация:} Создание экземпляра WebDriver и открытие браузера.
|
||||||
|
\item \textbf{Навигация:} Переход к нужной веб-странице с помощью метода get().
|
||||||
|
\item \textbf{Поиск элементов:} Обнаружение элементов на странице с использованием
|
||||||
|
различных локаторов (ID, XPath, CSS-селекторы и др.).
|
||||||
|
\item \textbf{Взаимодействие:} Выполнение действий над элементами (клик, ввод текста,
|
||||||
|
выбор из выпадающих списков и т.д.).
|
||||||
|
\item \textbf{Ожидание:} Использование явных и неявных ожиданий для синхронизации
|
||||||
|
с динамическими элементами страницы.
|
||||||
|
\item \textbf{Проверка:} Получение информации о состоянии элементов и проверка результатов.
|
||||||
|
\item \textbf{Завершение:} Закрытие браузера и освобождение ресурсов.
|
||||||
|
\end{enumerate}
|
||||||
|
Принцип выполнения команд:
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Команда из тестового скрипта преобразуется в HTTP-запрос через JSON Wire Protocol.
|
||||||
|
\item Запрос передается соответствующему драйверу браузера.
|
||||||
|
\item Драйвер интерпретирует запрос и выполняет необходимые действия в браузере.
|
||||||
|
\item Результат действия возвращается обратно в виде HTTP-ответа.
|
||||||
|
\item Ответ преобразуется в формат, понятный тестовому скрипту.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section{Описание выполненных работ}
|
||||||
|
\subsection{Работа №1}
|
||||||
|
В ходе работы необходимо прописать юнит тесты для методов библиотеки calcualtor.jar, реализующий функционал калькулятора, производящего вычисления суммы, разности, умножения, деления, возведения в степень, ивзлечение корня, а также значений базовых тригонометрических функций. Дря реализации тестов, необходимо использовать JUnit.
|
||||||
|
|
||||||
|
\subsubsection{Класс CalculatorTest}
|
||||||
|
Все тесты содержатся в классе CalculatorTest. В методе setUp() инициализируется объект \texttt{Calculator}. Константой DELTA задается допустимая погрешность. Код определения полей класса и метода setUp() представлен в листинге \ref{lst:CalculatorTest}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Класс CalculatorTest}, label={lst:CalculatorTest}]
|
||||||
|
class CalculatorTest {
|
||||||
|
private static final double DELTA = 0.0001;
|
||||||
|
private Calculator calculator;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
calculator = new Calculator();
|
||||||
|
}
|
||||||
|
}\end{lstlisting}
|
||||||
|
|
||||||
|
\subsubsection{Тесты для метода Sum}
|
||||||
|
В классе CalculatorTest реализованы следующие тесты для метода sum:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{testLongSum} - тест для проверки суммы двух чисел типа long.
|
||||||
|
\item \texttt{testDoubleSum} - тест для проверки суммы двух чисел типа double.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Код тестов представлен в листинге \ref{lst:SumTest}. Тесты параметризованы с помощью аннотации \texttt{@ParameterizedTest} и \texttt{@CsvSource}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Тесты для метода Sum}, label={lst:SumTest}]
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"1, 2, 3",
|
||||||
|
"5, 0, 5",
|
||||||
|
"-7, 7, 0",
|
||||||
|
"9223372036854775806, 1, 9223372036854775807"
|
||||||
|
})
|
||||||
|
void testLongSum(long a, long b, long expected) {
|
||||||
|
assertEquals(expected, calculator.sum(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"1.5, 2.5, 4.0",
|
||||||
|
"5.5, 0.0, 5.5",
|
||||||
|
"-3.5, 3.5, 0.0",
|
||||||
|
"0.1, 0.2, 0.3"
|
||||||
|
})
|
||||||
|
void testDoubleSum(double a, double b, double expected) {
|
||||||
|
assertEquals(expected, calculator.sum(a, b), DELTA);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\textbf{Результаты запуска тестов:}
|
||||||
|
|
||||||
|
Результаты запуска тестов представлены на рисунке \ref{fig:test1}.
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.65\linewidth]{img/test1.png}
|
||||||
|
\caption{Результаты запуска тестов для метода sum}
|
||||||
|
\label{fig:test1}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
По итогам запуска, метод sum прошел все тесты.
|
||||||
|
|
||||||
|
\subsubsection{Тесты для метода Mul}
|
||||||
|
|
||||||
|
В классе CalculatorTest реализованы следующие тесты для метода mul:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{testLongMul} - тест для проверки произведения двух чисел типа long.
|
||||||
|
\item \texttt{testDoubleMul} - тест для проверки произведения двух чисел типа double.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Код тестов представлен в листинге \ref{lst:MulTest}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Тесты для метода Mul}, label={lst:MulTest}]
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"2.5, 3.0, 7.5",
|
||||||
|
"0.0, 5.5, 0.0",
|
||||||
|
"-2.5, 3.0, -7.5",
|
||||||
|
"-2.5, -3.0, 7.5"
|
||||||
|
})
|
||||||
|
void testDoubleMult(double a, double b, double expected) {
|
||||||
|
assertEquals(expected, calculator.mult(a, b), DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"2, 3, 6",
|
||||||
|
"0, 5, 0",
|
||||||
|
"-2, 3, -6",
|
||||||
|
"-2, -3, 6",
|
||||||
|
"1000, 1000, 1000000"
|
||||||
|
})
|
||||||
|
void testLongMult(long a, long b, long expected) {
|
||||||
|
assertEquals(expected, calculator.mult(a, b));
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\textbf{Результаты запуска тестов:}
|
||||||
|
|
||||||
|
Результаты запуска тестов представлены на рисунке \ref{fig:test2}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.65\linewidth]{img/test2.png}
|
||||||
|
\caption{Результаты запуска тестов для метода mul}
|
||||||
|
\label{fig:test2}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
По итогам запуска, метод mul для типа long прошел все тесты, а для типа double прошёл лишь 1 из 4 тестов.
|
||||||
|
|
||||||
|
\subsubsection{Тесты для метода Sqrt}
|
||||||
|
|
||||||
|
В классе CalculatorTest реализованы следующие тесты для метода sqrt:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{testSqrt} - тест для проверки квадратного корня числа типа double. Проверяет положительные, отрицательные значения, а также 0.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Код тестов представлен в листинге \ref{lst:SqrtTest}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Тесты для метода Sqrt}, label={lst:SqrtTest}]
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(doubles = { 4.0, 0.0, -4.0, 1000000.0 })
|
||||||
|
void testSqrt(double value) {
|
||||||
|
double expected = Math.sqrt(value);
|
||||||
|
assertEquals(expected, calculator.sqrt(value), DELTA);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\textbf{Результаты запуска тестов:}
|
||||||
|
|
||||||
|
Результаты запуска тестов представлены на рисунке \ref{fig:test3}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.55\linewidth]{img/test3.png}
|
||||||
|
\caption{Результаты запуска тестов для метода sqrt}
|
||||||
|
\label{fig:test3}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
По итогам запуска, метод sqrt не прошёл один из четырёх тестов. Метод неккоректно работает с отрицательными числами.
|
||||||
|
|
||||||
|
\subsubsection{Тесты для метода Tg}
|
||||||
|
|
||||||
|
В классе CalculatorTest реализованы следующие тесты для метода tg:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{testTg} - тест для проверки тангенса числа типа double. Проверяет положительные, отрицательные значения, а также 0.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Код тестов представлен в листинге \ref{lst:TgTest}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={Тесты для метода Tg}, label={lst:TgTest}]
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(doubles = { 0, Math.PI / 6, Math.PI / 4, -Math.PI / 3, 10 })
|
||||||
|
void testTg(double angle) {
|
||||||
|
double expected = Math.tan(angle);
|
||||||
|
double actual = calculator.tg(angle);
|
||||||
|
assertEquals(expected, actual, 0.0001);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\textbf{Результаты запуска тестов:}
|
||||||
|
|
||||||
|
Результаты запуска тестов представлены на рисунке \ref{fig:test4}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.65\linewidth]{img/test4.png}
|
||||||
|
\caption{Результаты запуска тестов для метода tg}
|
||||||
|
\label{fig:test4}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
По итогам запуска, метод tg прошел лишь 1 из 5 тестов.
|
||||||
|
|
||||||
|
\subsubsection{Результаты работы №1}
|
||||||
|
В результате комплексного тестирования библиотеки \texttt{calculator.jar} были получены следующие результаты:
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \textbf{Тестирование метода сложения (SumTests)}:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Все тесты корректности сложения для целых чисел и чисел с плавающей точкой прошли успешно
|
||||||
|
\item Операции с граничными значениями (максимальные/минимальные значения \texttt{long} и \texttt{double}) выполняются без переполнения
|
||||||
|
\item Особые случаи (сложение с нулем) соответствуют ожидаемым результатам.
|
||||||
|
\end{itemize}
|
||||||
|
\item \textbf{Тестирование метода умножения (MulTests)}:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Метод для умножения целых чисел прошёл все тесты.
|
||||||
|
\item Метод для умножения чисел с плавающей точкой прошёл 1 из 4 тестов.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\item \textbf{Тестирование метода квадратного корня (SqrtTests)}:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Метод прошёл 3 из 4 тестов.
|
||||||
|
\item Метод корректно обрабатывает положительные значения и 0.
|
||||||
|
\item Отрицательные числа обрабатываются некорректно. Вместо того, чтобы вернуть ошибку или \texttt{NaN}, метод вычисляет значения корня по модулю числа.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\item \textbf{Тестирование метода тангенса (tgTests)}:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Метод прошёл 1 из 5 тестов.
|
||||||
|
\item Функция тангенса фактически всегда возвращает 1 (кроме случаев, когда \texttt{sin(a) = 0})
|
||||||
|
\item Свойство нечетности (\texttt{tg(-x) = -tg(x)}) не соблюдается, значение \texttt{tg(x)} равно 1.
|
||||||
|
\item Функция возвращает неккоректное значение при попытке вычислить \texttt{tg(0)}. При вычислении возваращется \texttt{NaN}, а не 0, как ожидается.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\item \textbf{Общие выводы:}
|
||||||
|
|
||||||
|
Два из четырёх тестируемых методов не прошли все тесты. В реализации методов умножения и вычисления тангенса присутствуют ошибки.
|
||||||
|
|
||||||
|
Метод умножения неккоректно работает с дробными числами. А метод для вычисления тангенса возвращает 1 для любого входного значения, кроме 0. При нулевом входном значении метод возвращает \texttt{NaN}.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\subsection{Работа №2}
|
||||||
|
В ходе выполнения работы №2 необходимо было реализовать два теста для тестирования web-сайта с помощью библиотеки Selenium WebDriver. Тесты должны проверять, что элементы сайта \texttt{https://jdi-testing.github.io/jdi-light/index.html} отображаются корректно и позволяют взаимодействовать с собой правильным образом. Реализация тестов должна быть выполнена согласно Java Code Convention и запускаться с помощью TestNG suite xml.\par
|
||||||
|
|
||||||
|
Тесты разделяются на 2 класса, в которых необходимо реализовать тесты, связанные с взаимодействием сайта.
|
||||||
|
|
||||||
|
В первом наборе тестов необходимо проверить корректность отображения страницы. Все сценарии, которые необходимо проверить, представлены в \hyperref[tab1]{таблице 1}
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\label{tab1}
|
||||||
|
\caption{\centering{Тест-кейсы для веб-приложения (с использованием SoftAsserts)}}
|
||||||
|
\footnotesize
|
||||||
|
\begin{tabularx}{\textwidth}{|c|>{\centering\arraybackslash}X|>{\centering\arraybackslash}X|>{\centering\arraybackslash}X|}
|
||||||
|
\hline
|
||||||
|
№ & Шаг тестирования & Данные & Ожидаемый результат \\
|
||||||
|
\hline
|
||||||
|
1 & Открыть тестовый сайт по URL & \url{https://jdi-testing.github.io/jdi-light/index.html} & Тестовый сайт открыт \\
|
||||||
|
\hline
|
||||||
|
2 & Проверить заголовок браузера & "Home Page" & Заголовок соответствует "Home Page" \\
|
||||||
|
\hline
|
||||||
|
3 & Выполнить вход в систему & Логин: Roman, Пароль: Jdi1234 & Пользователь авторизован \\
|
||||||
|
\hline
|
||||||
|
4 & Проверить отображение имени пользователя & "ROMAN IOVLEV" & Имя отображается корректно \\
|
||||||
|
\hline
|
||||||
|
5 & Проверить пункты меню в шапке & "HOME", "CONTACT FORM", "SERVICE", "METALS \& COLORS" & 4 пункта меню с правильным текстом \\
|
||||||
|
\hline
|
||||||
|
6 & Проверить изображения на странице & 4 изображения & Все изображения отображаются \\
|
||||||
|
\hline
|
||||||
|
7 & Проверить тексты под иконками & 4 текстовых блока & Тексты соответствуют ожидаемым \\
|
||||||
|
\hline
|
||||||
|
8 & Проверить наличие iframe & Кнопка "Frame Button" & iframe существует \\
|
||||||
|
\hline
|
||||||
|
9 & Проверить кнопку во фрейме & - & Кнопка "Frame Button" доступна \\
|
||||||
|
\hline
|
||||||
|
10 & Вернуться в основное окно & - & Фокус на основном окне \\
|
||||||
|
\hline
|
||||||
|
11 & Проверить левое меню & "Home", "Contact form", "Service", "Metals \& Colors", "Elements packs" & 5 пунктов меню с правильным текстом \\
|
||||||
|
\hline
|
||||||
|
12 & Закрыть браузер & - & Браузер закрыт \\
|
||||||
|
\hline
|
||||||
|
\end{tabularx}
|
||||||
|
\end{table}\par
|
||||||
|
Во втором наборе тестов необходимо проверить корректность взаимодействия пользователя с сайтом (в частности, правильность выбора чекбоксов, радиобаттонов и элементов из выпадающего списка).
|
||||||
|
Таблица тестов, необходимых к реализации во втором упражнении, представлена в \hyperref[tab2]{таблице 2}.
|
||||||
|
|
||||||
|
\begin{table}[h!]
|
||||||
|
\centering
|
||||||
|
\label{tab2}
|
||||||
|
\caption{\centering{Тест-кейсы для веб-приложения (с использованием SoftAsserts)}}
|
||||||
|
\footnotesize
|
||||||
|
\begin{tabularx}{\textwidth}{|c|>{\RaggedRight\arraybackslash}X|>{\RaggedRight\arraybackslash}X|>{\RaggedRight\arraybackslash}X|}
|
||||||
|
\hline
|
||||||
|
\ № & Шаг тестирования (Testing Step) & Данные (Data) & Ожидаемый результат (Expected Result) \\
|
||||||
|
\hline
|
||||||
|
1 & Открыть тестовый сайт по URL & \url{https://jdi-testing.github.io/jdi-light/index.html} & Тестовый сайт открыт (Test site is opened) \\
|
||||||
|
\hline
|
||||||
|
2 & Проверить заголовок браузера (Check browser title) & "Home Page" & Заголовок соответствует "Home Page" (Title matches "Home Page") \\
|
||||||
|
\hline
|
||||||
|
3 & Выполнить вход в систему (Perform login) & username: Roman, password: Jdi1234 & Пользователь авторизован (User is logged in) \\
|
||||||
|
\hline
|
||||||
|
4 & Проверить отображение имени пользователя (Verify username display) & "ROMAN IOVLEV" & Имя отображается корректно (Name is displayed correctly) \\
|
||||||
|
\hline
|
||||||
|
5 & Открыть через хедер меню Service -> Different Elements Page (Navigate using header menu: Service -> Different Elements Page) & & Страница открыта (Page is opened) \\
|
||||||
|
\hline
|
||||||
|
6 & Выбрать чекбоксы (Select checkboxes) & Water, Wind & Элементы отмечены (Elements are checked) \\
|
||||||
|
\hline
|
||||||
|
7 & Выбрать переключатель (Select radio) & Selen & Элемент отмечен (Element is selected) \\
|
||||||
|
\hline
|
||||||
|
8 & Выбрать в один из выпадающего списка (Select in dropdown) & Yellow & Элемент выбран (Element is chosen) \\
|
||||||
|
\hline
|
||||||
|
9 & Проверить, что для каждого чекбокса, radio и dropdown есть отдельная строчка лога (Verify that for each checkbox, radio, and dropdown there is a separate log row) & & Логи отображаются и соответствуют выбранным значениям (Logs are displayed and correspond to selected values) \\
|
||||||
|
\hline
|
||||||
|
10 & Закрыть браузер (Close browser) & & Браузер закрыт (Browser is closed) \\
|
||||||
|
\hline
|
||||||
|
\end{tabularx}
|
||||||
|
\end{table}
|
||||||
|
\subsubsection{Класс DriverSetup}
|
||||||
|
Класс \texttt{DriverSetup} выполняет первоначальную настройку \texttt{WebDriver} перед запуском тестов. Он устанавливает системные свойства для Chrome Driver, настраивает HTTP клиент и создает экземпляр Chrome Driver, открывая тестовый сайт и выполняет авторизацию пользователя.
|
||||||
|
|
||||||
|
\begin{lstlisting}[label= list5, caption=\ ]
|
||||||
|
public class DriverSetup {
|
||||||
|
protected static WebDriver driver;
|
||||||
|
|
||||||
|
@BeforeTest
|
||||||
|
public static void setup() {
|
||||||
|
System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver.exe");
|
||||||
|
System.setProperty("webdriver.http.factory", "jdk-http-client");
|
||||||
|
|
||||||
|
driver = new ChromeDriver();
|
||||||
|
|
||||||
|
driver.navigate().to("https://jdi-testing.github.io/jdi-light/index.html");
|
||||||
|
|
||||||
|
driver.findElement(By.cssSelector("html > body > header > div > nav > ul.uui-navigation.navbar-nav.navbar-right > li > a > span")).click();
|
||||||
|
driver.findElement(By.id("name")).sendKeys("Roman");
|
||||||
|
driver.findElement(By.id("password")).sendKeys("Jdi1234");
|
||||||
|
driver.findElement(By.id("login-button")).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterTest
|
||||||
|
public static void exit() {
|
||||||
|
driver.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Класс содержит следующие элементы:
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{driver}: Защищенная статическая переменная типа WebDriver, представляющая экземпляр браузера Chrome, используемый для выполнения тестов.
|
||||||
|
\item \textbf{@BeforeTest setup()}: Статический метод, помеченный аннотацией @BeforeTest, выполняемый перед всеми тестовыми методами. Он выполняет следующие действия:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Устанавливает системные свойства `webdriver.chrome.driver` и `webdriver.http.factory`.
|
||||||
|
\item Создает экземпляр `ChromeDriver`.
|
||||||
|
\item Открывает тестовый сайт по URL: \url{https://jdi-testing.github.io/jdi-light/index.html}.
|
||||||
|
\item Выполняет вход в систему, находя и заполняя поля логина и пароля, а также нажимая кнопку входа.
|
||||||
|
\end{itemize}
|
||||||
|
\item \textbf{@AfterTest exit()}: Статический метод, помеченный аннотацией `@AfterTest`, выполняемый после всех тестовых методов. Он закрывает браузер с помощью `driver.close()`.
|
||||||
|
\end{itemize}
|
||||||
|
\subsubsection{Класс Task1Test}
|
||||||
|
Класс \texttt{Task1Test} является тестовым классом, который выполняет проверки различных элементов на главной странице веб-сайта. Для проверки ожидаемых результатов используются "мягкие" утверждения (SoftAsserts), что позволяет продолжить выполнение теста даже в случае неудачи одного из утверждений.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=Java, caption=Класс Task1Test, basicstyle=\footnotesize\ttfamily, breaklines=true]
|
||||||
|
package edu.hsai.homework2;
|
||||||
|
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
import org.testng.asserts.SoftAssert;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Task1Test extends DriverSetup {
|
||||||
|
@Test
|
||||||
|
public void testTask1() {
|
||||||
|
SoftAssert softAssert = new SoftAssert();
|
||||||
|
|
||||||
|
softAssert.assertEquals(driver.getTitle(), "Home Page");
|
||||||
|
|
||||||
|
softAssert.assertEquals(driver.findElement(By.id("user-name")).getText(), "ROMAN IOVLEV");
|
||||||
|
|
||||||
|
List<WebElement> headerItems = driver.findElements(By.cssSelector("ul.uui-navigation.nav > li"));
|
||||||
|
softAssert.assertEquals(headerItems.size(), 4);
|
||||||
|
|
||||||
|
String[] expectedHeaderTexts = {"HOME", "CONTACT FORM", "SERVICE", "METALS & COLORS"};
|
||||||
|
for (int i = 0; i < headerItems.size(); i++) {
|
||||||
|
softAssert.assertTrue(headerItems.get(i).isDisplayed());
|
||||||
|
softAssert.assertEquals(headerItems.get(i).getText(), expectedHeaderTexts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<WebElement> images = driver.findElements(By.cssSelector(".benefit-icon > span"));
|
||||||
|
softAssert.assertEquals(images.size(), 4);
|
||||||
|
for (WebElement image : images) {
|
||||||
|
softAssert.assertTrue(image.isDisplayed());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<WebElement> texts = driver.findElements(By.className("benefit-txt"));
|
||||||
|
softAssert.assertEquals(texts.size(), 4);
|
||||||
|
|
||||||
|
String[] expectedTexts = {
|
||||||
|
"To include good practices\nand ideas from successful\nEPAM project",
|
||||||
|
"To be flexible and\ncustomizable",
|
||||||
|
"To be multiplatform",
|
||||||
|
"Already have good base\n (about 20 internal and \n some external projects), \n wish to get more..."
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < texts.size(); i++) {
|
||||||
|
softAssert.assertEquals(texts.get(i).getText(), expectedTexts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebElement iframe = driver.findElement(By.id("frame"));
|
||||||
|
softAssert.assertTrue(iframe.isDisplayed());
|
||||||
|
|
||||||
|
driver.switchTo().frame(iframe);
|
||||||
|
WebElement frameButton = driver.findElement(By.id("frame-button"));
|
||||||
|
softAssert.assertTrue(frameButton.isDisplayed());
|
||||||
|
|
||||||
|
driver.switchTo().defaultContent();
|
||||||
|
|
||||||
|
List<WebElement> leftMenuItems = driver.findElements(By.cssSelector("ul.sidebar-menu.left > li"));
|
||||||
|
softAssert.assertEquals(leftMenuItems.size(), 5);
|
||||||
|
|
||||||
|
String[] expectedMenuTexts = {"Home", "Contact form", "Service", "Metals & Colors", "Elements packs"};
|
||||||
|
for (int i = 0; i < leftMenuItems.size(); i++) {
|
||||||
|
WebElement item = leftMenuItems.get(i);
|
||||||
|
softAssert.assertTrue(item.isDisplayed());
|
||||||
|
softAssert.assertEquals(item.getText(), expectedMenuTexts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
softAssert.assertAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Класс содержит следующие основные компоненты:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Наследование от DriverSetup}: Класс \texttt{Task1Test} наследуется от класса \texttt{DriverSetup}, который выполняет предварительную настройку WebDriver и открывает веб-сайт.
|
||||||
|
\item \textbf{@Test testTask1()}: Это тестовый метод, помеченный аннотацией \texttt{@Test}, который указывает, что это метод TestNG для выполнения тестов.
|
||||||
|
\item \textbf{SoftAssert softAssert = new SoftAssert()}: Создание экземпляра \texttt{SoftAssert}, который позволяет собирать ошибки и не останавливать выполнение теста при первой неудаче.
|
||||||
|
\item \textbf{Проверки (Assertions)}: Метод содержит ряд проверок с использованием \texttt{softAssert.assertEquals()} и \texttt{softAssert.assertTrue()} для проверки различных элементов веб-страницы:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Заголовок страницы (\texttt{softAssert.assertEquals(driver.getTitle(), "Home Page")})).
|
||||||
|
\item Имя пользователя (\texttt{softAssert.assertEquals(driver.findElement(By.id("user-name")).getText(), "ROMAN IOVLEV")})).
|
||||||
|
\item Элементы в секции заголовка (количество и текст элементов меню).
|
||||||
|
\item Изображения на главной странице (количество и отображение).
|
||||||
|
\item Тексты под иконками (количество и соответствие ожидаемым текстам).
|
||||||
|
\item Наличие и отображение iframe.
|
||||||
|
\item Наличие и отображение кнопки во iframe.
|
||||||
|
\item Элементы в левом меню (количество и текст элементов).
|
||||||
|
\end{itemize}
|
||||||
|
\item \textbf{Переключение на iframe и обратно}: В коде происходит переключение на iframe для проверки содержимого внутри него, а затем возврат обратно к основному содержанию страницы.
|
||||||
|
\item \textbf{softAssert.assertAll()}: Вызов этого метода в конце тестового метода позволяет убедиться, что все собранные ошибки будут выведены, и тест завершится с соответствующим статусом.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\subsubsection{Класс Task2Test}
|
||||||
|
Класс \texttt{Task2Test} является тестовым классом, который проверяет различные элементы и функциональности веб-сайта. Он использует библиотеку Selenium WebDriver для взаимодействия с веб-страницей и библиотеку TestNG для организации и выполнения тестов. Класс выполняет проверку заголовка страницы, имени пользователя, а также взаимодействует с элементами на странице "Different Elements" (чекбоксы, радиокнопки, выпадающий список) и проверяет логи.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=Java, caption=Класс Task2Test, basicstyle=\footnotesize\ttfamily, breaklines=true]
|
||||||
|
public class Task2Test extends DriverSetup {
|
||||||
|
private static final By USER_NAME = By.id("user-name");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBrowserTitle() {
|
||||||
|
assertEquals(driver.getTitle(), "Home Page", "Browser title should be 'Home Page'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLogin() {
|
||||||
|
WebElement userNameElement = new WebDriverWait(driver, Duration.ofSeconds(10))
|
||||||
|
.until(ExpectedConditions.visibilityOfElementLocated(USER_NAME));
|
||||||
|
|
||||||
|
assertTrue(userNameElement.isDisplayed(), "Username should be displayed");
|
||||||
|
assertEquals(userNameElement.getText(), "ROMAN IOVLEV", "Username should be 'ROMAN IOVLEV'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testElements() {
|
||||||
|
WebElement serviceDropdown = driver.findElement(By.cssSelector("header .nav > li.dropdown"));
|
||||||
|
serviceDropdown.click();
|
||||||
|
|
||||||
|
WebElement differentElementsLink = driver.findElement(By.xpath("//a[text()='Different elements']"));
|
||||||
|
differentElementsLink.click();
|
||||||
|
assertEquals(driver.getTitle(), "Different Elements", "Заголовок страницы 'Different Elements' неверный.");
|
||||||
|
|
||||||
|
List<String> checkboxesToSelect = Arrays.asList("Water", "Wind");
|
||||||
|
List<WebElement> checkboxes = new WebDriverWait(driver, Duration.ofSeconds(10))
|
||||||
|
.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(
|
||||||
|
By.cssSelector(".label-checkbox")
|
||||||
|
));
|
||||||
|
|
||||||
|
for (WebElement checkbox : checkboxes) {
|
||||||
|
if (checkboxesToSelect.contains(checkbox.getText())) {
|
||||||
|
if (!checkbox.findElement(By.tagName("input")).isSelected()) {
|
||||||
|
checkbox.click();
|
||||||
|
}
|
||||||
|
assertTrue(checkbox.findElement(By.tagName("input")).isSelected());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String radioToSelect = "Selen";
|
||||||
|
List<WebElement> radios = new WebDriverWait(driver, Duration.ofSeconds(10))
|
||||||
|
.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(
|
||||||
|
By.cssSelector(".label-radio")
|
||||||
|
));
|
||||||
|
|
||||||
|
for (WebElement radio : radios) {
|
||||||
|
if (radio.getText().equals(radioToSelect)) {
|
||||||
|
radio.click();
|
||||||
|
assertTrue(radio.findElement(By.tagName("input")).isSelected());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String dropdownValueToSelect = "Yellow";
|
||||||
|
WebElement dropdown = new WebDriverWait(driver, Duration.ofSeconds(10))
|
||||||
|
.until(ExpectedConditions.elementToBeClickable(
|
||||||
|
By.cssSelector(".colors select")
|
||||||
|
));
|
||||||
|
|
||||||
|
Select select = new Select(dropdown);
|
||||||
|
select.selectByVisibleText(dropdownValueToSelect);
|
||||||
|
assertEquals(select.getFirstSelectedOption().getText(), dropdownValueToSelect);
|
||||||
|
|
||||||
|
List<WebElement> logs = new WebDriverWait(driver, Duration.ofSeconds(10))
|
||||||
|
.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(
|
||||||
|
By.cssSelector(".logs li")
|
||||||
|
));
|
||||||
|
for (var elem : logs)
|
||||||
|
System.out.println(elem.getText());
|
||||||
|
|
||||||
|
List<String> expectedLogs = Arrays.asList(
|
||||||
|
"Water: condition changed to true",
|
||||||
|
"Wind: condition changed to true",
|
||||||
|
"metal: value changed to Selen",
|
||||||
|
"Colors: value changed to Yellow"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedLogs.size(); i++) {
|
||||||
|
String actualLog = logs.get((logs.size()-1) - i).getText().replaceAll("\\d{2}:\\d{2}:\\d{2}", "").trim();
|
||||||
|
assertTrue(actualLog.endsWith(expectedLogs.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Класс содержит следующие основные компоненты:
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Наследование от DriverSetup}: Класс \texttt{Task2Test} наследуется от класса \texttt{DriverSetup}, который выполняет предварительную настройку WebDriver и открывает веб-сайт.
|
||||||
|
\item \textbf{Поле USER\_NAME}: Приватное статическое поле \texttt{USER\_NAME} типа \texttt{By}, содержащее локатор для элемента с именем пользователя.
|
||||||
|
\item \textbf{@Test testBrowserTitle()}: Тестовый метод, который проверяет заголовок браузера на соответствие значению "Home Page". Использует \texttt{assertEquals} для проверки.
|
||||||
|
\item \textbf{@Test testLogin()}: Тестовый метод, который проверяет, что имя пользователя отображается и соответствует ожидаемому значению "ROMAN IOVLEV". Использует явное ожидание (\texttt{WebDriverWait}) для проверки видимости элемента.
|
||||||
|
\item \textbf{@Test testElements()}: Тестовый метод, который выполняет следующие шаги:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Открывает страницу "Different Elements" через меню "Service".
|
||||||
|
\item Выбирает чекбоксы "Water" и "Wind".
|
||||||
|
\item Выбирает радиокнопку "Selen".
|
||||||
|
\item Выбирает значение "Yellow" в выпадающем списке.
|
||||||
|
\item Проверяет логи на соответствие ожидаемым значениям.
|
||||||
|
\end{itemize}
|
||||||
|
\item \textbf{Явные ожидания (WebDriverWait)}: Используются для ожидания появления и кликабельности элементов, что делает тесты более стабильными.
|
||||||
|
\item \textbf{Проверка логов}: Код проверяет логи на соответствие ожидаемым значениям, учитывая порядок и формат записей.
|
||||||
|
\end{itemize}
|
||||||
|
\subsubsection{Результаты работы №2}
|
||||||
|
Результаты запуска тестов представлены на Рис.~\ref{fig:selenium-tests}
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=0.35\linewidth]{img/selenium-tests.png}
|
||||||
|
\caption{Результаты выполнения тестов Selenium WebDriver}
|
||||||
|
\label{fig:selenium-tests}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
Все разработанные тесты для проверки веб-сайта успешно пройдены. Тесты охватывают широкий спектр элементов страницы, включая заголовки, элементы навигации, iframe, чекбоксы и выпадающие списки.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Заключение}
|
||||||
|
\addcontentsline{toc}{section}{Заключение}
|
||||||
|
|
||||||
|
В ходе выполнения лабораторной работы были изучены ключевые аспекты автоматизированного тестирования программного обеспечения. Работа охватила создание, написание и выполнение юнит-тестов для Java-библиотеки calculator.jar с использованием JUnit, а также организацию тестирования веб-страниц с помощью Selenium WebDriver и TestNG.
|
||||||
|
|
||||||
|
В результате тестирования библиотеки calculator.jar были обнаружены неточности в реализации некоторых методов, в частности, неверное вычисление тангенса и некорректная работа с числами с плавающей точкой при умножении. Эти ошибки могли бы остаться незамеченными при поверхностном ручном тестировании, что демонстрирует ценность автоматизированного подхода.
|
||||||
|
|
||||||
|
При тестировании веб-сайта с помощью Selenium WebDriver была подтверждена корректность отображения элементов и их функциональность. Успешное прохождение всех тестов показало, что сайт работает в соответствии с ожиданиями в тестовых сценариях.
|
||||||
|
|
||||||
|
Использованные инструменты автоматизированного тестирования продемонстрировали себя как эффективное средство для систематической проверки программного обеспечения на соответствие требованиям. Они позволяют выполнять тесты на больших наборах данных и многократно повторять одни и те же сценарии, что затруднительно при ручном тестировании.
|
||||||
|
|
||||||
|
Тем не менее, автоматизированное тестирование имеет свои ограничения. Оно фокусируется преимущественно на проверке функциональности и не способно в полной мере оценить удобство использования или обнаружить непредвиденные сценарии использования. Кроме того, сами автоматизированные тесты могут содержать ошибки.
|
||||||
|
|
||||||
|
Таким образом, можно заключить, что наиболее эффективный подход к тестированию сочетает в себе автоматизированные и ручные методы. Автоматизированное тестирование обеспечивает стабильность и повторяемость проверок, а ручное позволяет выявлять проблемы, связанные с удобством использования и нестандартными сценариями. Комплексное применение обоих подходов существенно повышает качество программного обеспечения.
|
||||||
|
|
||||||
|
\newpage
|
||||||
|
\section*{Список литературы}
|
||||||
|
\addcontentsline{toc}{section}{Список литературы}
|
||||||
|
|
||||||
|
\vspace{-1.5cm}
|
||||||
|
\begin{thebibliography}{0}
|
||||||
|
\bibitem{1} {Что такое Selenium WebDriver? — Habr. [Электронный ресурс]. \\URL: \href{https://habr.com/ru/articles/152971/}{https://habr.com/ru/articles/152971/} (дата обращения: 30.04.2025).}
|
||||||
|
|
||||||
|
\bibitem{2} {Selenium IDE — Habr. [Электронный ресурс]. \\URL: \href{https://habr.com/ru/articles/590607/}{https://habr.com/ru/articles/590607/} (дата обращения: 30.04.2025).}
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||