• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

7 Błędów Początkujących Podczas Pisania Testów Jednostkowych w .NET

Object Storage Arubacloud
+2 głosów
603 wizyt
pytanie zadane 15 kwietnia 2020 w C# przez cSharpKazik Użytkownik (840 p.)

Cześć smiley 

✅ Tym razem video przedstawiające 7 Błędów Początkujących Podczas Pisania Testów Jednostkowych.

✅ Opisałem na przykładach, 7 najczęstszych błędów początkujących, popełnianych podczas pisania Testów Jednostkowych w .NET

Zapraszam do oglądania: https://video.modestprogrammer.pl/video-2/

komentarz 15 kwietnia 2020 przez Adrian1999 Nałogowiec (34,570 p.)
A ja mam takie pytanie, aktualnie uczę się C# i wpadła mi w oko jedna rzecz. Dokładnie używasz typów niejawnych, z tego co przeczytałem z książki używanie tego słowa w momencie kiedy dokładnie wiemy co chcemy uzyskać jest jest przykładem niezbyt estetycznego kodu oraz złych praktyk programistycznych. Mógłbyś się odwołać do tego komentarza?
komentarz 15 kwietnia 2020 przez cSharpKazik Użytkownik (840 p.)

Co masz na myśli, pisząc typ niejawny? smiley

Masz na myśli var? Jeżeli tak, to używanie var'ów jest dużo bardziej zalecaną praktyką. Dzięki nim, kod jest bardziej czytelny, można szybciej pisać kod, nie musisz za każdym razem pisać dwukrotnie jakichś długich deklaracji, wystarczy użyć krótkie słowa var.

Sam zobacz co jest bardziej przejrzyste i szybsze do napisania:

List<PrzykladowaKlasa> simpleList = new List<PrzykladowaKlasa>();
var simpleList = new List<PrzykladowaKlasa>();

 

komentarz 15 kwietnia 2020 przez Adrian1999 Nałogowiec (34,570 p.)

Odniosę się do książki, na mocy prawa cytatu. Książka "Język C# 6.0 i platforma .NET 4.6" Autora Troelsen Andrew oraz Japikse Phiplip. "Po omówieniu składni deklarowania zmiennych lokalnych z typizacją niejawną na usta ciśnie się pytanie: kiedy używać tej konstrukcji? Przede wszystkim używanie słowa kluczowego var do deklarowania zmiennych lokalnych dla samej sztuki niewiele daje. Na dodatek może wprowadzać w błąd inne osoby czytające kod, ponieważ utrudnia szybkie ustalenie bazowego typu danych, a tym samym zrozumienie ogólnego przeznaczenia zmiennej. Jeśli wiesz, że potrzebny jest typ int, to zadeklaruj int!...

Nadużywanie typizacji niejawnej (ze słowem kluczowym var) w kodzie produkcyjnym jest przez większość programistów odbierane jako oznaka złego stylu."

Jestem aktualnie w trakcie czytania tej książki, i odwołuje się ona na argument "przez większość programistów", jestem dlatego ciekawy zdań innych programistów. 

komentarz 15 kwietnia 2020 przez cSharpKazik Użytkownik (840 p.)

A to ciekawe smiley

Ja się z tym zdaniem nie zgadzam, a swoje argumenty podałem wyżej. Skoro piszą, że przez większość programistów var jest odbierane jako oznaka złego stylu, to chętnie poznam te badania.

komentarz 15 kwietnia 2020 przez Adrian1999 Nałogowiec (34,570 p.)
Hmmm, jak napisałem ja jedynie odwołuje się na książkę, z tego względu że ja się uczę. I fajnie jest takie sytuacje omawiać. Z ciekawości stworzę aż oddzielny wątek, w którym poddam dyskusji ten temat, za moment dodam tutaj link. Nie jestem osobą która będzie jakoś dyskutować, ja jestem po prostu początkującym którego to zaciekawiło.

1 odpowiedź

0 głosów
odpowiedź 15 kwietnia 2020 przez Ehlert Ekspert (212,670 p.)

Całkiem fajny filmik. Mam małe ale. 

