Adam Tibi

Test-Driven Development w C# i .NET

Tworzenie wysokiej jakości kodu w architekturze DDD za pomocą znanych narzędzi i bibliotek

Przekład: Robert Górczyński

Tytuł oryginału: Pragmatic Test-Driven Development in C# and .NET: Write loosely coupled, documented, and high-quality code with DDD using familiar tools and libraries

Tłumaczenie: Robert Górczyński

ISBN: 978-83-289-1694-4

Copyright © Packt Publishing 2022. First published in the English language under the title ‘Pragmatic Test-Driven Development in C# .NET – (9781803230191)’

Polish edition copyright © 2025 by Helion S.A.

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher.

Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji.

Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli.

Autor oraz wydawca dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz wydawca nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce.

Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
https://helion.pl/user/opinie/tedrde_ebook
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.

Helion S.A.
ul. Kościuszki 1c, 44-100 Gliwice
tel. 32 230 98 63
e-mail: helion@helion.pl
WWW: https://helion.pl (księgarnia internetowa, katalog książek)

- Kup w wersji papierowej

- Poleć książkę na Facebook.com

- Oceń książkę

- Księgarnia internetowa

- Lubię to! » Nasza społeczność

Chciałbym podziękować mojej drogiej partnerce Elvirze za utrzymywanie mnie na właściwej drodze, za okazane wsparcie oraz za słowa zachęty podczas pracy nad tą książką. Bez Twojej pomocy to przedsięwzięcie by się nie udało. Dziękuję również moim kochanym synom Robinowi i Charliemu za wyrozumiałość, gdy byłem zajęty pracą.

Chciałbym też podziękować osobie, które we mnie uwierzyła i mnie zachwyciła, mojemu nauczycielowi dr. Jaberowi M. Jaberowi, a także mojemu ulubionemu autorowi pozycji technicznych Charlesowi Petzoldowi, który zainspirował mnie swoim stylem pisania i prostotą.

Podziękowania kieruję do redaktorów, którymi są Nithya Sadanandan i Yashi Gupta, za ich cierpliwość do mnie oraz za przekazane uwagi.

Na koniec chciałbym podziękować korektorowi technicznemu Ahmedowi Ilyasowi za przekazane uwagi i życzliwe opinie co do każdego rozdziału, dzięki którym mogłem z pasją kontynuować pracę.

O autorze

Adam Tibi to mieszkający w Londynie konsultant oprogramowania z przeszło 20-letnim doświadczeniem w pracy z technologiami .NET, Python, stosem Microsoft i Azure. Zajmował się mentoringiem zespołów, projektowaniem architektury, promowaniem podejścia zwinnego, dobrymi praktykami w zakresie opracowywania oprogramowania oraz tworzeniem kodu. Adam pracował dla różnych firm, m.in. Shell, Lloyds Bank, Lloyd’s of London, Willis Towers Watson oraz licznych startupów. Jako konsultant z obszernym portfolio, zdobył solidną wiedzę na temat zawiłości programowania sterowanego testami, którą to podzielił się w niniejszej książce.

O korektorze merytorycznym

Ahmed Ilyas ma 18-letnie doświadczenie w tworzeniu oprogramowania.

Po opuszczeniu firmy Microsoft zdecydował się założyć własną firmę konsultingową, która oferuje najlepsze rozwiązania dla wielu branż i pomaga znajdować odpowiedzi na rzeczywiste problemy. Do budowania technologii — by zapewnić klientom najlepsze praktyki i wzorce oraz najlepsze oprogramowanie — używa wyłącznie produktów firmy Microsoft. To umożliwia długotrwałą stabilizację i zgodność w nieustannie zmieniającej się branży oprogramowania, a ponadto pomaga programistom z całego świata w ulepszaniu technologii i przesuwaniu ich granic.

Był trzykrotnie nagrodzony przez Microsoft tytułem MVP w dziedzinie C#. Dzięki doskonałej reputacji zdobył ogromną bazę klientów dla swojej firmy konsultingowej Sandler Software LLC, która obsługuje podmioty z wielu różnych branż. Klienci umieszczali go na swoich listach „zatwierdzonych konsultantów” jako zaufanego dostawcę i partnera, co ponownie zaprowadziło go do firmy Microsoft.

W przeszłości wielokrotnie zajmował się korektą książek wydawanych przez Packt Publishing i dziękuje wydawnictwu za to, że ponownie otrzymał taką możliwość.

Wprowadzenie

Jako konsultant współpracowałem z wieloma zespołami i organizacjami. Miałem okazję spotykać zespoły korzystające z programowania sterowanego testami (ang. test-driven development, TDD) oraz zespoły, które przeprowadzały testy jednostkowe bez stosowania podejścia w stylu TDD. Zetknąłem się również z zespołami, których członkowie sądzili, że wykonują testy jednostkowe, podczas gdy tak naprawdę przeprowadzano testy integracyjne. Miałem również styczność z zespołami, w których nie wykonywano żadnych testów. Na podstawie zebranych danych empirycznych zacząłem nabierać przekonania, że największy sukces odnosiły zespoły przyjmujące podejście w stylu TDD, przy czym nie działo się tak z powodu zastosowania programowania sterowanego testami. Podejście w stylu TDD jest skutkiem pasji.

Programowanie sterowane testami oznacza testy jednostkowe plus pasję. W niektórych zespołach testy jednostkowe są narzucone, więc programiści nie mają wyjścia. Jednak podejście w stylu TDD rzadko jest narzucane i decyzja o jego zastosowaniu należy do programistów. Nie trzeba dodawać, że programiści pracujący z pasją tworzą wysokiej jakości projekty, które z kolei mają większą szansę na osiągnięcie sukcesu.

Programowanie sterowane testami jest zwykle łączone z wybranymi bądź wszystkimi aspektami architektury DDD (ang. domain-driven design). Dlatego też w książce omówiłem podejścia w stylu TDD i DDD, aby przedstawić znacznie bardziej rzeczywiste przykłady. Ponadto chciałem odzwierciedlić obecny stan rynku, który jest podzielony między dwiema kategoriami baz danych: relacyjnymi i opartymi na dokumentach. Dlatego też pozwoliłem sobie dodać przykładowe rozdziały dotyczące każdej z tych kategorii, aby dzięki temu pokazać różnice w implementacji testów jednostkowych, a jednocześnie zachować pragmatyczny charakter książki.

Nie daj się zwieść objętości książki; to diagramy i przykładowe fragmenty kodu sprawiły, że jest tak duża. Starałem się unikać przestarzałych i niepraktycznych teorii, aby w ten sposób zmniejszyć ilość omawianego materiału i skoncentrować się na najważniejszych kwestiach.

Podejście i testy jednostkowe są wymieniane w większości obecnych ofert pracy, stanowią wymaganie podczas wykonywania przykładowych projektów w trakcie rekrutacji, a także pojawiają się w pytaniach zadawanych kandydatom na stanowiska związane z programowaniem. Jeżeli chcesz dowiedzieć się więcej na te tematy i stać się programistą stosującym podejście w stylu TDD, masz w ręku odpowiednią książkę.

