Міністерство освіти і науки України
Національний університет „Львівська політехніка”
Кафедра ЕОМ
Звіт
з лабораторної роботи №6
з дисципліни
“ Тестування програмних засобів ”
Львів 2012
Тема: Тестування «чорного» ящика.
Мета: розглянути метод тестування «чорного» ящика, продемонструвати методику на конкретному прикладі.
Виконання роботи:
Завданням функціонального тестування є перевірка відповідності програми своїм специфікаціям. При даному підході текст програми не доступний, і програма розглядається як «чорний ящик». Найпоширенішими видами функціонального тестування є методи випадкового тестування, еквівалентної розбивки й аналізу граничних умов.
Випадкове (стохастичне) тестування. Відповідно до даного методу створюється необхідна кількість незалежних тестів, у яких вхідні дані генеруються випадковим чином. Недоліком даного методу є загальна кількість тестів, які необхідно згенерувати відповідно до вимог надійності, до того ж забезпечивши незалежність цих тестів. Так, наприклад, для забезпечення надійності програмного забезпечення з імовірністю відмови не більше 10 і з помилкою не більше 5%, потрібно згенерувати 299 572 тестів. З метою скорочення кількості необхідних тестів Майєрсом було запропоновано розглядати розбиття безлічі вихідних даних на еквівалентні класи. Тестування за класами еквівалентності. Відповідно до даної методики необхідно розбити множину значень вхідних даних на кінцеве число підмножин (які будуть називатися класами еквівалентності), щоб кожний тест, що є представником певного класу, був еквівалентним будь-якому іншому тесту цього класу. Два тести є еквівалентними, якщо вони виявляють ті самі помилки. Проектування тестів за методом класів еквівалентності проводиться у два етапи:
- виділення за специфікацією класів еквівалентності;
- побудова множини тестів.
На першому етапі відбувається вибір зі специфікації кожної вхідної умови та розбиття її на дві або більше групи, що відповідають так званим – правильним класам еквівалентності (ПКЕ) та – неправильним класам еквівалентності (НКЕ), тобто множинам допустимих для програми й недопустимих значень вхідних даних. Цей процес залежить від вигляду вхідної умови.
Наприклад: якщо вхідна умова описує множину (|х|<=0.5), то визначається один ПКЕ (-0.5<=х <=0.5) і два НКЕ (х< -0.5; х>0.5). На другому етапі методу класів еквівалентності виділені класи використовуються для побудови тестів. Для НКЕ тести проектуються таким чином, що кожен тест покриває один і тільки один НКЕ, доки всі НКЕ не будуть покриті. Метод класів еквівалентності дозволяє значно скоротити кількість тестів у порівнянні з методом випадкового тестування, але також має свої недоліки. Основний з них - це складність виділення класів еквівалентності, особливо НКЕ, а також можливий пропуск певних типів високоефективних тестів (тобто тестів, що характеризуються великою ймовірністю виявлення помилок). Так, наприклад, мінімальні й максимальні припустимі значення вхідних параметрів дозволяють виявити більшість помилок, пов'язаних з відповідностями й переповненнями типів даних. Для вирішення даної проблеми був запропонований метод аналізу граничних умов.
Під граничними умовами розуміють ситуації, що виникають безпосередньо на границі певної вхідної або вихідної умови, вище або нижче її. Метод аналізу граничних умов відрізняється від методу класів еквівалентності наступним:
- вибір будь-якого представника класу еквівалентності здійснюється таким чином, щоб перевірити тестом кожну границю цього класу;
- при побудові тестів розглядаються не тільки вхідні умови, але й вихідні (тобто певні специфіковані обмеження на значення вхідних даних).
Загальні правила методу аналізу граничних умов:
побудувати тести для границь множини допустимих значень вхідних даних і тести з недопустимими значеннями, що відповідають незначному виходу за межі цієї множини. Наприклад, для множини [-1.0; 1.0] будуються тести -1.0; 1.0; -и.001; 1.001; Зауважимо, що на практиці з метою локалізації несправностей створюють також тести, що відповідають допустимим значенням, тобто є внутрішніми для множини та ті, що незначно відхиляються від граничних значень: -1.0; 1.0; -1.001; 1.001;0.999;- 0.999 Якщо множина допустимих значень вхідних даних дискретна, то будуються тести для мінімального й максимального значення вхідних умов і тести для значень, більших або менших цих величин. Наприклад, якщо вхідний файл може містити від 1 до 255 записів, то вибираються тести для порожнього файлу та файлу, що містить 1, 254, 255 і 256 записів.
1) використовувати перше правило для кожної вихідної умови;
2) якщо вхідні й вихідні дані програми являють собою впорядковану множину (послідовний файл, лінійний список та ін.), то при тестуванні зосередити увагу на першому й останньому елементі множини;
3) повторити процедуру для всіх знайдених граничних умов.
Аналіз граничних умов - один з найбільш корисних методів проектування тестів. Але він часто виявляється неефективним через те, що граничні умови іноді ледь вловимі, а їхнє виявлення досить важке.
Приклад тестування:
У ролі «чорного ящика» виступає програма, яка по введеній даті визначає скільки днів залишилось до гіпотетичного кінця світу 21.12.2012
Рис. 1 головне вікно програми
Дата вводиться у форматі: дд.мм.рррр тому вхідним параметром буде стрічка з датою. А конкретні вхідні параметри матимуть наступні значення
ДД − (01..31)
ММ − (01..12)
РРРР − (0001..9999)
Результатом роботи програми є поле «результат» з кількістю днів що залишилися до кінця світу. Якщо були введені некоректні дані, то у полі результат з’явиться повідомлення «error».
Для створення автоматизованих тестів я використав мову програмування С# і фреймворк CUIT. Він дозволяє автоматизовано тестувати інтерфейс користувача, шляхом натискання на елементи керування на формі, зчитувати дані, вводити дані. Результати тестів виводяться в стандартне вікно переглядача тестів MS Visual Studio та записуються в лог файли.
Для створення автоматизованих тестів даної програми необхідно виділити параметри по яких потрібно тестувати:
Тестування випадковою підходящою датою
Тестування критичних значень
Тестування на введення нечислових даних
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows.Input;
using System.Windows.Forms;
using System.Drawing;
using Microsoft.VisualStudio.TestTools.UITesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UITest.Extension;
using Keyboard = Microsoft.VisualStudio.TestTools.UITesting.Keyboard;
namespace testUILab6
{
/// <summary>
/// Сводное описание для CodedUITest1
/// </summary>
[CodedUITest]
public class CodedUITest1
{
public CodedUITest1()
{
Log.clearLogs();
}
[TestInitialize]
public void LaunchPrg()
{
this.UIMap.LaunchPrg();
}
[TestMethod]
public void TestGoodParam()
{
this.UIMap.TestGoodData();
}
[TestMethod]
public void TestBadParam()
{
this.UIMap.TestBadData();
}
[TestMethod]
public void TestCriticalParam()
{
this.UIMap.TestCriticalData();
}
[TestCleanup]
public void ClosePrg()
{
this.UIMap.ClosePrg();
}
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
private TestContext testContextInstance;
public UIMap UIMap
{
get
{
if ((this.map == null))
{
this.map = new UIMap();
}
return this.map;
}
}
private UIMap map;
}
}
public void LaunchPrg()
{
// Запуск "D:\PolyTech\ТПЗ\testlab6\testlab6\bin\Debug\testlab6.exe"
ApplicationUnderTest uIДатаEndWindow = ApplicationUnderTest.Launch(this.RecordedMethod1Params.UIДатаEndWindowExePath, this.RecordedMethod1Params.UIДатаEndWindowAlternateExePath);
}
public void ClosePrg()
{
WinButton uICloseButton = this.UIДатаEndWindow.UIДатаEndTitleBar.UICloseButton;
Mouse.Click(uICloseButton, new Point(19, 6));
}
public void TestGoodData()
{
WinEdit uIMaskedTextBox1Edit = this.UIДатаEndWindow.UI________Window.UIMaskedTextBox1Edit;
WinEdit uIMaskedTextBox4Edit = this.UIДатаEndWindow.UIMaskedTextBox4Window.UIMaskedTextBox4Edit;
WinButton uIОчиститиButton = this.UIДатаEndWindow.UIОчиститиWindow.UIОчиститиButton;
WinButton uIОбрахуватиButton = this.UIДатаEndWindow.UIОбрахуватиWindow.UIОбрахуватиButton;
int i;
bool isOk = true;
Random rd = new Random();
for (i = 0; i < Constants.TESTS_NUMBER_PER_OPERATION; i++)
{
int day = rd.Next(1, 28);
int mon = rd.Next(1, 12);
int goodyear = rd.Next(1, 2011);
DateTime date = new DateTime(goodyear, mon, day);
Keyboard.SendKeys(uIMaskedTextBox1Edit, date.ToString("ddMMyyyy"), ModifierKeys.None);
Mouse.Click(uIОбрахуватиButton, new Point(20, 11));
if (getDays(goodyear, mon, day) != uIMaskedTextBox4Edit.Text)
{
Log.writeFailedMessage(
String.Format("fail: param \"{2}\" expected \"{0}\" - actual \"{1}\"",
getDays(goodyear,mon,day),
uIMaskedTextBox4Edit.Text,
date.ToString("ddMMyyyy")));
isOk = false;
}
else
{
Log.writePassedMessage(
String.Format("pass: param \"{2}\" expected \"{0}\" - actual \"{1}\"",
getDays(goodyear, mon, day),
uIMaskedTextBox4Edit.Text,
date.ToString("ddMMyyyy")));
}
Mouse.Click(uIОчиститиButton, new Point(54, 6));
}
Assert.IsTrue(isOk);
}
public void TestBadData()
{
WinEdit uIMaskedTextBox1Edit = this.UIДатаEndWindow.UI________Window.UIMaskedTextBox1Edit;
WinEdit uIMaskedTextBox4Edit = this.UIДатаEndWindow.UIMaskedTextBox4Window.UIMaskedTextBox4Edit;
WinButton uIОчиститиButton = this.UIДатаEndWindow.UIОчиститиWindow.UIОчиститиButton;
WinButton uIОбрахуватиButton = this.UIДатаEndWindow.UIОбрахуватиWindow.UIОбрахуватиButton;
int i;
bool isOk = true;
Random rd = new Random();
for (i = 0; i < Constants.TESTS_NUMBER_PER_OPERATION; i++)
{
int day = rd.Next(1, 31);
int mon = rd.Next(1, 12);
int goodyear = rd.Next(1, 9999 - 2012) + 2012;
DateTime date = new DateTime(goodyear, mon, day);
Keyboard.SendKeys(uIMaskedTextBox1Edit, date.ToString("ddMMyyyy"), ModifierKeys.None);
Mouse.Click(uIОбрахуватиButton, new Point(20, 11));
if ("error" != uIMaskedTextBox4Edit.Text)
{
Log.writeFailedMessage(
String.Format("fail: param \"{2}\" expected \"{0}\" - actual \"{1}\"",
"error",
uIMaskedTextBox4Edit.Text,
date.ToString("ddMMyyyy")));
isOk = false;
}
else
{
Log.writePassedMessage(
String.Format("pass: param \"{2}\" expected \"{0}\" - actual \"{1}\"",
"error",
uIMaskedTextBox4Edit.Text,
date.ToString("ddMMyyyy")));
}
Mouse.Click(uIОчиститиButton, new Point(54, 6));
}
Assert.IsTrue(isOk);
}
public void TestCriticalData()
{
WinEdit uIMaskedTextBox1Edit = this.UIДатаEndWindow.UI________Window.UIMaskedTextBox1Edit;
WinEdit uIMaskedTextBox4Edit = this.UIДатаEndWindow.UIMaskedTextBox4Window.UIMaskedTextBox4Edit;
WinButton uIОчиститиButton = this.UIДатаEndWindow.UIОчиститиWindow.UIОчиститиButton;
WinButton uIОбрахуватиButton = this.UIДатаEndWindow.UIОбрахуватиWindow.UIОбрахуватиButton;
int i;
bool isOk = true;
Random rd = new Random();
for (i = 0; i < Constants.TESTS_NUMBER_PER_OPERATION; i++)
{
int day = rd.Next(1, 28);
int mon = rd.Next(1, 12);
int goodyear = rd.Next(1, 2011);
DateTime date = new DateTime(goodyear, mon, day);
char[] ch = date.ToString("ddMMyyyy").ToCharArray();
for (int j = 0; j < 8; j++)
{
if (rd.Next(0, 2) == 1)
{
ch[j] = '_';
}
}
Keyboard.SendKeys(uIMaskedTextBox1Edit, new String(ch), ModifierKeys.None);
Mouse.Click(uIОбрахуватиButton, new Point(20, 11));
if ("error" != uIMaskedTextBox4Edit.Text)
{
Log.writeFailedMessage(
String.Format("fail: param \"{2}\" expected \"{0}\" - actual \"{1}\"",
new String(ch),
uIMaskedTextBox4Edit.Text,
date.ToString("ddMMyyyy")));
isOk = false;
}
else
{
Log.writePassedMessage(
String.Format("pass: param \"{2}\" expected \"{0}\" - actual \"{1}\"",
new String(ch),
uIMaskedTextBox4Edit.Text,
date.ToString("ddMMyyyy")));
}
Mouse.Click(uIОчиститиButton, new Point(54, 6));
}
Assert.IsTrue(isOk);
}
//----------------------------------
private String getDays(int year, int month, int day)
{
DateTime dp = new DateTime(year, month, day);
System.DateTime dk = new System.DateTime(2012, 12, 21);
System.TimeSpan diff = dk.Subtract(dp);
return diff.Days.ToString();
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace testUILab6
{
static class Constants
{
public const int TESTS_NUMBER_PER_OPERATION = 10;
public const string PASSED_TEST_LOG = "d:\\Lab6_passed.log";
public const string FAILED_TEST_LOG = "d:\\Lab6_failed.log";
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
namespace testUILab6
{
static class Log
{
public static void writeFailedMessage(string msg)
{
FileStream aFile = new FileStream(Constants.FAILED_TEST_LOG, FileMode.Append, FileAccess.Write);
System.IO.StreamWriter file;
file = new System.IO.StreamWriter(aFile);
file.WriteLine(msg + "\n");
file.Close();
}
public static void writePassedMessage(string msg)
{
FileStream aFile = new FileStream(Constants.PASSED_TEST_LOG, FileMode.Append, FileAccess.Write);
System.IO.StreamWriter file;
file = new System.IO.StreamWriter(aFile);
file.WriteLine(msg + "\n");
file.Close();
}
private static bool isClear = false;
public static void clearLogs()
{
if (!isClear)
{
isClear = true;
System.IO.File.Create(Constants.PASSED_TEST_LOG).Close();
System.IO.File.Create(Constants.FAILED_TEST_LOG).Close();
writeFailedMessage("Test end-world program");
writePassedMessage("Test end-world program");
}
}
}
}
Рис. 2 Результати тестування
Висновок: На цій лабораторній роботі я освоїв методику тестування «чорного» ящика.