IMHO ostatni przykład nie jest za bardzo adekwatny. Zmieniłeś kod w klasie która była zależnością kodu testowanego. W takim wypadku testy przejdą! Czemu miałyby nie przechodzić. Powinien być oddzielny test sprawdzający walidator.

Co do tamtego serwisu to potrzebujemy dwóch testów, bo masz dwa edge-case'y. Jeden test jednostkowy zabezpiecza przed regresją kodu w konkretnej testowanej klasie. Ty wykorzystując gotową implementację sprawiasz że test przestaje być jednostkowy.

Poza tym wideo jak najbardziej na yes​​​​​. 

komentarz 15 kwietnia 2020 przez cSharpKazik Użytkownik (840 p.)

Właśnie o to chodzi, że to nie jest zależność kodu testowanego smiley Trzeba się zastanowić do czego służą mocki. Służą one do tego by pozbywać się tylko zewnętrznych zależności z testów, takich jak bazy danych, web serwisy, pliki itp Nie powinniśmy w testach mockować wszystkiego. Później w naszych testach jest pełno mocków, nawet widziałem już takie testy gdzie w act była uruchamiana zamockowana wcześniej metoda smiley

komentarz 15 kwietnia 2020 przez Ehlert Ekspert (212,670 p.)

Ustalmy kilka faktów:

Test który napisałeś z mockiem był dobry. Co prawda brakowało obsługi drugiego edge-case'u ale ok. Dlaczego? Ponieważ zabezpieczał przed regresją kod testowany. Testowany był serwis od produktów. (pomijam niezbyt precyzyjną nomenklaturę ProductService) ​. Dlaczego testując tą klasę oczekujesz że testy nie przejdą kiedy zmieniasz kod w zupełnie innej klasie?

Testy powinny być jak najbardziej minimalistyczne i angażować jak najmniej elementów środowiska i implementacji do ich uruchomienia. Zastanów się czy wartość pola Quantity dla metody Add ma jakieś znaczenie. Nie ma wcale. Jest to szczegół implementacyjny wykorzystywany przez validator.

Twój test powinien skupiać się na tym ifie. Jeśli validator zwraca false, oczekujesz wyjątku, inaczej robisz asercję. To jest istota testu.

Zauważ że Twój test naraża Cię na trudne do znalezienia błędy. Zmieniają się założenia biznesowe i validator sprawdza jeszcze kilka cech produktu. I jest problem. Testy nie przechodzą, a kod ProductServiceu się nie zmienił.

Co do zależności. Nie można tutaj mówić o czymś konkretnym, ponieważ co poziom to zależność oznacza co innego. Dla testów jednostkowych będzie to jakaś inna klasa. Dla testów funkcjonalnych baza  danych. Dla testów e2e może to być api Facebooka.

komentarz 16 kwietnia 2020 przez Ehlert Ekspert (212,670 p.)

Mogę liczyć na odpowiedź, bo jestem bardzo ciekawy. wink

komentarz 16 kwietnia 2020 przez cSharpKazik Użytkownik (840 p.)

Wydaje mi się, że już napisałem wszystko w poprzedniej wiadomości. Ogólnie mamy inne podejście do testów, moje się sprawdza u mnie, Twoje prawdopodobnie sprawdza się u Ciebie i o to w tym wszystkim chodzi smiley

Ja podchodzę do mocków jak do czegoś sztucznego, zakłamanego, to znaczy jest to taka atrapa, której należy używać jak najmniej w testach, ponieważ wtedy nie testujemy naszego kodu. Dodatkowo przez mocki testy są mniej czytelne, co widać również na przykładzie, który przedstawiłem. Im mniej mocków w teście, tym lepiej. Jednak jeżeli chcemy testować metody, które mają zewnętrzne zależności takie jak baza danych itp to nie mogę tego testować jednostkowo bez użycia mocków, ale tylko w takich sytuacjach pozwalam sobie na użycie mocków. Wtedy muszę użyć mocków, ponieważ chcę żeby moje testy były szybkie, niezawodne, nie potrzebowały konfigurowacji odpowiedniego środowiska.

komentarz 16 kwietnia 2020 przez Ehlert Ekspert (212,670 p.)