Dostępnych jest wiele innych dobrych książek poświęconych programowaniu sterowanemu testami i przeznaczonych dla programistów .NET, więc być może zastanawiasz się, dlaczego akurat masz sięgnąć po tę pozycję. W swojej książce zamierzam pokazać Ci rzeczywiste implementacje przez wejście do świata architektury DDD, relacyjnych baz danych oraz baz danych opartych na dokumentach. Zaprezentuję drzewo decyzyjne modelu mentalnego, którego używają zespoły stosujące programowanie sterowane testami. Pokażę również związek między regułami SOLID a podejściem w stylu TDD, a także omówię niezapomniane najlepsze praktyki, znane jako wskazówki FIRSTHAND, dotyczące podejścia w stylu TDD.

Moim celem było napisanie książki, która pozwoli Ci stać się pewnym siebie praktykiem programowania sterowanego testami, a przynajmniej praktykiem testów jednostkowych. Mam nadzieję, że osiągnąłem ten cel.

Dla kogo przeznaczona jest ta książka?

Programowanie sterowane testami to ważne podejście stosowane podczas opracowywania, dokumentowania i testowania aplikacji od samego początku jej powstawania. Gdy programista chce otrzymać wyższe stanowisko, ze słowem senior w nazwie, wówczas musi znać zagadnienia związane z programowaniem sterowanym testami, testami jednostkowymi, dublerami używanymi w trakcie testów (ang. test doubles), mechanizmem wstrzykiwania zależności itd.

To jest książka przeznaczona dla programistów .NET średniego i wyższego szczebla, którzy szukają możliwości wykorzystania potencjału drzemiącego w programowaniu sterowanym testami, aby tworzyć wysokiej jakości aplikacje. Przyjąłem założenie, że masz podstawową wiedzę z zakresu programowania w języku C# oraz programowania zorientowanego obiektowo. Nie musisz nic wiedzieć na temat koncepcji TDD ani testów jednostkowych. W książce znajdziesz dokładne omówienia wszystkich koncepcji programowania sterowanego testami i testów jednostkowych, a ponadto stanowi ona doskonały przewodnik dla programistów, którzy chcą zupełnie od podstaw tworzyć aplikacje z zastosowaniem podejścia w stylu TDD, oraz tych, którzy dopiero planują wykorzystanie testów jednostkowych w swoich organizacjach.

Co znajdziesz w książce?

Niniejsza książka przedstawia programowanie sterowane testami i jego ekosystem na platformie .NET, obejmujący zintegrowane środowiska programistyczne oraz biblioteki, a także wyjaśnia proces przygotowania odpowiedniego środowiska. Na początku zostaną omówione zagadnienia pozwalające na zdobycie podstawowej wiedzy z zakresu programowania sterowanego testami, obejmujące m.in. wstrzykiwanie zależności, testy jednostkowe i dublery używane podczas testów. Następnie, po przedstawieniu podejścia w stylu TDD oraz związanych z nim najlepszych praktyk, pokażę budowanie aplikacji zupełnie od początku, z wykorzystaniem architektury DDD.

W książce znajdziesz również podstawowe informacje związane z przygotowaniem potoku ciągłej integracji i obsługą starszego kodu, stworzonego bez uwzględnienia zadań związanych z jego przetestowaniem. Na koniec przedstawię informacje o sposobach zastosowania podejścia w stylu TDD we własnej organizacji.

Rozdział 1., „Przygotowanie pierwszej implementacji TDD”, nie zawiera przydługiego wprowadzenia ani teorii, a zamiast tego od razu przechodzi do kwestii związanych z wyborem zintegrowanego środowiska programistycznego i przygotowania pierwszej implementacji w stylu TDD, aby Czytelnik otrzymał przedsmak tego, co znajduje się w książce.

Rozdział 2., „Wprowadzenie do mechanizmu wstrzykiwania zależności”, przedstawia zaawansowane reguły programowania zorientowanego obiektowo, które trzeba poznać, aby zrozumieć koncepcję mechanizmu wstrzykiwania zależności. Znajdziesz tutaj również wiele przykładów.

Rozdział 3., „Rozpoczęcie pracy z testami jednostkowymi”, zawiera krótkie wprowadzenie do frameworka xUnit oraz podstawy związane z testami jednostkowymi.

Rozdział 4., „Rzeczywiste stosowanie testów jednostkowych z wykorzystaniem dublerów używanych podczas testów”, zawiera omówienie wielu zagadnień związanych z namiastkami, imitacjami i biblioteką NSubstitute oraz przedstawia wiele innych kategorii testów.

Rozdział 5., „Programowanie sterowane testami”, pokazuje, jak tworzyć testy jednostkowe, ale z wykorzystaniem podejścia w stylu TDD, a także przedstawia ich wady i zalety.

Rozdział 6., „Wskazówki FIRSTHAND dotyczące programowania sterowanego testami”, zawiera dokładne omówienie najlepszych praktyk związanych z testami jednostkowymi i programowaniem sterowanym testami.

Rozdział 7., „Pragmatyczne omówienie architektury DDD”, przedstawia wprowadzenie do zagadnień dotyczących architektury DDD, usług i repozytoriów.

Rozdział 8., „Opracowanie aplikacji pozwalającej na rezerwowanie wizyt”, prezentuje specyfikację naszej przykładowej aplikacji, którą zbudujemy w dalszej części książki z wykorzystaniem architektury DDD i programowania sterowanego testami.

Rozdział 9., „Wykorzystanie Entity Framework i relacyjnej bazy danych do opracowania aplikacji pozwalającej na rezerwowanie wizyt”, pokazuje przykład budowania aplikacji z zastosowaniem programowania sterowanego testami i relacyjnej bazy danych.

Rozdział 10., „Wykorzystanie wzorca repozytorium i opartej na dokumentach bazy danych do opracowania aplikacji pozwalającej na rezerwowanie wizyt”, pokazuje przykład budowania aplikacji z zastosowaniem programowania sterowanego testami, bazy danych opartej na dokumentach oraz wzorca repozytorium.

Rozdział 11., „Wdrożenie potoku ciągłej integracji za pomocą usługi GitHub Actions”, wyjaśnia, jak używać usługi GitHub Actions do utworzenia potoku ciągłej integracji przeznaczonego dla aplikacji zbudowanej w rozdziale 10.

Rozdział 12., „Praca z uaktualnionymi projektami”, pokazuje przebieg procesu, gdy rozważane jest użycie programowania sterowanego testami i testów jednostkowych w starszych projektach.

Rozdział 13. „Zawiłości związane ze stosowaniem programowania sterowanego testami”, wyjaśnia przebieg szkolenia, gdy organizacja ma wdrożyć programowanie sterowane testami.

Dodatek A., „Biblioteki, których najczęściej używa się podczas stosowania testów jednostkowych”, przedstawia wybrane przykłady związane z bibliotekami MSTest, NUnit, Moq, Fluent Assertions i AutoFixture.

Dodatek B., „Zaawansowane scenariusze związane z używaniem imitacji”, prezentuje nieco bardziej zaawansowany scenariusz z użyciem biblioteki NSubstitute.

Jak najwięcej wynieść z lektury tej książki?

W książce przyjąłem założenie, że znasz składnię języka programowania C# oraz masz przynajmniej roczne doświadczenie w pracy z Visual Studio bądź podobnym zintegrowanym środowiskiem programistycznym. Wprawdzie omówię reguły programowania zorientowanego obiektowo, ale mimo to założyłem, że znasz już jego podstawy.

