Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Krzysztof Manuszewski

Podobne prezentacje


Prezentacja na temat: "Krzysztof Manuszewski"— Zapis prezentacji:

1 Krzysztof Manuszewski
ZTO Testy jednostkowe Krzysztof Manuszewski

2 W czym pomagają testy jednostkowe?
Ułatwiają znajdowanie błedów Ułatwiają zrozumienie kodu Ułatwiają utrzymanie kodu Ułatwiają pisanie kodu

3 Testy jednostkowe Testy weryfikują na bieżąco konkretne aspekty zachowania klas. Złamanie założeń powoduje załamanie konkretnych testów. Przy dodawaniu/zmianach funkcjonalności testy chronią przed zepsuciem już zaimplemen-towanych funkcji. Przy TDD/BDD pokazują czy osiągnęliśmy założony cel. Stanowią dokumentację i zarazem przykłady użycia Kod powinień być pisany prosto. Działający kod można i należy udoskonalać. Aby to było bezpieczne potrzebne są testy.

4 Test jednostkowy Jest to automatyczny fragment kodu uruchamiający i weryfikujący poprawność wykonania pewnego aspektu kodu produkcyjnego Testy są pisane z wykorzystaniem framework-ów. Dzięki temu mogą być stworzone i uruchamiane szybko i łatwo: .Net: np. NUnit, MSTest, MBUnit, Xunit, MSpec Testy mogą być uruchamiane pojedynczo lub masowo przez każdego członka zespołu lub w procesie automatycznego budowania (CI/CD) Testy są częścią projektu (trzeba o nie dbać!) ale nie są dostarczane do klientów