Twoje prawdopodobnie sprawdza się u Ciebie i o to w tym wszystkim chodzi

Chcę zrozumieć Twój sposób myślenia. Podejście do testów jednostkowych określone przez guru w naszej branży jest dość powszechnie znane, więc chcę zweryfikować, może ja coś źle rozumuję. Napisz proszę jak przetestujesz taki kontroler. Dałem Java, ale to pseudokod. 

final class UserHttpController {

    private UserFacebookApiInterface facebookApi;
    private FacebookDataSerializerInterface fbDataSerializer;

    public UserHttpController(
        UserFacebookApiInterface facebookApi,
        FacebookDataSerializerInterface fbDataSerializer
    ) {
        this.facebookApi = facebookApi;
        this.fbDataSerializer = fbDataSerializer;
    }

    @RequestMap(':id', User::class, 'user')
    public Data getFacebookDataForUser(User user) {
        if (!this.facebookApi.isUserAvailable(user)) {
            return null;
        }

        Data userFacebookData = this.facebookApi.getDataForUser(user.facebookId);
        return this.fbDataSerializer(userFacebookData);
    }
}
komentarz 20 kwietnia 2020 przez cSharpKazik Użytkownik (840 p.)

Przerobiłem sobie kod na C#

 

public class UserHttpController
    {
        private IUserFacebookApi _facebookApi;
        private IFacebookDataSerializer _fbDataSerializer;

        public UserHttpController(IUserFacebookApi facebookApi, 
            IFacebookDataSerializer fbDataSerializer)
        {
            _facebookApi = facebookApi;
            _fbDataSerializer = fbDataSerializer;
        }

        public Data GetFacebookDataForUser(User user)
        {
            if (!_facebookApi.IsUserAvailable(user))
                return null;

            var userFacebookData = _facebookApi.GetDataForUser(user.FacebookId);
            return _fbDataSerializer.Deserialize(userFacebookData);
        }
    }

 

Oczywiście przydałoby się więcej informacji o kodzie, który zamieściłeś, ale napisałem 2 przykładowe przypadki testowe (bez sprawdzania rzucania wyjątków przez te metody), mogą wyglądać na przykład tak:

public class UserHttpControllerTests
    {
        [Test]
        public void GetFacebookDataForUser_WhenUserAvailable_ShouldReturnDeserializedData()
        {
            var mockUserFacebookApi = new Mock<IUserFacebookApi>();
            var facebookSerializer = new FacebookDataSerializer();
            var user = new User { FacebookId = 1 };
            var expectedData = new Data { Name = "1" };

            mockUserFacebookApi
                .Setup(x => x.IsUserAvailable(user)).Returns(true);

            mockUserFacebookApi
                .Setup(x => x.GetDataForUser(user.FacebookId))
                .Returns("serializeData:1");            

            var userHttpController = new UserHttpController(
                mockUserFacebookApi.Object,
                facebookSerializer);

            var result = userHttpController.GetFacebookDataForUser(user);

            result.Name.Should().Be(expectedData.Name);
        }

        [Test]
        public void GetFacebookDataForUser_WhenUserUnavailable_ShouldReturnNull()
        {
            var mockUserFacebookApi = new Mock<IUserFacebookApi>();
            var facebookSerializer = new FacebookDataSerializer();
            var user = new User();

            mockUserFacebookApi
                .Setup(x => x.IsUserAvailable(user)).Returns(null);           

            var userHttpController = new UserHttpController(
                mockUserFacebookApi.Object,
                facebookSerializer);

            var result = userHttpController.GetFacebookDataForUser(user);

            result.Should().BeNull();
        }
    }

Czyli testuję tutaj 2 przypadki. Po pierwsze, czy jeżeli użytkownik nie będzie dostępny to zostanie zwrócony null. Oraz czy jeżeli będzie dostępny to zostanie zwrócony oczekiwany wynik. 

Jeżeli chciałbyś przetestować tutaj metodę GetDataForUser, to musiałbyś jeszcze napisać testy integracyjne.

komentarz 20 kwietnia 2020 przez Ehlert Ekspert (212,670 p.)