Oprogramowanie użyte w książce

Wymagania co do systemu operacyjnego

Visual Studio 2022

Windows lub macOS

Fine Code Coverage

Windows

SQL Server

Windows, macOS (Docker) lub Linux

Cosmos DB

Windows, macOS (Docker) lub Linux (Docker)

Biblioteki i frameworki

Wymagania co do systemu operacyjnego

.NET Core 6, C# 10

Windows, macOS lub Linux

xUnit

Windows, macOS lub Linux

NSubstitute

Windows, macOS lub Linux

Entity Framework

Windows, macOS lub Linux

Aby maksymalnie wykorzystać materiał zamieszczony w książce, musisz mieć zintegrowane środowisko programistyczne przeznaczone do tworzenia kodu źródłowego w języku C#. Zdecydowałem się na użycie Visual Studio 2022 Community Edition, a alternatywne opcje przedstawię na początku rozdziału 1.

Jeżeli masz tę książkę w wersji elektronicznej (e-book), wówczas gorąco zachęcam Cię do samodzielnego wypisywania kodu bądź też pobrania go z repozytorium GitHub (jego adres podam w następnym podrozdziale). Dzięki temu możesz uniknąć wielu potencjalnych błędów związanych z kopiowaniem i wklejaniem kodu źródłowego.

Pobieranie przykładów kodu

Wszystkie pliki z kodem źródłowym opisanym w tej książce są dostępne w repozytorium GitHub pod adresem https://github.com/PacktPublishing/Pragmatic-Test-Driven-Development-in-C-Sharp-and-.NET. Wszelkie uaktualnienia omówionego kodu będą udostępniane poprzez wymienione repozytorium.

Przyjęte konwencje

W książce zastosowałem wiele różnych stylów tekstu, pozwalających na rozróżnienie poszczególnych rodzajów informacji.

KodWTekście. Ten styl wskazuje na fragmenty kodu w tekście, nazwy tabel baz danych i dane wejściowe wprowadzone przez użytkownika. Oto przykład: „Wykonanie tego fragmentu kodu zakończy się niepowodzeniem, ponieważ użycie UnitTest2 przed UnitTest1 prowadzi do niezaliczenia testu”.

Kursywa. Tym stylem oznaczyłem nazwy plików i katalogów, rozszerzenia plików, ścieżki dostępu, adresy URL, a także adresy w serwisie Twitter. Oto przykład: „Otwórz plik Class1.cs w katalogu MyProject”.

Blok kodu przedstawia się następująco:

public class SampleTests

{

private static int _staticField = 0;

[Fact]

public void UnitTest1()

{

_staticField += 1;

Assert.Equal(1, _staticField);

}

[Fact]

public void UnitTest2()

{

_staticField += 5;

Assert.Equal(6, _staticField);

}

}

Gdy będę chciał zwrócić Ci uwagę na konkretny fragment kodu, wówczas skorzystam z pogrubienia elementów bądź całych wierszy:

public class SampleTests

{

private static int _staticField = 0;

[Fact]

public void UnitTest1()

{

_staticField += 1;

Assert.Equal(1, _staticField);

}

[Fact]

public void UnitTest2()

{

_staticField += 5;

Assert.Equal(6, _staticField);

}

}

Polecenia wydawane w wierszu poleceń zostaną zapisane w pokazany tutaj sposób.

GET https://webapidomain/services

Nowe pojęcia i ważne słowa są zapisywane pogrubioną czcionką. Słowa widoczne na ekranie, w menu lub w oknach dialogowych są przedstawiane w tekście następująco: „Po zainstalowaniu lokalnego emulatora, musisz pobrać ciąg tekstowy połączenia. W tym celu przejdź pod adres https://localhost:8081/_explorer/index.html i skopiuj zawartość pola Primary Connection String”.

Wskazówki lub ważne uwagi

Wskazówki lub ważne uwagi będą się pojawiać w takich ramkach.

CZĘŚĆ 1
Rozpoczęcie pracy i podstawy TDD

W tej części kolejno wprowadzę wszystkie koncepcje tworzące podejście programowania sterowanego testami. Rozpocznę od mechanizmu wstrzykiwania zależności, następnie przejdę do dublerów używanych podczas testów, a zakończę wskazówkami i najlepszymi praktykami dotyczącymi podejścia w stylu TDD.

Lektura części I książki zapewni wiedzę niezbędną do tworzenia aplikacji z wykorzystaniem programowania sterowanego testami. Oto rozdziały składające się na tę część:

- Rozdział 1., „Przygotowanie pierwszej implementacji TDD”

- Rozdział 2., „Wprowadzenie do mechanizmu wstrzykiwania zależności”

- Rozdział 3., „Rozpoczęcie pracy z testami jednostkowymi”

- Rozdział 4., „Rzeczywiste stosowanie testów jednostkowych z wykorzystaniem dublerów używanych podczas testów”

- Rozdział 5., „Programowanie sterowane testami”

- Rozdział 6., „Wskazówki FIRSTHAND dotyczące programowania sterowanego testami”

Rozdział 1.
Przygotowanie pierwszej implementacji TDD

Zawsze uwielbiałem książki, które rozpoczynały się od krótkiego przykładu przedstawiającego zagadnienia omawiane w tekście, a dopiero później zawierały dokładne omówienie tematu. Dzięki temu wiem, co będę poznawał podczas lektury książki. Chciałbym zastosować takie samo podejście i na początku książki zamieścić przykład w postaci niewielkiej aplikacji.

W tym miejscu zasymuluję minimalne wymagania biznesowe, a podczas ich implementacji przedstawię wybrane koncepcje związane z testami jednostkowymi i programowaniem sterowanym testami (ang. test-driven development , TDD ). Nie przejmuj się, jeśli te koncepcje nie są dla Ciebie jasne bądź wymagają dalszych wyjaśnień, ponieważ w tym rozdziale celowo przedstawiam je tylko pokrótce, aby dać Ci przedsmak tego, co znajdziesz w książce. W pozostałej części tekstu znajdziesz dokładne omówienie wszystkich poruszonych tutaj koncepcji.

Muszę dodać, że w książce wymiennie używam pojęć testów jednostkowych i podejścia w stylu TDD , z niewielką różnicą między nimi. Wszystko stanie się jaśniejsze w rozdziale 5.

Natomiast w tym rozdziale poruszam następujące tematy:

- wybór zintegrowanego środowiska programistycznego (ang. integrated development environment , IDE),

- utworzenie szkieletu rozwiązania razem z testami jednostkowymi,

- implementacja wymagań z zastosowaniem programowania sterowanego testami.

Zanim zakończysz lekturę rozdziału, będziesz umiał dość komfortowo tworzyć proste testy jednostkowe za pomocą xUnit oraz ogólnie poznasz podejście w stylu TDD.

Wymagania techniczne

Kod dla tego rozdziału znajdziesz w repozytorium GitHub pod adresem https://github.com/PacktPublishing/Pragmatic-Test-Driven-Development-in-C-Sharp-and-.NET/tree/main/ch01 .

Wybór zintegrowanego środowiska programistycznego

