~~REVEAL~~
====== Testy jednostkowe ======
===== Unit test - co to jest? ======
**Test jednostkowy** (ang. //unit test//) to technika testowania tworzonego oprogramowania poprzez wykonywanie testów weryfikujących poprawność działania pojedynczych elementów (jednostek) programu - np. metod, obiektów.
Testowany fragment programu poddawany jest testowi, który wykonuje go i porównuje wynik z oczekiwanymi wynikami.
Źródło: [[wp>Test_jednostkowy|pl.wikipedia.org]]
==== ====
* **Test jednostkowy** \\ to fragment kodu, który sprawdza inny fragment kodu\\
* **Unit**\\ najmniejsza testowalna część aplikacji
* funkcja
* klasa
* metoda
==== Aksjomaty testowania ====
* Testy są: czasochłonne, kosztowne, ryzykowne, nużące
* Żadnego realnego oprogramowanie nie da się przetestować całkowicie.
* Test nie udowadnia braku błędów, a udowadnia jedynie to, ze ich nie znaleźliśmy
* Nie wszystkie znalezione błędy zostaną naprawione (za duży koszt, brak czasu, ryzyko naprawy)
* Paradoks pestycydów - błędy w oprogramowaniu uodparniają się na testy
* Testowanie wymaga wyobraźni i złośliwości
* Czy warto tracić czas na pisanie testów ?
==== Czym są testy jednostkowe? ====
{{:zajecia:npr:unit-test-blak-box.jpg?800|}}
==== Przykład: klasa testowana ====
public class Kalkulator
{
public int Suma(int [] x)
{
if (x == null) throw new ArgumentNullException();
int s = 0;
for(int i=1; i < x.Length ; i++) s+=x[i];
return s;
}
}
==== Przykład: klasa testowa ====
public class KalkulatorTest
{
public void SumaTest()
{
int[] x = { 1, 2, 3, 4 };
Kalkulator c = new Kalkulator();
int oczekiwanyWynik = 10;
int aktualnyWynik = c.Suma(x);
if (aktualnyWynik != oczekiwanyWynik)
throw new Exception($"Test oblany: spodziewana wartosc {oczekiwanyWynik}, aktualna wartosc {aktualnyWynik}");
}
}
==== ====
public class KalkulatorTest
{
public void SumaTestException()
{
Kalkulator c = new Kalkulator();
try {
c.Suma(null);
}
catch (ArgumentNullException) {
return;
}
throw new Exception("Test oblany");
}
}
==== Przykład: środowisko uruchomieniowe ====
class Program
{
static void Main(string[] args)
{
KalkulatorTest test = new KalkulatorTest();
test.SumaTest();
test.SumaTestException();
Console.WriteLine("Wszystkie testy zaliczone.");
}
}
==== ====
**Porażka**
| {{zajecia:npr_2015_1:kalkulator_failed2.png?1000|}} |
**Sukces**
| {{zajecia:npr_2015_1:kalkulator_passed2.png?1000|}} |
==== Przykład: VS2012 MS Test ====
[TestClass]
public class UnitTestKalkulator
{
[TestMethod]
public void TestSuma10()
{
// arrange
int[] x = { 1, 2, 3, 4 };
Kalkulator c = new Kalkulator();
int oczekiwanyWynik = 10;
//act
int aktualnyWynik = c.Suma(x);
//assert
Assert.AreEqual(oczekiwanyWynik, aktualnyWynik);
}
}
===== Test-driven development (TDD) =====
Technika zwinna (agile), wywodząca się z programowania ekstremalnego.
{{zajecia:npr_2015_1:tdd.jpg|}}
Źródło: [[wp>Test-driven_development|Test-driven development]] [[http://msdn.microsoft.com/pl-pl/library/test-driven-development.aspx|Wstęp do Test Driven Development (MSDN)]]
==== ====
* Test -> code -> refactor
* najpierw test sprawdzający dodawaną funkcjonalność (test nieudany)
* implementacja funkcjonalności (test udany)
* refaktoryzacja napisanego kodu, żeby spełniał on oczekiwane standardy.
==== Korzyści TDD i testowania ====
* Wczesne wykrywanie błędów -> mniejszy koszt
* Odporność oprogramowania na błędy regresyjne, czyli błędy powstałe w wyniku poprawek kodu
* Ułatwienie refaktoringu i zmian w kodzie pokrytym testami
* Dokumentowanie i wyjaśnianie kodu. Test wyjaśnia jaką funkcjonalność realizuje jednostka kodu i jak należy jej używać.
* Lepiej zaprojektowane interfejsy i API. Testy zmuszają do lepszego przemyślenia rozwiązań i dokładnego określenia jakie zadania dana metoda ma wykonywać.
* Testy mogą pełnić rolę specyfikacji projektowej (TDD) podobnej do UML.
* Uproszczona integracja, łatwiejsze łączenie różnych fragmentów kodu poddanych wcześniej testom. Testowanie bottom-up. Jednak zbiór UT nie zastąpi testów systemowych (które najczęściej są wykonywane ręcznie)
* Automatyzacja i powtarzalność (contignous integration): testy można uruchamiać regularnie o określonych porach lub na pewnych etapach produkcji. Oszczędność czasu w stosunku do ręcznego testowania.
* Możliwość przetestowania funkcjonalności bez uruchamiania całego oprogramowania
===== Wzorce w testowaniu jednostkowym =====
Wzorzec AAA: Arrange -> Act -> Assert
- przygotowanie środowiska testowego (Fixtures)
- ustawienie stanu wejściowego
- uruchomienie testowanych jednostek kodu (Act)
- porównanie uzyskanych wyników (Assert)
- zwolnienie zasobów (Teardown)
==== xUnit ====
* Wzorzec **xUnit** - zbiór środowisk testowych (frameworks) wywodzących swoją architekturę od SUnit (Kent Beck, 1998, Smaltalk).
* **Assercje** - funkcje weryfikujące stan i zachowanie jednostek kodu. Zazwyczaj w postaci wyrażenia logicznego, które zwraca albo prawdę albo fałsz. Niepowodzenie przerywa dany test (rzucany wyjątek).
* **Test runner** - program uruchamiający testy i raportujący wyniki
* **Test fixtures** (test context) - zbiór warunków wymaganych do przeprowadzenia testu
* **Test suites** - zbiór testów uruchamianych w jednym środowisku (fixture). Kolejność uruchomienia nie ma znaczenia.
===== Unit Test Frameworks =====
Narzędzia i biblioteki wspierające tworzenie testów, ich organizację, automatyzację wykonywania, raportowanie
* Biblioteka asercji i innych metod przydatnych przy testowaniu
* Automatyzacja w procesie wytwórczym
* Generatory testów, testy parametryczne
* Mechanizmy izolacji: fake, mock, itd
* Metryki: pokrycie kodu, ścieżek, ...
==== Środowiska testowe ====
* Java -> [[http://junit.org/|JUnit]], [[http://testng.org/doc/index.html|TestNG]]
* PHP -> [[https://phpunit.de/index.html|PHPUnit]]
* C -> [[https://github.com/siu/minunit|MinUnit]], [[http://cunit.sourceforge.net/|CUnit]], (biblioteka assert.h), [[https://cmocka.org/|cmocka]]
* C%%++%% -> [[http://freedesktop.org/wiki/Software/cppunit/|CppUnit]], [[http://www.qa-systems.com/cantata.html|Cantata]], [[https://code.google.com/p/googletest/|GoogleTest]], [[http://www.boost.org/doc/libs/1_57_0/libs/test/doc/html/index.html|Boost Test Library]]
* .Net -> Visual Studio Unit Testing Framework (MS Test, VS Prof./Ultimate), [[http://www.nunit.org/|NUnit]], [[http://xunit.github.io/|xUnit.net]]
* Python -> [[http://pyunit.sourceforge.net/|PyUnit]]
* [[http://www.mathworks.com/help/matlab/matlab-unit-test-framework.html|MATLAB Unit Testing Framework]]
* Delphi -> DUnit, Fortran -> pFUnit
Wikipedia: [[wp>List_of_unit_testing_frameworks|List of unit testing frameworks]]
==== .NET frameworks ====
* Unit Test:
* MS Test, [[http://www.nunit.org/|Nunit]], [[https://xunit.github.io/|XUnit.net]], MBUnit
* [[https://xunit.github.io/docs/comparisons.html|Porównanie]]
* Frameworki izolacji
* [[https://github.com/Moq/moq4|Moq]] The most popular and friendly mocking framework for .NET
* [[http://www.hibernatingrhinos.com/oss/rhino-mocks|Rhino mock]]
* [[http://fakeiteasy.github.io/|FakeItEasy]] The easy mocking library for .NET
* [[http://nsubstitute.github.io/|NSubstitute]] A friendly substitute for .NET mocking frameworks
* wiele innych: NMock, Typemock, ...
==== Przykład: MS Test ====
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class UnitTestKalkulator
{
private Kalkulator _calc;
[TestInitialize]
public void Init()
{
_calc = new Kalkulator();
}
[TestMethod]
public void TestSuma10()
{
int[] x = { 1, 2, 3, 4 };
int oczekiwanyWynik = 10;
int aktualnyWynik = _calc.Suma(x);
Assert.AreEqual(oczekiwanyWynik, aktualnyWynik);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void TestSumaException()
{
_calc.Suma(null);
}
}
==== Przykład: NUnit/MBUnit ====
using NUnit.Framework;
[TestFixture]
public class UnitTestKalkulator
{
private Kalkulator _calc;
[SetUp]
public void Create()
{
_calc = new Kalkulator();
}
[Test]
public void TestSuma10()
{
int[] x = { 1, 2, 3, 4 };
int oczekiwanyWynik = 10;
int aktualnyWynik = _calc.Suma(x);
Assert.AreEqual(oczekiwanyWynik, aktualnyWynik);
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void TestSumaException()
{
_calc.Suma(null);
}
}
==== Przykład: xUnit.net ====
using Xunit;
public class KalkulatorTest
{
private Kalkulator _calc;
public KalkulatorTest()
{
_calc = new Kalkulator();
}
[Fact]
public void TestSuma()
{
int[] x = { 1, 2, 3, 4 };
int oczekiwanyWynik = 10;
int aktualnyWynik = _calc.Suma(x);
Assert.Equal(oczekiwanyWynik, aktualnyWynik);
}
[Fact]
public void TestSumaException()
{
Assert.Throws(() => _calc.Suma(null));
}
}
===== Dobrze napisane testy =====
* Test wyłącznie pojedynczej jednostki, nie można pisać testów badających kilka funkcji naraz
* Testy powinny być niezależne od siebie
* Izolacja: testy niezależne od zewnętrznych zasobów, są one zastępowane makietami symulującymi ich działanie (mock, fake, stub, ...)
* Przejrzystość - testy wyjaśniają i dokumentują kod
* Szybkość - testy powinny wykonywać się szybko, to nie są testy wydajności
* Konwencje nazewnicze - nazwa testu oddaje intencje
* Pisz kod tak aby był łatwy do testowania
==== Scenariusze ====
* **Analiza ścieżek** - badanie przebiegu możliwych ścieżek od punktu początkowego do końcowego, wszystkie możliwe kombinacje rozgałęzień
* Boundary test - działania w pętli pomijane lub pętla uruchamiana raz (dla wszystkich ścieżek)
* Interior test - działania we wnętrzu pętli uważa się za przetestowane, jeśli zostały wykonane wszystkie ścieżki, które są możliwe przy dwukrotnym powtórzeniu pętli
==== ====
* **Klasy równoważności** - zbiór danych o podobnym sposobie przetwarzania. Testujemy wybrane elementy ze zbioru. na wejściu kilka elementów przetwarzanych w ten sam sposób
* klasy poprawności/niepoprawności - przypadki dla których przewidujemy poprawne/niepoprawne działanie \\ Np. ''{2, 4, 70)'' dla liczb dodatnich całkowitych
* **Wartości brzegowe** - wewnątrz, pomiędzy lub na granicy klas równoważności \\ Np. ''{-1, 0, 2, 4, 70)'' dla liczb dodatnich całkowitych
==== Zakres dostępu ====
* **metody publiczne**, testowane bez problemu
* **metody chronione** - nie można w bezpośredni sposób wywołać metody chronionej ze sterownika testu. Rozwiązaniem jest stworzenie klasy opakowującej dziedziczącej po badanej klasie i wywołanie chronionych metod w dziedziczącej klasie bazowej
* **metody prywatne** - za pomocą mechanizmu refleksji można ominąć ograniczenia i wywołać prywatną metodę.
* Czy testy jednostkowe powinny dotyczyć wyłącznie metod publicznych?
==== Testy prywatnych metod ====
Klasa {{https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.privateobject.aspx|PrivateObject}} pozwala opakować klasę zawierającą prywatne elementy do testowania. Dostęp do prywatnych elementów za pomocą metody ''Invoke()''
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestMethod]
public void TestPrywatnejMetody()
{
var obj = new PrivateObject(typeof(KlasaZPrywatnąMetodą));
var wynik = obj.Invoke("PrywatnaMetoda");
Assert.AreEqual(wynik, spodziewanyWynik);
}
===== Rodzaje testów =====
* Widoczność:
* Testy funkcjonalne (czarna skrzynka), zazwyczaj wykonywanie przez osoby nie wytwarzające kodu. \\ Testy black-box: acceptance, fuzz, smoke, performance (load, stress)
* Testy strukturalne (biała/przezroczysta skrzynka), test ma wgląd w testowany kod. \\ Testy white-box: unit, regression, integration, mutation
==== ====
* Skala:
* jednostkowe - pojedyncze funkcjonalności
* systemowe - testy aplikacji jako całość: bezpieczeństwo, wymagania niefunkcjonalne, wydajność
* integracyjne - współpraca komponentów, testuje współdziałanie klas i modułu
===== Testy jednostkowe w VS =====
{{ zajecia:npr_2015_1:unit_test_project_vs2012.png?600|}}
Szablony projektów: Unit Test Project, Visual C#, C%%++%%
* Namespace: Microsoft.VisualStudio.TestTools.UnitTesting
* Wiele rozszerzeń wspierających: Resharper, [[http://www.testdriven.net/default.aspx|TestDriven.net]]
==== Generowanie metod testowych ====
{{https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/45/92/0842.CreateUnitTest.jpg?600|}}
* Narzędzia do generowania projeku testowego i metod testowych dostępne w VS 2010 ale nie w 2012/13, powróciły w 2015
* Wtyczki np. {{https://visualstudiogallery.msdn.microsoft.com/45208924-e7b0-45df-8cff-165b505a38d7|Unit Test Generator}}
==== Menu test ====
* Uruchomienie wybranych testów
* Debbugowanie wybranych testów
* Pokrycie kodu
* Okna: Test Explorer, Code Coverage Results
{{zajecia:npr_2015_1:test_menu_vs2012.png?600|}}
==== VS Test Explorer ====
|{{zajecia:npr_2015_1:test_explorer_vs2012_2.png?350 |}} | {{zajecia:npr_2015_1:test_explorer_vs2012_3.png?350 |}} |
==== Typy asercji ====
* [[https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.assert.aspx|Assert Class (MSDN)]]
* **AreEqual** / **AreNotEqual** - porównanie warości
* **AreSame** / **AreNotSame** - porównanie referencji
* **Fail** - test jednostkowy jest niezaliczony
* **Inconclusive** - wykonanie testu jest nierozstrzygnięte
* **IsTrue** / **IsFalse** - porównanie wartości true/false
* **IsInstanceOfType** / IsNotInstanceOfType - czy dostarczona wartość nie jest podanego typu
* **IsNull** / **IsNotNull** - czy dostarczona wartość nie jest NULL
==== ====
* [[https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.collectionassert.aspx|CollectionAssert]] zestaw metod testowych dla porównywania kolekcji (np. CollectionAssert.AreEqual)
* [[https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.stringassert.aspx|StringAssert]] zestaw asercji do testowania napisów
* **Contains**
* **Matches** / **DoesNotMatch** dopasowanie wyrażenia regularnego
* **StartsWith** / **EndsWith**
==== Artybuty ===
* **TestClass** określa klasę zawierającą metody testowe
* Przygotowanie środowiska testowego (fixture/teardown) dla assembly, zestawu testów w klasie i pojedyńczej metody testowej
* **AssemblyInitialize** / **AssemblyCleanup**
* **ClassInitialize** / **ClassCleanup**
* **TestInitialize** / **TestCleanup**
* **ExpectedException** spodziewany wyjątek rzucany z testu
* **Description** opis testu
* **Ignore** test nie będzie uruchomiony
* **TestCategory** pozwala grupować testy
* **WorkItem** powiązanie testu z zadaniem (work item)
[[https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.aspx|UnitTesting Namespace]]
==== Kontekst testu ====
* Klasa [[https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.testcontext.aspx|TestContext]] przechowuje informacje, które można dostarczyć do testów
* Połaczenia do danych (data row, Data Driven Unit Test)
* Informacje o wykonywanym teście (np. ścieżki dostępu)
* Infromacje do testowania ASP.NET, np.: URL serwera Web, dostęp do obiektu Page
* Dostęp do własności ''TextContext'' (tworzona automatycznie lub należy dodać ręcznie)
* Metody oznaczone atrybutem ClassInitialize i AssemblyInitialize muszą dostarczyć obiekt TextContext w argumencie
==== Setup fixture ====
{{zajecia:npr_2015_1:context_mstest.png?800|}}
===== NUnit =====
{{ http://www.nunit.org/img/logo.gif| }}
* xUnit dla języków .Net, początkowo port z JUnit
* najpopularniejsze środowisko dla tej platformy
* wersja 3.0 przepisana od nowa, obecnie wersja 3.2
* biblioteki: framework, mock
* aplikacja uruchamiająca testy: GUI, console
* Assercja: [[http://www.nunit.org/index.php?p=classicModel&r=2.6.4|classic model]] vs. [[http://www.nunit.org/index.php?p=constraintModel&r=2.6.4|Constraint-Based Assert Model]] (NUnit 2.4)
==== GUI ====
{{zajecia:npr_2015_1:nunit_main_window.png?800|}}
==== CLI ====
{{zajecia:npr_2015_1:nunit_console.png?800|}}
===== xUnit.net =====
{{ https://raw.github.com/xunit/media/master/full-logo.png|}}
"xUnit.net is a free, open source, community-focused unit testing tool for the .NET Framework. Written by the original inventor of NUnit v2, xUnit.net is the latest technology for unit testing C#, F#, VB.NET and other .NET languages. xUnit.net works with ReSharper, CodeRush, TestDriven.NET and Xamarin."
Zaprojektowany z myślą o maksymalnym uproszczeniu testowania
==== ====
Pakiety NuGet
* ''xUnit.net'' framework
* ''xunit.runner.console'' uruchamianie testów w konsoli
* ''xunit.runner.visualstudio'' integracja z Test Explorer w VS
* Wtyczka do Resharpera: runner for xUnit.net
{{http://xunit.github.io/|xUnit website}}\\
[[https://xunit.codeplex.com/wikipage?title=Comparisons|Porównanie: NUnit vs. MS Test vs. xUnit]]
===== Data Driven Unit Test =====
* Metody testowe mogą pobierać dane do testów ze źródeł dostępnych na maszynach uruchamiających testy
[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0; Data Source=C:\Data\MathsData.sdf;", "Numbers")]
[TestMethod]
public void AddIntegers_FromDataSourceTest()
{
var target = new Maths();
// Access the data
int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
int actual = target.IntegerMethod(x, y);
Assert.AreEqual(expected, actual);
}
Źródło: [[https://msdn.microsoft.com/en-us/library/ms182527.aspx|How To: Create a Data-Driven Unit Test]]
===== Xunit.net: Teorie =====
[Theory]
[InlineData(3)]
[InlineData(5)]
[InlineData(6)]
public void MyFirstTheory(int value)
{
Assert.True(IsOdd(value));
}
bool IsOdd(int value)
{
return value % 2 == 1;
}
}
* w NUnit bardzo szeroki zakres parametryzacji za pomocą odpowiednich atrybutów
==== ====
[Theory]
[PropertyData("SplitCountData")]
public void SplitCount(string input, int expectedCount)
{
var actualCount = input.Split(' ').Count();
Assert.Equal(expectedCount, actualCount);
}
public static IEnumerableZródło: {{http://www.tomdupont.net/2012/04/xunit-theory-data-driven-unit-test.html|Theory DDT}}, Tom DuPont
===== Skip i Timeout =====
[Fact(Skip="Trait Extensibility is not working in 1654"), Category("Slow Test")]
public void LongTest()
{
Thread.Sleep(500);
}
[Fact, Trait("Category", "Supa")]
public void LongTest2()
{
Assert.True(true);
}
[Fact(Timeout=50)]
public void TestThatRunsTooLong()
{
System.Threading.Thread.Sleep(250);
}
===== AutoFixture =====
* [[https://github.com/AutoFixture|AutoFixture]] is an open source library for .NET designed to minimize the 'Arrange' phase.
* Tworzenie anonimowych zmiennych
[TestMethod]
public void IntroductoryTest()
{
// Fixture setup
Fixture fixture = new Fixture();
int expectedNumber = fixture.Create();
MyClass sut = fixture.Create();
// Exercise system
int result = sut.Echo(expectedNumber);
// Verify outcome
Assert.AreEqual(expectedNumber, result, "Echo");
// Teardown
}
===== AutoData =====
[Theory, AutoData]
public void IntroductoryTest(int expectedNumber, MyClass sut)
{
int result = sut.Echo(expectedNumber);
Assert.Equal(expectedNumber, result);
}
===== Techniki izolacji =====
* Izolacja: test jednostkowy nie może wykraczać poza zakres testowanej jednostki kodu
* Testy wykraczające poza ten zakres
* to testy integracji
* są wolniejsze
* mogą się nie powieść ze względu na błędy w tych zewnętrznych modułach
==== ====
* Separacja od zewnętrznych procesów wymusza tworzenie kodu bardziej modularnego, prostszego w testowaniu i ponownym wykorzystaniu (dependency inversion principle, warstwy abstrakcji pomiędzy komunikującymi się modułami)
* Obiekty imitujące dostęp do zewnętrznych procesów: Fake, Mock, Stub, Spies, Exstract and Override, ....
* Frameworks .NET: [[https://github.com/Moq/moq4|Moq]], [[https://github.com/FakeItEasy/FakeItEasy|FakeItEasy]], [[http://www.hibernatingrhinos.com/oss/rhino-mocks|RhinoMocks]], [[http://nsubstitute.github.io/|NSubstitute]], ...
==== Mock objects ====
* **Atrapy obiektów** (mock objects) - symulowane obiekty naśladujące w kontrolowany sposób zachowanie rzeczywistych obiektów.
* symulowanie połączenia do bazy danych
* symulowanie z zasobami, które mogą być niedostępne, np.: połączenia z serwisami sieciowymi
* symulowanie obiektów, które jeszcze nie powstały
* symulowanie w celu zwiększenia szybkości testów
* Obiekty Fake i Mock realizują wzorzec [[wp>Dependency injection]]
==== Mocks, fakes i inne ====
* Wzorce [[http://xunitpatterns.com/Test%20Double%20Patterns.html|Test Doubles]], różnią się przede wszystkim sposobami wykorzystania
* Dummy - zwraca pewną domyślna wartość
* Stub - pozwala zwracać więcej wartości, bardziej złożona logika wewnętrzna
* Spy
* Fake - metody tego obiektu dostarczają niezbędne dane do działania testu
* Mock - to obiekt Fake zawierający w sobie assercję testową lecz nie tylko
* Inne: strict mock, dynamic mock, loose mock, substitute, mole.
==== ====
* "no need to learn what’s the theoretical difference between a mock, a stub, a fake, a dynamic mock, etc." (Moq project)
* "all fake objects are just that — fakes. Usage determines whether they're mocks or stubs." (FakeItEasy)
==== Dependency Injection ====
* **Wstrzykiwanie zależności** - wzorzec projektowy i wzorzec architektury oprogramowania polegający na usuwaniu bezpośrednich zależności pomiędzy komponentami na rzecz architektury typu plug-in. Szczególna realizacja paradygmatu z odwróceniem sterowania sterowania (Inversion of Control, IoC). \\
* Wstrzyknięcie przez : konstruktor, settery, interfejs
* DI pozwala wstrzyknąć do obiektów testowanych zależności od obiektów-zaślepek (atrap, mock objects), w ten sposób TTD narzuca stosowanie dobrych praktyk
==== Przykład DI ====
public class Client {
// Wewnętrzna referencja do usługi
private Service service;
// Wstrzyknięcie przez konstruktor
Client(Service service) {
this.service = service;
}
// Metoda w której klient korzysta z usługi (chcemy wyizolować to działanie)
public double Compute(double x) {
return x * service.GetSomeValue();
}
}
==== Przykład: biblioteka Moq ====
// Tworzymy obiekt atrapę
var mockClient = new Mock();
// Ustawiamy metodę aby zwracała symulowaną wartość
mockClient.Setup(mock => mock.GetSomeValue()).Returns(1);
// Wstrzykujemy atrapę do obiektu który będzie testowany
var client = new Clent(mockClient.Object);
// Test
Assert.IsEqual(client.Compute(2), oczekiwanaWartosc);
===== Pokrycie kodu (code coverage) =====
Pokrycie kodu mierzy, ile procent kodu zostało sprawdzone przez testy jednostkowego. Przyjmuje się, że dobrze napisane testy powinny mieć pokrycie rzędu przynajmniej 70% .
==== Metryki pokrycia ====
* **Pokrycie wyrażeń** (ang. statement coverage) – metryka stanowi iloraz liczby linii kodu wywołanych przez testy jednostkowe oraz całkowitej liczby linii kodu. Jeśli jakaś linia nie została pokryta, istnieje zatem ryzyko wystąpienia w niej błędu.
* **Pokrycie rozgałęzień** (ang. branch coverage) – pokrycie nie bada linii kodu, a rozgałęzienia zrealizowane za pomocą np. instrukcji if (czy test przetestował ścieżki true i false)
* **Pokrycie ścieżki** (path coverage) - ścieżka to unikatowa sekwencja rozgałęźień w funkcji
* inne: pokrycie funkcji/metod, klas (metod w klasach)/ patch coverage
==== Code coverage w VS ====
* VS2010: Test -> Edit Test Settings -> Local
* Data and Diagnostic -> Code Coverag
* Configure - wybór źródła danych do analizy
* VS2012: Test -> Analyze Code Coverage -> All Tests/ Selected Test
| {{zajecia:npr_2015_1:code_coverage_results_vs2012.png?600|}} | {{zajecia:npr_2015_1:code_coverage_coloring_vs2012.png?500|}} |
==== Testowanie mutacyjne ====
**Testowanie mutacyjne** – technika automatycznego badania, na ile dokładnie testy jednostkowe sprawdzają kod programu. Polega na automatycznym wielokrotnym wprowadzaniu małych losowych błędów w programie i uruchamianiu za każdym razem testów jednostkowych, które powinny te błędy wykryć. Testowanie mutacyjne wymaga dużej mocy obliczeniowej i dlatego dopiero od niedawna próbuje się je wykorzystywać w praktyce.
Źródło: [[wp>Testowanie_mutacyjne|pl.wikipedia.pl]]
===== Testy w Resharper =====
* Środowisko uruchomieniowe : MS Test, NUnit, xUnit.net (wtyczka do R#)
{{zajecia:npr_2015_1:resharper_test_menu.png|}}
==== Unit test explorer ====
{{zajecia:npr_2015_1:resharper_test_explorer.png|}}
==== Unit test sesions ====
{{zajecia:npr_2015_1:resharper_sessions.png|}}
==== dotTrace i DotCover ====
* profilowanie testów dotTrace Performance
* Pokrycie kodu DotCover
{{https://www.jetbrains.com/dotcover/img/30/report.png?600|}}
===== Integracja z TFS =====
* Uruchamianie testów w po stronie serwera - konfugiracja buldów
* Raporty z wykonanych testów
{{zajecia:npr:tfs-build-def-with-test2.png?800|}}
===== Podsumowanie =====
* Testy jednostkowe nie dają 100% pewności, że kod nie posiada błędów
* Należy stosować je wraz z innymi testami: testy integracyjne, systemowe, obciążeniowe itd.
* Nie wszystko da się przetestować: zachowania niedeterministyczne, aplikacje wielowątkowe
* Na jedną linię kodu może przypadać nawet do 5 linii kodu testu - nie zawsze jest to opłacalne
* Kod testujący jak każdy inny kod nie jest odporny na błędy
===== Bibliografia =====
* [[http://msdn.microsoft.com/pl-pl/library/testy-jednostkowe-w-visual-studio.aspx|Testy jednostkowe w Visual Studio]]
* Jeremy Lindblom, [[https://speakerdeck.com/jeremeamia/unit-testing|Unit Testing]]
* [[wp>List_of_unit_testing_frameworks|List of unit testing frameworks]]
* [[http://xunitpatterns.com/|XUnit Test Patterns]], Gerard Meszaros
* [[http://www.codethinked.com/beginning-mocking-with-moq-3-part-1|Beginning Mocking With Moq 3]] by Justin Etheredge ([[http://www.codethinked.com/beginning-mocking-with-moq-3-part-2|part 2]], [[http://www.codethinked.com/beginning-mocking-with-moq-3-part-3|part 3]], [[http://www.codethinked.com/beginning-mocking-with-moq-3-part-4|part 4]])
* {{http://video.ch9.ms/sessions/teched/eu/2014/Labs/DEV-H211.pdf|Introduction to unit testing}}
* [[https://msdn.microsoft.com/en-us/library/dd264975.aspx|Verifying Code by Using Unit Tests]] (MSDN)