Z tego co się doczytałem u Martina Fowlera, to używanie żywej implementacji było charakterystyczne dla czasów kiedy frameworki do testowania nie dawały możliwości mockowania, tworzenia doubli oraz spy'ów. Określenie unit odnosiło się do spójnej jednostki logicznej której scope wychodził poza klasę. Wtedy rzeczywiście mogłoby mieć to sens, pod warunkiem że klasy w takiej jednostce mają silne zależności. W praktyce jeśli dzisiaj piszesz dobry kod trzymając się SOLIDu, KISS itp itd do jednej spójnej jednostki logicznej nie ma sensu angażować więcej niż jedną klasę.

Sposób w jaki Ty testujesz jest moim zdaniem błędny.

Zmienia się kod FacebookDataSerializer bezpośrednio użyty w teście i wywala się test kontrolera mimo że kontroler nie został zmieniony. Nie wiem z jakiego powodu ustawiasz pole name, jest to szczegół implementacyjny. Tym bardziej z czapy jest ta asercja, skąd pewności że wartość pola name się nie zmieni? Dla tego testu to coś całkowicie nieistotnego.

Zwróć również proszę uwagę, że kontroler wykorzystuje interfejs. Z wykorzystaniem dependency injection łatwa podmiana wstrzykiwanego kodu również może powodować problemy w zaproponowanym przez Ciebie teście.

komentarz 20 kwietnia 2020 przez Ehlert Ekspert (212,670 p.)

Nie chcę abyś pomyślał, że na siłę chcę Ci wytknąć niekompetencję. Sam mam również pewne doświadczenia z takimi podejściami do tematu gdzie później w niektórych projektach połowa unitów wypadała; nie ze względu na to, że nikt ich nie poprawiał. Powodem były właśnie tego typu praktyki, które czyniły testy bardzo ciężkimi w utrzymaniu.

Wiem że to inna technologia, ale jeśli chcesz obejrzyj ten film https://youtu.be/DVRJNPnezIk

Jeśli nie dla siebie, to może przynajmniej dla swoich klientów. Nie wiem co chcesz im dokładnie przekazywać, ale pieniądz nie jest mały wink powodzenia i pozdrawiam

komentarz 20 kwietnia 2020 przez cSharpKazik Użytkownik (840 p.)
Nie widziałem nigdy zdania, o którym piszesz odnośnie Uncle Boba, a nawet jeśli, to Martin Fowler kiedyś powiedział również, że pokrycie kodu testami powinno być równe 100%, a z tym się chyba nie zgodzisz? Ogólnie testy jakie ja pisze, a Ty uważasz za błędne, są wynikiem mojej praktyki programistycznej, oraz odbytych szkoleń i przeczytanych książek na temat testów jednostkowych. U mnie i osób, które znam takie testy się sprawdzają. Wprowadzanie mocków do testów moim zdaniem zawsze wprowadza nieład do testów, wprowadza zakłamanie do testów. Dodatkowo każda nowa inicjalizacja i setup mocka wprowadza kilka nowych linii, przez testy też są bardziej skomplikowane i mniej czytelne. Jeżeli chcesz wszystko mockować to w Twoich aplikacjach musi również być mnóstwo interfejsów, dla każdej nowej klasy musisz mieć interfejs, często tylko dlatego żeby w testach sobie daną klasę zamockować. Jeżeli każdą taką zależność wstrzykujesz przez parametr konstruktora, to są one przeładowane. Zawsze staram się mockować, tylko to co jest obligatoryjne, to znaczy zewnętrzne zależności. Oczywiście są wyjątki od tej reguły, ale nie dotyczy to tego przypadku.

Dzięki za film, bardzo chętnie obejrzę w wolnej chwili.

Pozdrawiam ;)

Podobne pytania

0 głosów
0 odpowiedzi 148 wizyt
0 głosów
1 odpowiedź 253 wizyt
0 głosów
1 odpowiedź 161 wizyt
pytanie zadane 20 kwietnia 2020 w PHP przez michal_php Stary wyjadacz (13,700 p.)

92,551 zapytań

141,393 odpowiedzi

319,523 komentarzy

61,936 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...