Z perspektywy podejścia w stylu TDD wybór zintegrowanego środowiska programistycznego będzie miał wpływ na Twoją produktywność. Implementacja podejścia wykorzystującego programowanie sterowane testami może być wspomagana przez IDE oferujące możliwości refaktoryzacji i generowania kodu źródłowego. Wybór odpowiedniego środowiska IDE zmniejszy liczbę powtarzających się — i potencjalnie nużących — zadań koniecznych do wykonania.

W kolejnych punktach przedstawię trzy popularne środowiska IDE, które oferują obsługę projektów w języku C#: Visual Studio ( VS ), Visual Studio Code ( VS Code ) i JetBrains Rider .

Microsoft Visual Studio

W tym rozdziale oraz w pozostałej części książki będę używał Visual Studio 2022 Community Edition — możesz skorzystać również z wydań Professional i Enterprise . Programiści indywidualni mogą bezpłatnie używać wydania Visual Studio Community Edition do tworzenia własnych aplikacji, zarówno bezpłatnych, jak i komercyjnych. Z kolei w przypadku organizacji istnieją pewne ograniczenia. Z pełną treścią licencji oraz ze szczegółami produktu możesz się zapoznać na stronie https://visualstudio.microsoft.com/vs/ community/ .

Jeżeli masz wcześniejsze wydanie Visual Studio i nie chcesz go uaktualniać, wówczas używane w książce VS 2022 Community Edition możesz zainstalować równolegle z dowolną poprzednią wersją.

Wersje Visual Studio 2022 przeznaczone dla platform Windows i Mac zawierają narzędzia wymagane do utworzenia kodu oraz uruchomienia testów. W tej książce wszystkie projekty, rysunki i procedury zostały wykonane z użyciem wydania VS przeznaczonego dla platformy Windows . Możesz je pobrać ze strony https://visualstudio.microsoft.com/downloads/ .

Aby można było wykonywać projekty zamieszczone w książce, podczas instalowania VS trzeba zaznaczyć pole wyboru przynajmniej przy ASP.NET and web development , jak pokazałem na rysunku 1.1.

Rysunek 1.1. Wybór komponentów w trakcie instalacji Visual Studio

Jeżeli w systemie masz już zainstalowane wcześniejsze wydanie Visual Studio, wówczas możesz sprawdzić, czy komponent ASP.NET and web development jest dostępny. W tym celu wykonaj wymienione tutaj kroki.

1. W Windows przejdź do Ustawienia/Aplikacje/Zainstalowane aplikacje .

2. Odszukaj Visual Studio na wyświetlonej liście aplikacji.

3. Kliknij przycisk przedstawiający pionowy wielokropek (trzy kropki jedna pod drugą).

4. Wybierz opcję Modyfikuj , jak pokazałem na rysunku 1.2.

Rysunek 1.2. Modyfikacja ustawień opcji instalacyjnych Visual Studio

Visual Studio to duże oprogramowanie, składające się z wielu komponentów do zainstalowania. Ponadto już po zainstalowaniu to środowisko IDE będzie uruchamiało się wolniej od produktów Visual Studio Code bądź Rider , które również są przedstawione w rozdziale.

ReSharper

JetBrains ReSharper to popularna komercyjna wtyczka przeznaczona dla Visual Studio, która wzbogaca je o wiele dodatkowych funkcjonalności. Z perspektywy programowania sterowanego testami jesteśmy zainteresowani wymienionymi tutaj aspektami:

- Refaktoryzacja . Wtyczka ReSharper zapewnia wiele związanych z refaktoryzacją funkcji, które okazują się przydatne na etapie refaktoryzacji kodu źródłowego.

- Generowanie kodu . Ta funkcja okazuje się szczególnie przydatna, gdy najpierw tworzy się testy jednostkowe, a dopiero później generuje kod źródłowy.

- Testy jednostkowe . Wtyczka ReSharper wspomaga istniejące w Visual Studio narzędzia przeznaczone do pracy z testami jednostkowymi oraz zapewnia możliwość współpracy z większą liczbą frameworków testów jednostkowych.

ReSharper to produkt oferowany na zasadzie subskrypcji; dostępna jest jego 30-dniowa wersja próbna. Zachęcam do rozpoczęcia pracy z narzędziami testów jednostkowych w Visual Studio bez użycia wtyczki ReSharper. Gdy poznasz już możliwości VS w tym zakresie, wtedy sięgnij po wtyczkę ReSharper, a dostrzeżesz korzyści, jakie zapewnia.

Uwaga

Każde nowe wydanie Visual Studio zawiera dodatkowe możliwości w zakresie refaktoryzacji i generowania kodu, podobne do oferowanych przez wtyczkę ReSharper. Jednak obecnie to ReSharper ma więcej funkcji zaawansowanych.

O wtyczce ReSharper wspominam tylko w tym miejscu książki. Więcej informacji na jej temat znajdziesz na stronie https://www.jetbrains.com/resharper/ .

JetBrains Rider

JetBrains to firma stojąca za środowiskiem IDE Rider oraz popularną wtyczką do Visual Studio, czyli ReSharper . Jeżeli zdecydujesz się na wybór JetBrains Rider jako środowiska programowania na platformie .NET, wówczas będziesz mieć dostęp do wszystkich funkcjonalności, które są wymagane podczas lektury niniejszej książki. Rider oferuje wiele funkcji, m.in.:

- potężne środowisko wykonywania testów jednostkowych , które może konkurować z oknem Test Explorer w Visual Studio,

- rozbudowane możliwości w zakresie refaktoryzacji i generowania kodu źródłowego, wraz ze znacznie bardziej zaawansowanymi funkcjami niż te dostępne w Visual Studio 2022.

Wymienione punkty mają znaczenie krytyczne podczas budowania systemu z wykorzystaniem podejścia w stylu TDD . Mimo to w książce zdecydowałem się na użycie środowiska Visual Studio zamiast Rider. Niemniej jednak zamieszczone w tekście informacje można zastosować również w przypadku środowiska Rider. Trzeba tylko pamiętać, że Rider korzysta z innego systemu menu i innych skrótów klawiszowych.

Uwaga

VS .NET (wydanie Visual Studio zapewniające obsługę platformy .NET) pojawiło się w lutym 2002 roku, podczas gdy środowisko Rider dopiero w sierpniu 2017 roku. Dlatego też produkt firmy Microsoft jest znacznie częściej używany przez programistów .NET. To miało wpływ na wybór środowiska IDE na potrzeby niniejszej książki.

Więcej informacji na temat środowiska Rider znajdziesz na stronie https://www.jetbrains.com/rider/ .

Visual Studio Code

Jeżeli zaliczasz się do zwolenników Visual Studio Code, z zadowoleniem przyjmiesz informację o dodaniu przez Microsoft wizualnej obsługi testów jednostkowych (co ma krytyczne znaczenie w programowaniu sterowanym testami) do wersji Visual Studio Code 1.59, wydanej w lipcu 2021 roku.

VS Code to lekkie środowisko IDE, które zapewnia natywne opcje dotyczące refaktoryzacji oraz obsługuje wiele wtyczek refaktoryzacji opracowanych przez podmioty zewnętrzne. Prostota i elegancja VS Code mogą przyciągać wielu zwolenników programowania sterowanego testami. Jednak dostępna funkcjonalność związana z programowaniem w języku C# — zwłaszcza dla osób z doświadczeniem w stosowaniu podejścia w stylu TDD — nie jest tak zaawansowana jak ta, którą można znaleźć w produktach Visual Studio i Rider.

