Inżynieria Oprogramowania
Testy Jednostkowe
Wydział Mechaniczny Politechniki Krakowskiej
Instytut Informatyki Stosowanej
Rok Akademicki 2011/2012, Grupa 13K3
Rafał Polak 13k3 07-11-2011 |
Sprawozdanie z Laboratorium Komputerowego nr.2 Testy Jednostkowe |
---|
Ogólne informacje
TESTY JEDNOSTKOWE - to w programowaniu metoda testowania tworzonego oprogramowania poprzez wykonywanie testów weryfikujących poprawność działania pojedynczych elementów (jednostek) programu - np. metod lub obiektów w programowaniu obiektowym lub procedur w programowaniu proceduralnym. Testowany fragment programu poddawany jest testowi, który wykonuje go i porównuje wynik (np. zwrócone wartości, stan obiektu, wyrzucone wyjątki) z oczekiwanymi wynikami - tak pozytywnymi, jak i negatywnymi (niepowodzenie działania kodu w określonych sytuacjach również może podlegać testowaniu).
Zaletą testów jednostkowych jest możliwość wykonywania na bieżąco w pełni zautomatyzowanych testów na modyfikowanych elementach programu, co umożliwia często wychwycenie błędu natychmiast po jego pojawieniu się i szybką jego lokalizację zanim dojdzie do wprowadzenia błędnego fragmentu do programu. Testy jednostkowe są również formą specyfikacji. Z powyższych powodów są szczególnie popularne w programowaniu ekstremalnym.
Cel Ćwiczenia
Celem ćwiczenia jest zapoznanie się z możliwością tworzenia testów do pisanych programów w programie Microsoft Visual Studio w języku C#. Stworzone poniżej testy są prostym przykładem w celu zweryfikowania poprawności działania pojedynczego elementu i późniejszego ich odwzorowywania w pisanych projektach.
Realizacja Testów
Utworzenie projektu w Microsoft VS o nazwie Kalkulator
Implementacja Przykładowej Klasy „Kalkulator” w projekcie realizującej funkcjonalność dodawania i dzielenia w raz z obsługą wyjątków.
Utworzono nową klasę MyStringMath zawierającą dwie metody statyczne Add i Devide kolejno definiujące dodawanie oraz dzielenie.
namespace Kalkulator
{
public static class MyStringMath
{
public static string Add(string valt1, string valt2)
{
double value1;
double value2;
if (!double.TryParse(valt1, out value1))
return "The 1st input value is invalid.";
if (!double.TryParse(valt2, out value2))
return "The 2nd input value is invalid.";
return (value1 + value2).ToString();
}
public static string Devide(string valt1, string valt2)
{
double value1;
double value2;
if (!double.TryParse(valt1, out value1))
return "The 1st input value is invalid.";
if (!double.TryParse(valt2, out value2))
return "The 2nd input value is invalid.";
if (value2 == 0) return "Nie dziel przez zero";
return (value1 / value2).ToString();
}
public static string error()
{
throw (new DivideByZeroException());
}
}
}
Formatka Programu zawiera dwa buttony odpowiadające za dodawanie lub dzielenie oraz dwa textboxy pozwalające wpisanie dwóch liczb, na których wykonywane będą działania matematyczne.
Oprogramowanie Formatki zawiera wywołanie metod zawartych w ww klasie po odpowiednim wciśnięciu przycisku zawartego w formatce:
namespace Kalkulator
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void Dodawanie_Click(object sender, EventArgs e)
{
wynik.Text = MyStringMath.Add(textBox1.Text, textBox2.Text);
}
private void Dzielenie_Click(object sender, EventArgs e)
{
wynik.Text = MyStringMath.Devide(textBox1.Text, textBox2.Text);
}
}
}
Stworzenie projektu Testów w solucji, który został połączony relacją z projektem głównym. Aby dodać dodatkowy projekt do solucji, należy na niej otworzyć menu kontekstowe i wybrać „Add >> New Project”
Do głównego kodu należy dopisać linię, która zapewnia użycie przestrzeni nazw z projektu głównego:
using Kalkulator;
Implementacja Klasy KalkulatorTest
namespace KalkulatorTest
{
/// <summary>
/// Summary description for UnitTest1
/// </summary>
[TestClass]
public class UnitTest1
{
public UnitTest1()
{
//
// TODO: Add constructor logic here
//
}
private TestContext testContextInstance;
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod] //Obowiązkowe przy opisaniu klasy testów
public void TestMethod1()
{
//string value1 = "2";
//string value2 = "1";
}
[TestMethod] //Obowiązkowe przy opisaniu klasy testów
public void CanAddToValues()
{
string value1 = "2";
string value2 = "1";
string result = MyStringMath.Add(value1, value2);
Assert.AreEqual("3", result);
}
[TestMethod] //Obowiązkowe przy opisaniu klasy testów
public void TestMethod2()
{
string value1 = "10";
string value2 = "2";
string result = MyStringMath.Devide(value1, value2);
Assert.AreEqual("5", result);
}
[TestMethod] //Obowiązkowe przy opisaniu klasy testów
[ExpectedException(typeof(DivideByZeroException))] //Niezbędne jest określenie typu wyjątku rzucanego przez klase
public void TestError()
{
string value1 = "1";
string value2 = "0";
string result = MyStringMath.div(value1, value2);
Assert.AreEqual("Nie dziel przez ZERO!!!", result);
}
}
}
Rezultat i Wniosek
Po stworzeniu programu należy przejść pozytywnie przez wszystkie testy stworzone w projekcie. W miarę możliwości podczas tworzenia projektu, testy należy zawrzeć w każdym miejscu, gdzie istnieje możliwość popełnienia błędu.
W celu przejścia przez testy klikamy przycisk „Debug All Test In Solution” lub za pomocą sekwencji przycisków CTRL+R. Wynik przejścia przez testy powinien odpowiadać poniższemu:
Przejście przez wszystkie wyjątki zostało wykonane bezbłędnie
Jeżeli natomiast wywołanie testu nie przejdzie, zwróci Failed należy wnieść poprawki w kod programu, zakładając, iż sam test został poprawnie przeprowadzony. Przykładem niepoprawnego wykonania testu jest:
public void CanAddToValues()
{
string value1 = "2";
string value2 = "1";
string result = MyStringMath.Add(value1, value2);
Assert.AreEqual("2", result);
}
Test nie przeszedł ponieważ oczekiwano wartości 2 w wyniku, natomiast suma (1+2) jest równa 3.
Po odpowiednim przeliczeniu poprawności wyniku test sprawdzi wynik zwracany przez program z wynikiem, przez nas wprowadzonym i jeżeli poprawnie dodamy dwie liczby test przejdzie.
Podobnie jest z testem sprawdzającym dzielenie przez zero. Gdy zrobimy go na zmiennych double test przejdzie, ponieważ nie wie jakiego typu wyjątku się spodziewamy.
Aby test działał poprawnie należy zdefiniować rodzaj wyjątku dodając linię przed wyjątkiem
ExpectedException (typeof(DivideByZeroException))
Wynikiem działania programu są niżej wymienione zrzuty ekranu przedstawiające funkcjonalności.
Wynik działania dodawania, bez wyrzucania wyjątku
Wynik działania dzielenia, bez wyrzucania wyjątku
Rzut wyjątkiem przy próbie dzielenia przez zero
Podsumowując działanie unit testów ściśle związane jest z metodą wprowadzania zmiennych i określaniem jakiego wyjątku można się spodziewać w danym fragmencie kodu. Poprawne przeprowadzenie testów zapobiega późniejszym problemom w projekcie i szybsze naprawianie bieżąco pisanego kodu.