5 Test jednostkowy – elementy (1)
bool IsLoginOk(string user, string password); [TestFixture] Class TestClass { [Test] public void TestLogin1() { LoginComponent sut = new AuthComponent (); bool result = sut.IsLoginOk("user","password"); Assert.IsFalse(result, "invalid user/password shouldn't be accepted"); }

6 NUnit Dedykowane GUI Wtyczki do VS: R# TestDriven.Net

7 Części testu czyli AAA Arrange – utwórz SUT/subject, zainicjuj środowisko Act – wykonaj test Assert – sprawdź wyniki Przygotowanie może być realizowane Np. w SetUp

8 Test jednostkowy – wspólny set-up
[TestFixture] Class TestClass { AuthComponent sut; [SetUp] public void Init() { sut = new AuthComponent (); } public void TestLogin1() { … } [Test] public void TestLogin2() { var result = sut.IsLoginOk("Iksinski","realPassword"); Assert.IsTrue(result,"valid user/password should be acceprted"); }

9 Gdzie leży problem czy nazwy byly dobre? wspólny setup, co to znaczy?
skad wiadomo, że "Iksinski" jest poprawnym uzytkownikiem

10 Gdzie leży problem Kod produkcyjny powinien być dobrze przetestowany.
Test powinien się jasno nazywać i powinien testować pojedynczy aspekt działania Nie należy pisać niepotrzebnych testów Kod testów powinien zawierać jak najmniej powtórzeń Testy powinny być zrozumiałe a ich kod powinien być dobrej jakości

11 Dlaczego Kod produkcyjny powinien być dobrze przetestowany ...
Jeśli testy maja "dziury" – nie można im w pełni zaufać (czy można ufać troche?) To znaczy nie powinna udać się żadna istotna zmiana kodu produkcyjnego bez zepsucia jednego lub wielu testów Jeśli znajdujemy problem w kodzie produkcyjnym – dodajemy nowy test/testy a potem poprawiamy kod. Testy powinny być stabilne i niezależne. Fałszywe alarmy to duży problem!

12 Co testować Logikę. Instrukcje warunkowe Pętle Testowanie prostych properties/funkcji mija się z celem. Publiczny interfejs. Jeżeli metody prywatne zawierają nietrywialna logikę może to znak, że klasa powinna zostać zrefaktoryzowana. Np. samochód vs. silnik

13 Życie prywatne klasy Jeżeli testy wymagają dostępu do niepublicznych składników np. dla weryfikacji stanu (niepokojące...) : Nie należy rozhermetyzować klasy Można dodać klasę potomną dla potrzeb testu (składniki protected) Można rozważyć użycie refleksji…

14 Dlaczego Test powinien się jasno nazywać
Testy powinny wskazywać na konkretne problemy - dobre nazwy zwalniają ze szczegółowych komunikatów przy asercjach Niejasna nazwa przy wielu testach (np. tysiącach) powoduje, że musimy przebijać się przez logi, debugować/analizować kod Nazwa testu powinna dobrze lokalizować błąd. Najlepiej bez debugowania, bez analizy komunikatów Jeśli mamy problem z nazwą testu – jest bardzo prawdopodobne, że próbujemy przetestować zbyt wiele rzeczy na raz

15 Jasno czyli jak ? Czy porzednie testy miały dobra nazwę? Konwencje
LoginComponent_InvalidUser_ShuldThrowException WhenUserIsInvalid. IsLoginOk_ShouldThrowException Trudno nazwać test, który dotyczy wielu aspektów zachowania klasy Przy dobrej nazwie komunikatów może nie być

16 Dlaczego Test powinien … testować pojedynczy aspekt działania kodu
to znaczy poszczególne zmiany w kodzie produkcyjnym powinny "zapalać" jak najmniej testów Test, który testuje wiele rzeczy bedzie częściej "padać" przy zmianach kodu Test, który testuje wiele rzeczy jest skomplikowany Test, który testuje wiele rzeczy niewiele mówi w momencie upadku Aspekt niekoniecznie znaczy jedna klasa, funkcja itd.

17 Dlaczego Nie należy pisać niepotrzebnych testów
Pisanie testów kosztuje. Utrzymanie testów kosztuje jeszcze więcej. Jeśli 2 testy padają zawsze razem – jeden jest niepotrzebny – oba testują to samo Jeśli test pada zawsze z jednym spośród kilku innych – jest niepotrzebny Pokusa by testować to co testują inne testy prowadzi do tego, że testy padają częściej niż by mogły

18 Dlaczego Kod testów powinien zawierać jak najmniej powtórzeń ...
Duplikacja to ZŁO : Utrudniona poprawa testów/rozwój kodu (Rak testów) Jak unikać powtórzeń wspólny Setup (ale nie tak ze jest jedne setup dla wszystkich testów – podejscie wolne i niejasne) Hierarchia klas, Buildery danych testowych własne asserty Własne asercje, Metody weryfikujące Testy sterowane danymi Uwaga: wywołanie konstruktora (np. new osoba(...)) - to też powtórzenie!

19 Dlaczego Testy powinny być zrozumiałe... Test stanowi dokumentację
Przed zmianą funkcjonalności (albo zaraz po) należy zmienić test(y) Boje się zmieniać tego czego nie rozumiem!!! Testy nie powinny zawierać logiki – jak testować testy? Jeśli test zawiera logikę należy ją wydzielić (np. do funkcji). Takie funkcje mozna przetestować.

20 Dlaczego … kod testów powinien być dobrej jakości
co to znaczy dobra jakość? Krótki, zrozumiały kod, dobre nazewnictwo, Brak powtórzeń Testy zmieniają się równie często co kod (albo częściej) Testy nie trafiają do klienta ale są podstawą pracy programisty Błędy w testach sa równie złe co w kodzie. Powolna praca z testami jest równie zła co powolna praca z kodem. Testy można i należy refaktoryzować

21 Jak pisać testy - podsumowanie
Dobrej jakości testy nie wymagają intensywnej pielęgnacji. Projekty (agile) raczej padają z powodu złej jakości testów a nie nie z powodu ich braku Kod nie może zawierać "hack-ów" (if test ....) Test, który zawsze działa – nic nie testuje. Zawsze należy sprawdzić czy są przypadki gdy test zawodzi Typowy kod jest trudny do testowania. Testy dla istniejącego (i stabilnego kodu) mają umiarkowany sens: poznawanie kodu przygotowanie do zmiany kodu

22 Testy sterowane danymi
Pojedynczy kod testu (parametryzowany) Test jest uruchamiany wielokrotnie dla różnych zestawów danych Dane dla testu mogą być umieszczone w kodzie lub brane z zewnętrznych źródeł (txt, xml, csv, xls, mdb itd.) UWAGA: to nie jest "silver bullet" Słaba diagnostyka Tendencja do testowania kombinatorycznego

23 Testy sterowane danymi MSTest
[TestClass] public class TestClass { [TestMethod] [DeploymentItem("Cipher.MDB")] [DataSource("System.Data.OleDb", "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\" Cipher.MDB \"", "Employees", DataAccessMethod.Sequential)] public void TestEncryptionMethod() var key = TestContext.DataRow["key"]; var phrase = TestContext.DataRow["text"]; var sut = new Cryptography(key); var encrypted = sut.EncryptText(phase); assert.AreEqal(encrypted, TestContext.DataRow["encryptedText"] ); }

24 Testy sterowane danymi NUnit
[TestCase(2.5d, 2d, Result=1.25d)] [TestCase(-2.5d, 1d, Result = -2.5d)] public double ValidateDivision(double numerator, double denominator) { var myClass = new MyClass(); return myClass.Divide(numerator,denominator); }

25 Dlaczego TDD/BDD Red/Green/Refactor
Test = specyfikacja funkcjonalności Test = projekt rozwiązania (wywołanie) Pewność, że test działa Lepsze wyczucie za jaki aspekt odpowiada konkretny test Test powinien weryfikować dany aspekt Test powinien dokumentować dany aspekt (być specyfikacją) Problem: należy sie skupić na jednym aspekcie. Nie próbować implementować za dużo.

26 Jak używać testów? Są często (stale?) uruchamiane podczas kodowania
Są cyklicznie uruchamiane na serwerze buildów. Testy odzwierciedlają kontrakt pomiędzy użytkownikiem i dostarczycielem funkcjonalności Testy stanowią wyznacznik jakości architektury kodu -> testy mogą służyć tworzeniu dobrej architektury (TDD/BDD) Należy "słuchać" co mówią testy – jak łatwo/trudno je utrzymać.

27 Filozofia: definiowania testów
Jeden po drugim: przyrostowy development Wszystkie na raz: np definiujemy pojedyncze user story jako sekwencje testów Wady drugiego podejścia Nie wiadomo, który test za co odpowiada Brak wyczucia nt. ew. nadmiarowości testów Masowe podejście – łatwiej coś przeoczyć

28 Struktura kodu testowego
Testy można grupować w klasy - test fixtures (np. dla wspólnej inicjalizacjj SUT) Jedna klasa testowa nie musi (i zwykle nie odpowiada) jednej klasie testowanej raczej konkretnym danym testowym Czesto (zwykle?) dla pojedynczej funkcji piszemy kilka testów: jeden test - jeden aspekt działania funkcji (jeden asert logiczny)

29 Filozofia: budowa test fixture
Up front Łatwo o błedny projekt Niepotrzebny kod – YAGNI (You aren't gonna need it) Test po teście: Nie należy pisać kodu na wyrost Przyrostowy development Fresh Fixture

30 Inicjalizacja Sut SUT = system under test
Instancja SUT nie powinna być współdzielona pomiędzy wieloma testami Wrażliwość na kolejnośc wykonania Trudna diagnostyka SUT może być kazdorazowo inicjowany w teście lub inicjowany w SetUp. To drugie podejście ułatwia unikanie redundancji. Ale należy unikać wielkich, nie do końca wspólnych setupów.

31 Testowanie zachowania
Izolacja klas

32 Filozofia: co testować
Stan obiektów Zachowanie obiektów: Testujemy wołania innych funkcji/obiektów Zasada proś [o przysługę] nie pytaj [o stan] Jak trzeba mieszamy podejścia Przede wszystkim należy testować to co ma wartość z punktu widzenia klienta

33 Zachowanie ... public class InvoiceProcessor { private ISender sender; private ILogger logger; public InvoiceProcessor(ISender nSender, ILogger nLogger) { sender = newSender; logger = nLogger; } public bool Process(...) { logger.Log("start"); if (...) { ... bool ret = sender.Send(invoice); ... } } var procesor = new InvoiceProcesor(new InvoiceSender(...), new Logger()); TEST

34 ...to nie stan Problem 1: ignorujemy zachowanie kodu logger.Log() Problem 2: nie mamy skonfigurowanego sendera –czy sender.Send() zwrócil true czy false Problem 3: czy sender zostal wywolany i z jakimi paramerami

35 Wymagane zastępstwo Problem 1: Problem 2:
public class FakeLogger : Ilogger { public void Log(string msg) {} } Problem 2: public class FakeSender : ISender { public bool Ret = true; public bool Send (obiect toSend) { return Ret; }

36 Wymagane zastępstwo Problem 3:
public class FakeSenderValidator : ISender { public bool Ret = true; public bool SendWasCalled = false; public object SendArgument; public bool Send (object toSend) { SendWasCalled = true; SendArgument = toSend; return Ret; } }

37 Bez nowych klas... Stub: – obiekt kreowany dynamicznie – akceptujący wołania i ew. zwracający konkretne wartości Mock: – obiekt kreowany dynamicznie – z mozliwością weryfikacji konkretnych zachowań Mocking frameworks: Nmock, Moq – stosunkowo proste Rhino mock – bardzo zaawansowany TypeMock – jeszcze bardziej zaawansowany ale ... komercyjny

38 Przykład 1, 2 [Test] public void Process_whenSendingSuccesful_...() { //Problem1: var logger = MockRepository.GenerateStub<ILogger>(); //Problem2: var sender = MockRepository.GenerateStub<ISender>(); sender. Stub(s => s.Send(null)). IgnoreArguments(). Return(true); InvoiceProcessor sut = new InvoiceProcessor(sender, logger); var result = Sut.Process(....); ... }

39 Przykład 3 [Test] public void Process_whenSendingSuccesful_...() { var logger = MockRepository.GenerateStub<ILogger>(); var sender = MockRepository.GenerateStub<ISender>(); sender. Stub(s => s.Send(null)). IgnoreArguments(). Return(true); Invoice invoice = ...; InvoiceProcessor sut = new InvoiceProcessor(sender, logger); var result = Sut.Process(invoice); ... //Problem 3: sender.AssertWasCalled( s => s.Send(invoice) ); }

40 Problemy przy testach Niejawne wejście - środo-wisko zewnetrzne np.:
Pojawienie się pliku Brak pamieci Pojawienie się procesu Otrzymanie maila Przyciśnięcie przycisku w GUI Zmiana danych w bazie Niejawne wyjście – efekt działania kodu np.: Skasowanie pliku Zabicie procesu Wysłanie maila Wyświetlenie czegoś na ekranie, zmiana stanu elementow GUI Zapis danych do bazy

41 Trudny test public void StartMonitoring(...) { ... if (System.IO.File.Exists("myFile")) //send } Niejawne wejście Niejawne wyjście

42 Dedykowana Podklasa class SystemMonitor{ public void StartMonitoring(...) { ... if (FileExists("myFile")) Send (...) } protected virtual bool FileExists(string fileName) { return System.IO.File.Exists(fileName); } protected virtual bool Send (...) { //send

43 Dedykowana Podklasa class SystemMonitorTestSubclas : SystemMonitor { public bool fileExists = true; public bool Sent = false; public virtual void Send (...) { Sent = true; } public virtual bool FileExists (...) { return fileExists; } } var sut = new SystemMonitorTestSubclas (); A z mockiem: var sut = MockRepository.GeneratePartialMock< SystemMonitor >(); sut.Stub(s => s.FileExist (null)).IgnoreArguments().Return(true); sut.Stub(s => s.Send (null)).IgnoreArguments(); .... sut.StartMonitoring(); sut.AssertWasCalled( s => s.Send (null), options => IgnoreArguments());

44 Obiekty izolujący class SystemMonitor { private FileSystemProvider fileSystem; private MailSenser sender; public void StartMonitoring(...) { ... while(...) { ... if (fileSystem.FileExists("myFile")) sender.Send (...) }

45 Narzędzia Frameworki UT Mocking tools: Kontenery IOC Automocking
xUnit MSpec Mocking tools: TypeMock Isolator RhinoMock Mocq Microsoft Fakes Kontenery IOC Automocking Specyfikacja wymagań SpecFlow

46 Warto poczytać, popatrzeć ...
Andy Hunt, Dave Thomas "Pragmatic Unit Testing in C# with Nunit" Roy Osherove "The Art of Unit Testing with Examples in .NET" Gerard Meszaros "xUnit Test Patterns" Prezenacje wideo: "Roy Osherove - Understanding Test Driven Development.wmv" "Roy Osherove - Unit Testing Best Practices.wmv" "Roy Osherove - Test Driven Development, Using Mock Objects.wmv"


Pobierz ppt "Krzysztof Manuszewski"

Podobne prezentacje


Reklamy Google