Wprawdzie w książce korzystam ze środowiska Visual Studio, ale przykłady można dostosować także do Visual Studio Code. Więcej informacji na temat tego produktu znajdziesz na stronie https://visualstudio.microsoft.com/downloads/ .

Wersje .NET i C#

Wydanie Visual Studio 2022 jest dostarczane razem z obsługą .NET 6 i C# 10 . Tego wydania będę używał na potrzeby niniejszego rozdziału oraz pozostałej części książki.

W swojej grupie w serwisie LinkedIn przeprowadziłem małą ankietę mającą na celu zebranie od użytkowników informacji o używanych przez nich środowiskach IDE. Wyniki tej ankiety pokazałem na rysunku 1.3.

Rysunek 1.3. Wyniki ankiety przeprowadzonej przeze mnie w serwisie LinkedIn

Jak możesz zobaczyć, najwięcej respondentów (58%) używa Visual Studio, przy czym 18% korzysta także z wtyczki ReSharper. Na drugim miejscu znalazł się Rider, który wybrało 24% badanych. Najmniejszą popularnością cieszy się Visual Studio Code (18%). Biorąc pod uwagę fakt, że w ankiecie wzięło udział jedynie 45 respondentów, wyniki są jedynie orientacyjne i na pewno nie odzwierciedlają sytuacji na rynku.

Wybór odpowiedniego środowiska IDE to indywidualna decyzja programisty. Gdy pytam programistę stosującego podejście w stylu TDD o wybrane przez niego środowisko, za każdym razem jestem przekonywany, jak świetne jest używane przez niego środowisko. Reasumując, zdecyduj się na środowisko IDE, które zapewni Ci najlepszą produktywność.

Utworzenie szkieletu rozwiązania razem z testami jednostkowymi

Skoro omówienie wymagań technicznych mamy już za sobą, możemy przejść do zbudowania naszej pierwszej implementacji. Na potrzeby tego rozdziału oraz by skoncentrować się na koncepcjach programowania sterowanego testami, rozpoczniemy od prostych wymagań biznesowych.

Przyjmujemy założenie, że jesteś programistą pracującym dla fikcyjnej firmy Unicorn Quality Solutions Inc. ( UQS ), która zajmuje się tworzeniem wysokiej jakości oprogramowania.

Wymagania

Zespoły tworzące oprogramowanie w firmie UQS stosują w trakcie tego procesu podejście zwinne (ang. agile ), więc wymagania biznesowe zostają przedstawione w postaci tzw. historyjek użytkownika .

Pracujesz nad biblioteką matematyczną, która będzie miała postać pakietu używanego przez innych programistów. Można założyć, że zajmujesz się opracowaniem funkcjonalności w bibliotece NuGet przeznaczonej do użycia przez inne aplikacje. Oto historyjka użytkownika, którą wybrałeś do zaimplementowania.

Tytuł historyjki:

Dzielenie całkowite

Opis historyjki:

Jako klient biblioteki matematycznej chcę mieć możliwość dzielenia dwóch liczb całkowitych.

Kryteria akceptacji:

Obsługa danych wejściowych typu Int32 i danych wyjściowych typu decimal.

Zapewnienie wysokiej dokładności danych wyjściowych bez ich zaokrąglania bądź jedynie z minimalnym zaokrągleniem.

Obsługa dzielenia liczb całkowitych zarówno podzielnych, jak i niepodzielnych.

Zgłoszenie wyjątku DivideByZeroException w przypadku dzielenia przez zero.

Utworzenie szkieletu projektu

Dla tej historyjki użytkownika będą potrzebne dwa projekty C#. Pierwszy to biblioteka klas zawierająca produkcyjny kod źródłowy, a drugi to testy jednostkowe dla tej biblioteki.

Uwaga

Biblioteka klas pozwala na umieszczanie funkcjonalności w module, który następnie może być używany przez wiele aplikacji. Podczas kompilacji powstaje plik tzw. biblioteki dołączanej dynamicznie (ang. dynamic-link library , DLL). Biblioteka klas nie może funkcjonować samodzielnie, natomiast może działać jako część aplikacji.

Jeżeli nie masz doświadczenia w pracy z biblioteką klas, na potrzeby tej książki możesz ją traktować jako aplikację działającą w konsoli (wiersz poleceń) bądź jako aplikację internetową.

Utworzenie projektu biblioteki klas

Te same projekty zostaną utworzone na dwa sposoby — za pomocą graficznego interfejsu użytkownika (ang. graphical user interface , GUI ) oraz za pomocą interfejsu wiersza poleceń (ang. command-line interface , CLI ). Wybierz preferowany przez siebie sposób bądź ten, w pracy z którym masz doświadczenie.

Użycie graficznego interfejsu użytkownika

Aby utworzyć bibliotekę klas, uruchom Visual Studio i wykonaj przedstawioną tutaj procedurę.

1. Wybierz opcję menu File/New/Project .

2. W wyświetlonym oknie dialogowym wyszukaj element Class Library (C#) .

3. Zaznacz znaleziony element, a następnie kliknij przycisk Next . Na ekranie zostanie wyświetlone okno dialogowe Add a new project , jak pokazałem na rysunku 1.4.

Rysunek 1.4. Wyszukiwanie szablonu projektu Class Library (C#)

Uwaga

Upewnij się, że w oknie widzisz tag C # oraz że NIE został wybrany element Class Library (.NET Framework) . W książce korzystamy z platformy .NET (a nie z klasycznej .NET Framework).

4. W oknie dialogowym Configure your new project wpisz Uqs.Arithmetic w polu Project name i UqsMathLib w polu Solution name , a następnie kliknij przycisk Next . Spójrz na rysunek 1.5.

Rysunek 1.5. Okno dialogowe przeznaczone do konfiguracji nowego projektu

5. W oknie dialogowym Additional information z rozwijanego menu wybierz opcję .NET 6.0 (Long-term support) , jak pokazałem na rysunku 1.6., i kliknij przycisk Create .

Rysunek 1.6. Okno dialogowe Additional information

W taki sposób projekt biblioteki klas został przygotowany za pomocą graficznego interfejsu użytkownika Visual Studio .

Użycie wiersza poleceń

Jeżeli preferujesz tworzenie projektu za pomocą wiersza poleceń, w tym miejscu znajdziesz polecenia, które trzeba w tym celu wydać.

1. Utwórz katalog o nazwie UqsMathLib (wydaj polecenie md UqsMathLib ).

2. W wierszu poleceń przejdź do nowego katalogu (wydaj polecenie cd UqsMathLib ), jak pokazałem na rysunku 1.7.

Rysunek 1.7. Okno wiersza poleceń zawierające niezbędne polecenia

3. Utwórz plik rozwiązania (z rozszerzeniem .sln ), który zostanie wygenerowany z nazwą odpowiadającą nazwie katalogu — czyli w omawianym przykładzie to plik UqsMathLib.sln . W tym celu wydaj następujące polecenie:

dotnet new sln

4. Utwórz nową bibliotekę klas o nazwie Uqs.Arithmetic w katalogu o tej samej nazwie i użyj platformy .NET 6.0 . Oto polecenie, które należy w tym celu wydać:

dotnet new classlib -o Uqs.Arithmetic -f net6.0

5. Nowy projekt dodaj do pliku rozwiązania, co odbywa się przez wykonanie przedstawionego tutaj polecenia.

dotnet sln add uqs.arithmetic

W taki sposób projekt biblioteki klas został przygotowany za pomocą wiersza poleceń.

Utworzenie projektu testów jednostkowych

W tym momencie mamy rozwiązanie zawierające tylko jeden projekt, czyli bibliotekę klas. Kolejnym zadaniem jest dodanie do naszego rozwiązania biblioteki testów jednostkowych. W tym celu skorzystamy z projektu xUnit Test Project .

xUnit.net to dostępne bezpłatnie, otwartoźródłowe narzędzie testów jednostkowych dla platformy .NET. Zostało udostępnione na licencji Apache 2. Visual Studio natywnie zapewnia możliwość dodawania i uruchamiania projektów xUnit, więc korzystanie z tego frameworka nie wymaga używania żadnego specjalnego narzędzia ani wtyczki.

Dokładne omówienie xUnit znajdziesz w rozdziale 3.

Będziemy stosować powszechnie przyjętą konwencję nadawania projektom testów jednostkowych nazw w postaci [NazwaProjektu].Tests.Unit . Dlatego też nasz projekt otrzymuje nazwę Uqs.Arithmetic.Tests.Unit .

Zaprezentuję dwa sposoby, na jakie jest możliwe utworzenie projektu testów jednostkowych. Wybierz ten, który Ci bardziej odpowiada.

Użycie graficznego interfejsu użytkownika

Aby utworzyć projekt testów jednostkowych, przejdź do okna Solution Explorer i wykonaj przedstawioną tutaj procedurę.

1. Prawym przyciskiem myszy kliknij plik rozwiązania ( UqsMathLib ).

2. Wybierz opcję menu Add/New Project… , jak pokazałem na rysunku 1.8.

Rysunek 1.8. Utworzenie nowego projektu w rozwiązaniu

3. W wyświetlonym oknie dialogowym wyszukaj element xUnit Test Project , wybierz go, a następnie kliknij przycisk Next .

4. W polu Project name wpisz wartość Uqs.Arithmetic.Tests.Unit .

5. Kliknij przycisk Next , wybierz platformę .NET 6.0 , a następnie kliknij przycisk Create .

W ten sposób projekt testów jednostkowych został utworzony za pomocą graficznego interfejsu użytkownika Visual Studio. Ten projekt wymaga jeszcze odwołania do biblioteki klas. Aby je dodać, wykonaj wymienione tutaj kroki.

1. W oknie eksploratora rozwiązań prawym przyciskiem myszy kliknij element Dependencies projektu Uqs.Arithmetic.Tests.Unit .

2. Wybierz opcję menu Add Project Reference… .

3. Zaznacz element Uqs.Arithmetic i kliknij przycisk OK .

W ten sposób rozwiązanie zostało w pełni przygotowane za pomocą graficznego interfejsu użytkownika Visual Studio. Tę samą procedurę można przeprowadzić za pomocą wiersza poleceń. Dokładnie to wyjaśnię w następnym podpunkcie.

Użycie wiersza poleceń

Obecnie mamy rozwiązanie zawierające tylko jeden projekt, bibliotekę klas. Kolejnym zadaniem jest dodanie do naszego rozwiązania biblioteki testów jednostkowych.

Utwórz nowy projekt xUnit o nazwie Uqs.Arithmetic.Tests.Unit w katalogu o tej samej nazwie i zdecyduj o użyciu platformy .NET 6.0 . Oto polecenie, które trzeba wydać:

dotnet new xunit -o Uqs.Arithmetic.Tests.Unit -f net6.0

Nowy projekt dodaj do pliku rozwiązania, co odbywa się przez wykonanie przedstawionego tutaj polecenia.

dotnet sln add Uqs.Arithmetic.Tests.Unit

W ten sposób nasze rozwiązanie zawiera dwa projekty. Skoro projekt testów jednostkowych jest przeznaczony do testowania biblioteki klas, powinien mieć do niej odwołanie.

Wprawdzie projekt został utworzony za pomocą wiersza poleceń, ale nadal projekt testów jednostkowych musi zawierać odwołanie do biblioteki klas. W tym celu należy dodać odwołanie z Uqs.Arithmetic.Tests.Unit do Uqs.Arithmetic , jak pokazałem w kolejnym fragmencie kodu:

dotnet add Uqs.Arithmetic.Tests.Unit reference

Uqs.Arithmetic

Przedstawione rozwiązanie zostało w pełni przygotowane za pomocą wiersza poleceń.

Ostateczne rozwiązanie

Niezależnie od metody użytej do utworzenia rozwiązania — za pomocą graficznego interfejsu użytkownika bądź wiersza poleceń — w tym momencie będziesz mieć te same pliki. Jeżeli otworzysz rozwiązanie w Visual Studio, zobaczysz okno pokazane na rysunku 1.9.

Rysunek 1.9. Struktura ostatecznego rozwiązania

Aby rozpocząć pracę od czystego stanu, usuń plik Class1.cs . Został dodany automatycznie przez szablon i nie będziemy go używać.

Na rysunku 1.10 pokazałem logiczną strukturę dwóch naszych projektów.

Rysunek 1.10. Logiczna struktura naszych przykładowych projektów

Dotychczas utworzyliśmy dwa projekty. Pierwszy ( Uqs.Arithmetic ) będzie na pewnym etapie przekazany do środowiska produkcyjnego, natomiast drugi ( Uqs.Arithmetic.Tests.Unit ) jest przeznaczony do przetestowania tego pierwszego projektu. Plik rozwiązania zapewnia połączenie między wymienionymi projektami.

Po zakończeniu tej mniej przyjemnej fazy budowania szkieletu projektu i definiowania zależności można przystąpić do znacznie przyjemniejszych zadań, które są bezpośrednio powiązane z testami jednostkowymi.

Zapoznanie się z wbudowanymi narzędziami przeznaczonymi do przeprowadzania testów

Osiągnęliśmy stan, kiedy trzeba rozejrzeć się oraz sprawdzić, jak wykrywać i wykonywać testy. To będzie wymagało poznania dostępnych narzędzi.

Mamy już kod wygenerowany przez szablon xUnit — zapoznaj się z kodem znajdującym się w pliku UnitTest1.cs i zamieszczonym w kolejnym fragmencie kodu.

using Xunit;

namespace Uqs.Arithmetic.Tests.Unit;

public class UnitTest1

{

[Fact]

public void Test1()

{

}

}

To jest zwykła klasa C#. Fact to atrybut pochodzący z frameworka xUnit. Wskazuje on każdemu narzędziu zgodnemu z xUnit, że metoda udekorowana tym atrybutem jest metodą testu jednostkowego . Narzędzia zgodne z xUnit, np. okno Test Explorer i .NET CLI Test Command , powinny mieć możliwość znalezienia tej metody w rozwiązaniu i jej uruchomienia.

Podążając za trendem z wcześniejszej części rozdziału, dostępne narzędzia przeznaczone do przeprowadzania testów można wykorzystać na dwa sposoby — za pomocą graficznego interfejsu użytkownika oraz z poziomu wiersza poleceń.

Użycie graficznego interfejsu użytkownika

Visual Studio oferuje wyposażone w graficzny interfejs użytkownika narzędzie przeznaczone do wykrywania i wykonywania testów — ma ono postać okna o nazwie Test Explorer . Aby zobaczyć, w jaki sposób to narzędzie będzie wykrywało metody testów, z poziomu menu wybierz opcję Test/Test Explorer . Na ekranie pojawi się okno dialogowe pokazane na rysunku 1.11.

Rysunek 1.11. Okno Test Explorer wyświetlające niewykonane testy

Jak możesz zobaczyć, to narzędzie wykryło wszystkie testy istniejące w naszym rozwiązaniu oraz wyświetliło je w hierarchii Nazwa projektu/Przestrzeń nazw/Klasa/Metoda . Zwróć uwagę na wyświetlenie hierarchii w kolorze szarym oraz ze znakiem wykrzyknika. Ten znak oznacza, że testy nigdy nie zostały wykonane. Aby uruchomić te testy, możesz kliknąć przycisk uruchomienia projektu w lewym górnym rogu okna (skrót klawiszowy Ctrl+R, T — naciśnij i przytrzymaj klawisz Ctrl , a następnie w krótkich odstępach czasu naciśnij kolejno klawisze R i T ). To spowoduje skompilowanie projektu i wykonanie kodu zdefiniowanego w metodach udekorowanych atrybutem Fact . Wynik tej operacji pokazałem na rysunku 1.12.

Rysunek 1.12. Okno Test Explorer wyświetla wynik wykonania testów

Nie oczekuj żadnych fajerwerków, ponieważ mamy jedynie pusty szkielet projektu. Jednak przynajmniej testy będą oznaczone kolorem zielonym, co potwierdzi poprawność działania przygotowanego środowiska. W podobny sposób testy można wykrywać i wykonywać za pomocą wiersza poleceń.

Użycie wiersza poleceń

Testy można również wykonywać za pomocą wiersza poleceń. W tym celu należy przejść do katalogu zawierającego rozwiązanie, a następnie wydać przedstawione tutaj polecenie:

dotnet test

Efekt wykonania tego polecenia pokazałem na rysunku 1.13.

Rysunek 1.13. Polecenie .NET przeznaczone do wykrywania i wykonywania testów

Możliwość używania tego rodzaju poleceń okazuje się przydatna na późniejszych etapach pracy, gdy zachodzi potrzeba automatyzacji wykonywania testów.

Implementacja wymagań z zastosowaniem programowania sterowanego testami

Przed przystąpieniem do tworzenia jakiegokolwiek kodu rozsądne wydaje się poznanie terminologii i konwencji, aby przygotować się na słownictwo związane z testami jednostkowymi. Dlatego też pokrótce przedstawię koncepcje testowanego systemu (ang. system under test , SUT ), testów typu czerwony i zielony (ang. red/green tests ) oraz podejścia typu „ przygotowanie, działanie, asercja ” (ang. arrange-act-assert , AAA ). Więcej informacji na temat tej terminologii znajdziesz w późniejszych rozdziałach. Natomiast w tym miejscu przedstawię jedynie podstawy, które pozwolą na wykonanie kilku testów.

Podczas poznawania terminologii i konwencji ułatwimy sobie implementację rozwiązania. Jedyną kwestią, którą możesz uznać za nową i nietypową, jest to, że w przypadku programowania sterowanego testami najpierw tworzy się test jednostkowy, a dopiero później kod produkcyjny. To jest jeden z najważniejszych aspektów podejścia w stylu TDD i spotkasz się z nim w tym podrozdziale.

SUT

Kod tworzący produkt zwykle jest określany mianem produkcyjnego kodu źródłowego . Typowy kod tego rodzaju utworzony z zastosowaniem programowania zorientowanego obiektowo (ang. object-oriented programming , OOP ) ma postać podobną do tutaj przedstawionej.

public class NazwaKlasy

{

public Type NazwaMetody(...)

{

// Kod wykonujący użyteczne zadania.

}

// Jeszcze więcej kodu.

}

Podczas testowania tego fragmentu kodu test jednostkowy będzie wywoływał metodę o podanej nazwie ( NazwaMetody ) i sprawdzał jej sposób działania. W trakcie działania ta metoda może wywoływać inne elementy klasy, a także może używać innych klas bądź je wywoływać. Kod wykonywany przez tę metodę ( NazwaMetody ) jest określany mianem testowanego systemu (SUT) bądź testowanego kodu (ang. code under test , CUT ). Znacznie częściej można spotkać to pierwsze określenie, SUT.

Testowany system będzie oferować punkt wejścia, który następnie będzie wykorzystywany przez testy jednostkowe. Ten punkt wejścia to najczęściej metoda wywoływana z poziomu testów jednostkowych. Na rysunku 1.14 pokazałem w sposób graficzny ideę testowanego systemu i punktu wejścia.

Rysunek 1.14. Testy jednostkowe działające w SUT

W sytuacji pokazanej na rysunku 1.14 można zobaczyć kilka testów jednostkowych wywołujących ten sam punkt wejścia testowanego systemu. Dokładne omówienie koncepcji SUT znajdziesz w rozdziale 3.

Klasa testów

Zgodnie z konwencją typowa klasa testów jednostkowych używa tych samych nazw pochodzących z SUT. W kolejnym fragmencie kodu pokazałem przykład typowej klasy testów jednostkowych.

public class NazwaKlasyTests

{

[Fact]

public void NazwaMetody_Warunek1_Oczekiwanie1()

{

// Testy jednostkowe Testing wywołujące metodę o nazwie NazwaMetody.

}

// Pozostałe testy…

[Fact]

public void NazwaMetody_WarunekN_OczekiwanieN()

{

// Testy jednostkowe Testing wywołujące metodę o nazwie NazwaMetody.

}

…

}

Zwróć uwagę na to, że nazwy klasy ( NazwaKlasy ) i metody ( NazwaMetody ) w dwóch poprzednich fragmentach kodu nie są przypadkowe. Zgodnie z konwencją powinny być takie same. Aby rozpocząć przygotowanie naszych klas testujących, musimy określić nazwy klasy i metody.

Nazwa klasy

Zgodnie z wymaganiami będzie nam potrzebna klasa przeznaczona do przechowywania wszystkich metod operacji dzielenia. Nadajemy więc tej klasie nazwę Division . Jeżeli chcesz dla niej utworzyć klasę testów jednostkowych, wówczas musi mieć ona nazwę DivisionTests . Następnym krokiem jest zmiana nazwy klasy UnitTest1 na DivisionTests . To obejmuje również zmianę nazwy pliku klasy na DivisionTests.cs .

Wskazówka

Po umieszczeniu wskaźnika myszy w dowolnym miejscu nazwy klasy w kodzie źródłowym (w omawianym przykładzie to była nazwa UnitTest1 ) należy nacisnąć klawisze Ctrl+R, R (trzymając wciśnięty klawisz Ctrl , w krótkim odstępie czasu dwukrotnie naciśnij klawisz R ). Wpisz nową nazwę, DivisionTests , i naciśnij klawisz Enter . To spowoduje także zmianę nazwy pliku, o ile zostało zaznaczone pole wyboru Rename symbol’s file .

Nazwa metody

Na szczęście wymagania w omawianym przykładzie są proste, więc nazwa naszej metody to po prostu Divide . Zgodnie z wymaganiami ta metoda będzie akceptowała dwa argumenty w postaci liczb całkowitych ( int32 ) i zwróci wartość typu decimal . Kolejnym krokiem jest refaktoryzacja istniejącego testu jednostkowego z Test1 na postać DivideWarunek1_Oczekiwanie1 .

Uwaga

Przypomnienie związane z terminologią stosowaną w arytmetyce : w przypadku wyrażenia 10 / 5 = 2 wartość 10 to dzielna, 5 to dzielnik, 2 zaś to współczynnik.

Warunki i oczekiwania

Podczas testowania następuje określenie warunku oraz tego, co jest oczekiwane w przypadku spełnienia danego warunku. Rozpoczynamy od warunku podstawowego , nazywanego również ścieżką pozytywną bądź ścieżką szczęśliwą . Najpierw trzeba przygotować wszystkie ścieżki pozytywne, a dopiero później można przejść do innych przypadków. Nasz cel w testach jednostkowych sprowadza się do ustalenia warunku i oczekiwań oraz przygotowania testu jednostkowego dla każdego wariantu.

Aby pokazać związek między testowanymi metodami (metoda w naszym testowanym systemie) a warunkami i oczekiwaniami, wykorzystamy doskonale znaną konwencję, której przykład zastosowania jest pokazany w kolejnym fragmencie kodu.

[Fact]

public void NazwaMetody_Warunek_Oczekiwanie()

{

...

Oto kilka losowo wybranych nazw metod testów jednostkowych, które pomogą oswoić się ze wspomnianą wcześniej konwencją:

- SaveUserDetails _MissingEmailAddress_EmailIsMissing

- ValidateUserCredentials_HashedPasswordDoesntMatch_False

- GetUserById_IdDoesntExist_UserNotFoundException

Więcej przykładów poznasz podczas definiowania testów jednostkowych.

Podstawowym wymaganiem jest przeprowadzenie dzielenia dwóch liczb całkowitych. Najprostsza implementacja polega na podzieleniu dwóch liczb całkowitych i otrzymaniu w wyniku również liczby całkowitej. W takim przypadku warunkiem jest istnienie podzielnych liczb całkowitych (ang. divisible integers ), oczekiwanie zaś to zwrócenie przez tę operację liczby całkowitej (ang. whole number ). Teraz należy więc uaktualnić sygnaturę naszego testu jednostkowego do postaci Divide_DivisibleIntegers_WholeNum ber oraz zdefiniować tę metodę w sposób przedstawiony w kolejnym fragmencie kodu.

[Fact]

public void Divide_DivisibleIntegers_WholeNumber()

{

int dividend = 10;

int divisor = 5;

decimal expectedQuotient = 2;

decimal actualQuotient = Division.Divide(dividend,

divisor);

Assert.Equal(expectedQuotient, actualQuotient);

}

Ten kod nie skompiluje się, ponieważ na tym etapie klasa Division jeszcze nie istnieje. Doskonale o tym wiemy, bo pod nazwą klasy Division znajduje się falista linia. To jest ten rzadki przypadek, w którym niemożliwość skompilowania klasy ze względu na brak pewnego komponentu nie jest czymś złym. W ten sposób otrzymujemy informację, że test zakończył się niepowodzeniem , co w tym przypadku również jest dobrą wiadomością.

Wprawdzie wydaje się głupie zakończenie testu niepowodzeniem z powodu braku możliwości skompilowania kodu, ponieważ klasa Division nie istnieje, ale to jednocześnie oznacza, że nie został utworzony kod testowanego systemu. W rozdziale 5. dowiesz się więcej na temat uwzględniania tego przypadku.

Assert to klasa pochodząca z frameworka xUnit. Metoda statyczna Equal jest wielokrotnie przeciążana, w omawianym kodzie zaś użyto wersji o następującej sygnaturze:

public static void Equal<T>(T expected, T actual)

Po uruchomieniu ta metoda będzie sygnalizowała frameworkowi xUnit, że wartości oczekiwana i faktycznie otrzymana nie są takie same. Jeżeli po wykonaniu testu wynikiem tej asercji jest true , to oznacza zaliczenie testu.

Czerwony — zielony

Niepowodzenie jest tym, co nas najbardziej interesuje. W późniejszych rozdziałach wyjaśnię, dlaczego tak się dzieje. W tym miejscu wystarczy wiedzieć, że pracę trzeba rozpocząć od nieudanej budowy (kompilacji) bądź testu zakończonego niepowodzeniem (nieudana asercja), a następnie doprowadzić do jego zaliczenia. Podejście typu niezaliczony-zaliczony jest również określane mianem techniki refaktoryzacji czerwony — zielony , która odzwierciedla ideę zły — dobry i stój — idź .

Konieczne jest dodanie klasy Division oraz metody Divide , a także utworzenie minimalnej ilości kodu, dzięki któremu test zostanie zaliczony. W projekcie Uqs.Arithmetic utwórz nowy plik o nazwie Division.cs i przedstawionej tutaj zawartości.

namespace Uqs.Arithmetic;

public class Division

{

public static decimal Divide(int dividend, int divisor)

{

decimal quotient = dividend / divisor;

return quotient;

}

}

Wskazówka

Klasę można utworzyć przez umieszczenie wskaźnika w dowolnym miejscu nazwy klasy (w omawianym przykładzie to Division ) i naciśnięcie klawiszy Ctrl+. (trzymając wciśnięty klawisz Ctrl , naciśnij klawisz przedstawiający kropkę). Wybierz opcję Generate new type… , a następnie z rozwijanego menu Project wybierz Uqs.Arithmetic i naciśnij przycisk OK . Dalej, aby wygenerować metodę, umieść wskaźnik w klasie Divide i naciśnij klawisze Ctrl+. , wybierz opcję Generate method ‘Division.Divide’ , a otrzymasz w klasie Division szkielet metody gotowy do wypełnienia własnym kodem.

Trzeba koniecznie pamiętać, że w kodzie C# wynikiem dzielenia dwóch liczb całkowitych jest liczba całkowita . Spotykałem się z sytuacjami, w których starsi programiści zapominali o tym, co z kolei prowadziło do poważnych konsekwencji. W zaimplementowanym kodzie zostało uwzględnione jedynie dzielenie liczb całkowitych, które prowadzi do wyniku w postaci liczby całkowitej. To powinno pozwolić na zaliczenie testu.

W tym momencie jesteśmy gotowi do wykonania testów za pomocą okna Test Explorer w Visual Studio. Naciśnij więc klawisze Ctrl+R, A , aby w ten sposób skompilować projekty, a następnie wykonać wszystkie znajdujące się w nich testy (obecnie to tylko jeden). Zauważ, że okno Test Explorer wskazuje kolor zielony, a między nazwą testu i atrybutem Fact znajduje się zielona ikona. Jej kliknięcie spowoduje wyświetlenie pewnych opcji związanych z testami, jak pokazałem na rysunku 1.15.