Skąd wiesz, co testować podczas pisania testów jednostkowych? [Zamknięte]


128

Używając C #, potrzebuję klasy o nazwie, Userktóra ma nazwę użytkownika, hasło, aktywną flagę, imię, nazwisko, imię i nazwisko itp.

Powinny istnieć metody uwierzytelniania i zapisywania użytkownika. Czy po prostu napiszę test dla metod? I czy muszę się martwić o testowanie właściwości, skoro są one getterami i ustawiającymi .Net?


Ten post pomoże w zawężeniu szerszego pytania: earnestengineer.blogspot.com/2018/03/… Możesz skorzystać z tych wskazówek, aby skupić się na swoim pytaniu
Lindsay Morsillo.

Pamiętaj, że hasła nie powinny być przechowywane w postaci zwykłego tekstu.
Pan Anderson

Odpowiedzi:


133

Wiele świetnych odpowiedzi na to pytanie dotyczy również mojego pytania: „ Początek TDD - Wyzwania? Rozwiązania? Zalecenia?

Chciałbym również polecić zajrzenie do mojego wpisu na blogu (który był częściowo zainspirowany moim pytaniem), otrzymałem kilka dobrych opinii na ten temat. Mianowicie:

Nie wiem od czego zacząć?

  • Rozpocząć na nowo. Myśl o pisaniu testów tylko wtedy, gdy piszesz nowy kod. Może to być przeróbka starego kodu lub zupełnie nowa funkcja.
  • Zacznij prosto. Nie uciekaj i nie próbuj zagłębić się w ramy testowe, a także nie być typowym dla TDD. Debug.Assert działa dobrze. Użyj go jako punktu wyjścia. Nie psuje twojego projektu ani nie tworzy zależności.
  • Zacznij pozytywnie. Próbujesz ulepszyć swoje rzemiosło, dobrze się z tym czujesz. Widziałem wielu programistów, którzy są szczęśliwi, że stagnują i nie próbują nowych rzeczy, aby się doskonalić. Postępujesz właściwie, pamiętaj o tym, a to pomoże ci powstrzymać się od poddania się.
  • Zacznij gotowy na wyzwanie. Rozpoczęcie testowania jest dość trudne. Spodziewaj się wyzwania, ale pamiętaj - wyzwania można pokonać.

Testuj tylko pod kątem tego, czego oczekujesz

Miałem prawdziwe problemy, kiedy zaczynałem, ponieważ ciągle siedziałem tam, próbując znaleźć każdy możliwy problem, który mógł się pojawić, a następnie próbowałem go przetestować i naprawić. To szybki sposób na ból głowy. Testowanie powinno być prawdziwym procesem YAGNI. Jeśli wiesz, że jest problem, napisz test. W przeciwnym razie nie przejmuj się.

Przetestuj tylko jedną rzecz

Każdy przypadek testowy powinien zawsze testować tylko jedną rzecz. Jeśli kiedykolwiek zdarzy Ci się umieścić „i” w nazwie przypadku testowego, robisz coś nie tak.

Mam nadzieję, że to oznacza, że ​​możemy odejść od „pobierających i ustawiających” :)


2
„Jeśli wiesz, że jest problem, napisz test. W przeciwnym razie nie przejmuj się”. Nie zgodziłbym się ze sposobem sformułowania tego. Miałem wrażenie, że testy jednostkowe powinny obejmować wszystkie możliwe ścieżki wykonania.
Corin Blaikie,

3
Chociaż niektórzy mogą popierać takie rzeczy, ja osobiście tego nie robię. Dobre 90% mojego bólu głowy pochodziło po prostu z robienia „wszystkiego”. Mówię o testowaniu tego, czego się spodziewasz (abyś wiedział, że otrzymujesz właściwe wartości), ale nie próbuj tego wszystkiego zrozumieć. YAGNI.
Rob Cooper,

4
Ja też opowiadam się za podejściem „przetestuj swoje błędy”. Gdybyśmy wszyscy mieli nieskończony czas i cierpliwość, przetestowalibyśmy każdą możliwą ścieżkę wykonania. Ale nie robimy tego, więc musisz poświęcić swój wysiłek tam, gdzie przyniesie to największy efekt.
Schwern

63

Przetestuj swój kod, a nie język.

Test jednostkowy, taki jak:

Integer i = new Integer(7);
assert (i.instanceOf(integer));

jest użyteczne tylko wtedy, gdy piszesz kompilator i istnieje niezerowa szansa, że ​​twoja instanceofmetoda nie działa.

Nie testuj rzeczy, na których egzekwowaniu możesz polegać. W twoim przypadku skupiłbym się na twoich metodach uwierzytelniania i zapisywania - i napisałbym testy, które upewniłyby się, że mogą z wdziękiem obsługiwać wartości null w dowolnym lub wszystkich tych polach.


1
Dobra uwaga na temat „Nie testuj frameworka” - coś, co też załapałem, gdy byłeś nowy. +1'ed :)
Rob Cooper,

38

To wciągnęło mnie w testy jednostkowe i bardzo mnie ucieszyło

Właśnie zaczęliśmy przeprowadzać testy jednostkowe. Od dawna wiedziałem, że dobrze będzie zacząć, ale nie miałem pojęcia, jak zacząć i co ważniejsze, co przetestować.

Następnie musieliśmy przepisać ważny fragment kodu w naszym programie księgowym. Ta część była bardzo złożona, ponieważ obejmowała wiele różnych scenariuszy. Część o której mówię to sposób na opłacenie faktur sprzedaży i / lub zakupu już wprowadzonych do systemu księgowego.

Po prostu nie wiedziałem, jak zacząć go kodować, ponieważ było tak wiele różnych opcji płatności. Faktura może wynosić 100 USD, ale klient przelał tylko 99 USD. Być może wysłałeś faktury sprzedaży do klienta, ale dokonałeś również zakupu od tego klienta. Więc sprzedałeś go za 300 $, ale kupiłeś za 100 $. Możesz oczekiwać, że Twój klient zapłaci Ci 200 USD, aby uregulować saldo. A co, jeśli sprzedałeś za 500 USD, ale klient zapłaci Ci tylko 250 USD?

Więc miałem bardzo złożony problem do rozwiązania z wieloma możliwościami, że jeden scenariusz działałby idealnie, ale byłby błędny na innym typie kombinacji faktury / płatności.

Tutaj na ratunek przyszedł test jednostkowy.

Zacząłem pisać (w kodzie testowym) sposób tworzenia listy faktur, zarówno sprzedaży, jak i zakupów. Następnie napisałem drugą metodę tworzenia faktycznej płatności. Zwykle użytkownik wprowadzałby te informacje za pośrednictwem interfejsu użytkownika.

Następnie stworzyłem pierwszą metodę TestMethod, testując bardzo prostą płatność za jedną fakturę bez żadnych rabatów. Cała akcja w systemie miałaby miejsce, gdyby płatność bankowa została zapisana w bazie danych. Jak widać, utworzyłem fakturę, utworzyłem płatność (transakcję bankową) i zapisałem transakcję na dysku. W moich potwierdzeniach umieszczam, jakie powinny być prawidłowe liczby kończące się w transakcji bankowej i na połączonej fakturze. Po transakcji sprawdzam liczbę płatności, kwoty płatności, kwotę rabatu i saldo faktury.

Po zakończeniu testu szedłem do bazy danych i dwukrotnie sprawdzałem, czy jest tam to, czego się spodziewałem.

Po napisaniu testu zacząłem kodować metodę płatności (część klasy BankHeader). W kodowaniu zająłem się tylko kodem, aby wykonać pierwszy test. Nie myślałem jeszcze o innych, bardziej złożonych scenariuszach.

Uruchomiłem pierwszy test, naprawiłem mały błąd, dopóki mój test nie przeszedł.

Potem zacząłem pisać drugi test, tym razem z rabatem od płatności. Po napisaniu testu zmodyfikowałem metodę płatności, aby obsługiwała rabaty.

Testując poprawność z rabatem płatności, testowałem również prostą płatność. Oczywiście oba testy powinny przejść pomyślnie.

Potem przeszedłem do bardziej złożonych scenariuszy.

1) Pomyśl o nowym scenariuszu

2) Napisz test dla tego scenariusza

3) Uruchom ten pojedynczy test, aby sprawdzić, czy przejdzie

4) Gdyby tak się nie stało, debugowałbym i modyfikowałbym kod, dopóki nie przejdzie.

5) Modyfikując kod, wykonywałem wszystkie testy

W ten sposób udało mi się stworzyć moją bardzo złożoną metodę płatności. Bez testów jednostkowych nie wiedziałem, jak rozpocząć kodowanie, problem wydawał się przytłaczający. Dzięki testowaniu mogłem zacząć od prostej metody i rozszerzać ją krok po kroku, mając pewność, że prostsze scenariusze nadal będą działać.

Jestem przekonany, że zastosowanie testów jednostkowych zaoszczędziło mi kilku dni (lub tygodni) kodowania i mniej więcej gwarantuje poprawność mojej metody.

Jeśli później pomyślę o nowym scenariuszu, mogę po prostu dodać go do testów, aby sprawdzić, czy działa, czy nie. Jeśli nie, mogę zmodyfikować kod, ale nadal mam pewność, że inne scenariusze nadal działają poprawnie. Pozwoli to zaoszczędzić dni i dni w fazie konserwacji i naprawiania błędów.

Tak, nawet przetestowany kod może nadal zawierać błędy, jeśli użytkownik robi rzeczy, o których nie pomyślałeś lub uniemożliwiłeś mu zrobienie

Poniżej znajduje się tylko kilka testów, które utworzyłem, aby przetestować moją metodę płatności.

public class TestPayments
{
    InvoiceDiaryHeader invoiceHeader = null;
    InvoiceDiaryDetail invoiceDetail = null;
    BankCashDiaryHeader bankHeader = null;
    BankCashDiaryDetail bankDetail = null;



    public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
    {
        ......
        ......
    }

    public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
    {
       ......
       ......
       ......
    }


    [TestMethod]
    public void TestSingleSalesPaymentNoDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 1, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSingleSalesPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 2, "01-09-2008"));
        bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
    }

    [TestMethod]
    [ExpectedException(typeof(ApplicationException))]
    public void TestDuplicateInvoiceNumber()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("100", true, 2, "01-09-2008"));
        list.Add(CreateSales("200", true, 2, "01-09-2008"));

        bankHeader = CreateMultiplePayments(list, 3, 300, 0);
        bankHeader.Save();
        Assert.Fail("expected an ApplicationException");
    }

    [TestMethod]
    public void TestMultipleSalesPaymentWithPaymentDiscount()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("119", true, 11, "01-09-2008"));
        list.Add(CreateSales("400", true, 12, "02-09-2008"));
        list.Add(CreateSales("600", true, 13, "03-09-2008"));
        list.Add(CreateSales("25,40", true, 14, "04-09-2008"));

        bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
        Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);

        Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);

        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
    }

    [TestMethod]
    public void TestSettlement()
    {
        IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
        list.Add(CreateSales("300", true, 43, "01-09-2008")); //Sales
        list.Add(CreateSales("100", false, 6453, "02-09-2008")); //Purchase

        bankHeader = CreateMultiplePayments(list, 22, 200, 0);
        bankHeader.Save();

        Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
        Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
        Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
        Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
        Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
    }

1
Znalazłem błąd w twoim teście jednostkowym! Powtarzasz tę linię zamiast dodawać 2 do jednej z nich:Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
Ryan Peschel

2
Mówisz: „Po uruchomieniu testu szedłem do bazy danych i dwukrotnie sprawdzałem, czy jest tam to, czego się spodziewałem”. To jest dobry przykład testu integracji między komponentami twojego systemu - nie jest to izolowany test jednostkowy pojedynczego fragmentu kodu.
JTech

2
Złamałeś również zasadę więcej niż jednego potwierdzenia na test.
Steve

13

Jeśli naprawdę są trywialne, nie przejmuj się testowaniem. Np. Jeśli są zaimplementowane w ten sposób;

public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
}

Jeśli z drugiej strony robisz coś sprytnego (np. Szyfrowanie i odszyfrowywanie hasła w programie pobierającym / ustawiającym), przeprowadź test.


10

Zasada jest taka, że ​​musisz przetestować każdy fragment logiki, który napiszesz. Jeśli zaimplementowałeś jakąś konkretną funkcjonalność w getterach i setterach, myślę, że warto je przetestować. Jeśli przypisują wartości tylko do niektórych pól prywatnych, nie przejmuj się.


6

To pytanie wydaje się być pytaniem, gdzie można nakreślić granicę, które metody są testowane, a które nie.

Metody ustawiające i pobierające do przypisywania wartości zostały stworzone z myślą o spójności i przyszłym wzroście oraz z przewidywaniem, że po pewnym czasie seter / getter może przekształcić się w bardziej złożone operacje. Sensowne byłoby wprowadzenie testów jednostkowych tych metod, również ze względu na spójność i przyszły rozwój.

Podstawowym celem jest niezawodność kodu, zwłaszcza podczas wprowadzania zmian w celu dodania dodatkowych funkcji. Nie wiem, czy ktokolwiek kiedykolwiek został zwolniony za włączenie seterów / pobierających do metodologii testowania, ale jestem pewien, że są ludzie, którzy chcieliby przetestować metody, które jako ostatnie byli świadomi lub pamiętają, były prostymi opakowaniami ustawiania / pobierania, ale to nie było dłuższa sprawa.

Być może inny członek zespołu rozszerzył metody set / get o logikę, która teraz wymaga przetestowania, ale nie stworzyła testów. Ale teraz Twój kod wywołuje te metody i nie zdajesz sobie sprawy, że uległy zmianie i wymagają dogłębnych testów, a testy, które wykonujesz podczas programowania i kontroli jakości, nie wywołują defektu, ale prawdziwe dane biznesowe z pierwszego dnia wydania tak. uruchomić go.

Dwaj koledzy z drużyny będą teraz debatować nad tym, kto upuścił piłkę i nie przeszedł testów jednostkowych, gdy zestaw / zostanie przekształcony w celu uwzględnienia logiki, która może zawieść, ale nie jest objęta testem jednostkowym. Kolega z zespołu, który pierwotnie napisał zestaw / dostaje, będzie miał łatwiejsze wyjście z tego czystego, jeśli testy zostaną wdrożone od pierwszego dnia na prostym zestawie / otrzymach.

Uważam, że kilka minut „zmarnowanego” czasu na pokrycie WSZYSTKICH metod testami jednostkowymi, nawet trywialnymi, może zaoszczędzić dni bólu głowy i utraty pieniędzy / reputacji firmy oraz czyjejś pracy.

A fakt, że owinąłeś trywialne metody testami jednostkowymi, może być zauważony przez młodszego kolegę z zespołu, gdy zmienią trywialne metody na nietrywialne i skłonią ich do aktualizacji testu, a teraz nikt nie ma kłopotów, ponieważ defekt został powstrzymany od osiągnięcia produkcji.

Sposób, w jaki kodujemy i dyscyplina, którą widać w naszym kodzie, mogą pomóc innym.


4

Kolejna odpowiedź kanoniczna. To, jak sądzę, od Rona Jeffriesa:

Przetestuj tylko kod, na którym chcesz działać.


3

Testowanie kodu standardowego jest stratą czasu, ale jak mówi Slavo, jeśli dodasz efekt uboczny do swoich metod pobierających / ustawiających, powinieneś napisać test towarzyszący tej funkcjonalności.

Jeśli tworzysz programowanie sterowane testami, powinieneś najpierw napisać kontrakt (np. Interfejs), a następnie napisać test (y), aby wypróbować ten interfejs, który dokumentuje oczekiwane wyniki / zachowanie. Następnie napisz swoje metody samodzielnie, bez dotykania kodu w testach jednostkowych. Na koniec pobierz narzędzie do pokrycia kodu i upewnij się, że testy sprawdzają wszystkie ścieżki logiki w kodzie.


3

Naprawdę trywialny kod, taki jak metody pobierające i ustawiające, które nie mają żadnego dodatkowego zachowania poza ustawieniem pola prywatnego, to przesada. W 3.0 C # ma nawet cukier składniowy, w którym kompilator zajmuje się polem prywatnym, więc nie musisz tego programować.

Zwykle piszę wiele bardzo prostych testów sprawdzających zachowanie, jakiego oczekuję od moich zajęć. Nawet jeśli to proste rzeczy, takie jak dodanie dwóch liczb. Dużo przełączam między pisaniem prostego testu a pisaniem kilku wierszy kodu. Powodem tego jest to, że mogę wtedy zmieniać kod bez obawy, że zepsuję rzeczy, o których nie pomyślałem.


Cieszę się, że dobrze zrozumiałeś zasadę KISS. Często mam testy, które są dosłownie jak 2-3 linie kodu, naprawdę małe, proste testy. Łatwy do nauczenia i trudny do złamania :) + 1'ed
Rob Cooper,

3

Powinieneś wszystko przetestować. W tej chwili masz metody pobierające i ustawiające, ale któregoś dnia możesz je nieco zmienić, na przykład walidację lub coś innego. Testy, które napiszesz dzisiaj, zostaną jutro wykorzystane, aby upewnić się, że wszystko działa jak zwykle. Pisząc test, należy zapomnieć o rozważaniach typu „teraz to trywialne”. W kontekście zwinnym lub opartym na testach powinieneś testować zakładając przyszłą refaktoryzację. Czy próbowałeś też wprowadzić naprawdę dziwne wartości, takie jak ekstremalnie długie ciągi lub inne „złe” treści? Cóż, nie powinieneś ... nigdy zakładać, jak bardzo Twój kod może zostać wykorzystany w przyszłości.

Ogólnie uważam, że pisanie obszernych testów użytkowników jest z jednej strony wyczerpujące. Z drugiej strony, chociaż zawsze daje nieoceniony wgląd w to, jak powinna działać Twoja aplikacja i pomaga odrzucić łatwe (i fałszywe) założenia (takie jak: nazwa użytkownika zawsze będzie miała mniej niż 1000 znaków).


3

W przypadku prostych modułów, które mogą znaleźć się w zestawie narzędzi lub w projekcie typu open source, należy przetestować jak najwięcej, w tym trywialne metody pobierające i ustawiające. Należy pamiętać, że generowanie testu jednostkowego podczas pisania określonego modułu jest dość proste i proste. Dodawanie metod pobierających i ustawiających to minimalny kod i można je obsługiwać bez większego zastanowienia. Jednak po umieszczeniu kodu w większym systemie ten dodatkowy wysiłek może uchronić Cię przed zmianami w systemie bazowym, takimi jak zmiany typu w klasie bazowej. Testowanie wszystkiego to najlepszy sposób na uzyskanie kompletnej regresji.


2

Pisanie testów jednostkowych dla metod pobierających i ustawiających nie zaszkodzi. W tej chwili mogą po prostu wykonywać operacje pobierania / ustawiania pól pod maską, ale w przyszłości możesz mieć logikę walidacji lub zależności między właściwościami, które należy przetestować. Łatwiej jest napisać to teraz, kiedy o tym myślisz, niż pamiętając o modernizacji, jeśli ten czas kiedykolwiek nadejdzie.


cóż, jeśli twoje metody pobierające / ustawiające potrzebują testów jednostkowych, musi być z nimi skojarzona jakaś logika, więc oznacza to, że logika musi być w nich zapisana, jeśli nie mają żadnej logiki, nie trzeba pisać testów jednostkowych.
Pop Catalin,

2
Chodzi o to, że logikę można do nich dodać później.
LegendLength

2

w ogóle, gdy metoda jest zdefiniowana tylko dla pewnych wartości, test dla wartości na i powyżej tej granicy, co jest dopuszczalne. Innymi słowy, upewnij się, że Twoja metoda robi to, co powinna, ale nic więcej . Jest to ważne, ponieważ kiedy ponosisz porażkę, chcesz zawodzić wcześnie.

W hierarchiach dziedziczenia upewnij się, że wykonałeś testy zgodności z LSP .

Testowanie domyślnych metod pobierających i ustawiających nie wydaje mi się zbyt przydatne, chyba że planujesz później przeprowadzić weryfikację.


2

cóż, jeśli myślisz, że może się zepsuć, napisz test. Zwykle nie testuję setera / gettera, ale powiedzmy, że tworzysz go dla User.Name, które łączą imię i nazwisko, napisałbym test, więc jeśli ktoś zmieni kolejność na nazwisko i imię, przynajmniej by wiedział zmienił coś, co było testowane.


2

Kanoniczna odpowiedź brzmi: „przetestuj wszystko, co może się zepsuć”. Jeśli masz pewność, że właściwości się nie zepsują, nie testuj ich.

A kiedy okaże się, że coś się zepsuło (znajdziesz błąd), oczywiście oznacza to, że musisz to przetestować. Napisz test, aby odtworzyć błąd, obejrzyj, jak się nie udaje, a następnie napraw błąd, a następnie zobacz przebieg testu.


1

Jak rozumiem testy jednostkowe w kontekście zwinnego programowania, Mike, tak, musisz przetestować metody pobierające i ustawiające (zakładając, że są one publicznie widoczne). Cała koncepcja testowania jednostkowego polega na testowaniu jednostki oprogramowania, która w tym przypadku jest klasą, jako czarnej skrzynki . Ponieważ metody pobierające i ustawiające są widoczne na zewnątrz, należy je przetestować wraz z uwierzytelnianiem i zapisywaniem.


1

Jeśli metody uwierzytelniania i zapisywania używają właściwości, testy pośrednio dotkną właściwości. Tak długo, jak właściwości zapewniają dostęp do danych, jawne testowanie nie powinno być konieczne (chyba że zamierzasz uzyskać 100% pokrycie).


1

Testowałbym twoje gettery i setery. W zależności od tego, kto pisze kod, niektórzy ludzie zmieniają znaczenie metod pobierających / ustawiających. Widziałem inicjalizację zmiennych i inną walidację jako część metod pobierających. Aby przetestować tego rodzaju rzeczy, chciałbyś, aby testy jednostkowe obejmowały ten kod jawnie.


1

Osobiście „przetestowałbym wszystko, co może się zepsuć”, a prosty getter (lub nawet lepsze właściwości auto) się nie zepsuje. Nigdy nie miałem niepowodzenia prostej instrukcji powrotu i dlatego nigdy nie miałem dla nich testu. Jeśli gettery mają w sobie obliczenia lub jakąś inną formę instrukcji, z pewnością dodałbym dla nich testy.

Osobiście używam Moq jako makiety struktury obiektów, a następnie sprawdzam, czy mój obiekt wywołuje otaczające obiekty tak, jak powinien.


1

Musisz pokryć wykonanie każdej metody klasy za pomocą UT i sprawdzić wartość zwracaną przez metodę. Obejmuje to metody pobierające i ustawiające, szczególnie w przypadku, gdy składowe (właściwości) są klasami złożonymi, które wymagają dużej alokacji pamięci podczas ich inicjalizacji. Wywołaj setera za pomocą na przykład bardzo dużego ciągu (lub czegoś z greckimi symbolami) i sprawdź, czy wynik jest poprawny (nie obcięty, kodowanie jest dobre itp.)

W przypadku prostych liczb całkowitych, które również mają zastosowanie - co się stanie, jeśli podasz long zamiast liczby całkowitej? To jest powód, dla którego piszesz UT :)


1

Nie testowałbym rzeczywistego ustawienia właściwości. Byłbym bardziej zaniepokojony tym, w jaki sposób te nieruchomości są zaludniane przez konsumentów i czym je zapełniają. Przy każdym testowaniu musisz rozważyć ryzyko z czasem / kosztem testowania.


1

Powinieneś testować „każdy nietrywialny blok kodu” używając testów jednostkowych, o ile to możliwe.

Jeśli twoje właściwości są trywialne i jest mało prawdopodobne, że ktoś wprowadzi do nich błąd, to powinno być bezpieczne, aby nie testować ich jednostkowo.

Twoje metody Authenticate () i Save () wyglądają na dobrych kandydatów do testów.


1

Najlepiej byłoby, gdybyś zrobił testy jednostkowe podczas pisania klasy. Oto, jak powinieneś to zrobić, korzystając z programowania sterowanego testami. Dodajesz testy podczas implementacji każdego punktu funkcji, upewniając się, że również przypadki brzegowe są objęte testem.

Pisanie testów później jest dużo bardziej bolesne, ale wykonalne.

Oto, co zrobiłbym na twoim stanowisku:

  1. Napisz podstawowy zestaw testów testujących podstawową funkcję.
  2. Pobierz NCover i uruchom go na swoich testach. W tym momencie pokrycie testu prawdopodobnie wyniesie około 50%.
  3. Dodawaj testy, które obejmują przypadki skrajne, aż uzyskasz pokrycie około 80% -90%

Powinno to dać ładny działający zestaw testów jednostkowych, które będą działać jako dobry bufor przeciwko regresjom.

Jedynym problemem związanym z tym podejściem jest konieczność zaprojektowania kodu aby był testowalny w ten sposób. Jeśli na początku popełnisz jakiekolwiek błędy w łączeniu, nie będziesz w stanie łatwo uzyskać wysokiego pokrycia.

Dlatego tak ważne jest, aby napisać testy przed napisaniem kodu. Zmusza cię do pisania kodu, który jest luźno powiązany.


1

Nie testuj działającego (standardowego) kodu. Więc jeśli twoje metody ustawiające i pobierające to po prostu „propertyvalue = wartość” i „return propertyvalue”, nie ma sensu go testować.


1

Nawet get / set może mieć dziwne konsekwencje, w zależności od tego, jak zostały zaimplementowane, dlatego należy je traktować jak metody.

Każdy z tych testów będzie musiał określić zestawy parametrów dla właściwości, definiując zarówno dopuszczalne, jak i niedopuszczalne właściwości, aby zapewnić, że wywołania zwracają / kończą się niepowodzeniem w oczekiwany sposób.

Musisz również zdawać sobie sprawę z pułapek bezpieczeństwa, na przykład wstrzyknięć SQL, i przetestować je.

Więc tak, musisz się martwić o testowanie właściwości.


1

Uważam, że głupie jest testowanie metod pobierających i ustawiających, gdy wykonują one tylko prostą operację. Osobiście nie piszę skomplikowanych testów jednostkowych, które obejmują dowolny wzorzec użycia. Próbuję napisać wystarczającą liczbę testów, aby upewnić się, że poradziłem sobie z normalnym zachowaniem podczas wykonywania i tyloma przypadkami błędów, jakie przychodzą mi do głowy. Napiszę więcej testów jednostkowych w odpowiedzi na zgłoszenia błędów. Używam testów jednostkowych, aby upewnić się, że kod spełnia wymagania i ułatwić przyszłe modyfikacje. Czuję się o wiele bardziej skłonny do zmiany kodu, gdy wiem, że jeśli coś złamię, to test się nie powiedzie.


1

Napisałbym test dla wszystkiego, dla czego piszesz kod, który można przetestować poza interfejsem GUI.

Zwykle każda logika, którą piszę, ma jakąkolwiek logikę biznesową, którą umieszczam w innej warstwie lub warstwie logiki biznesowej.

Wtedy łatwo jest napisać testy na wszystko, co robi coś.

Najpierw napisz test jednostkowy dla każdej metody publicznej w „Warstwie logiki biznesowej”.

Gdybym miał taką klasę:

   public class AccountService
    {
        public void DebitAccount(int accountNumber, double amount)
        {

        }

        public void CreditAccount(int accountNumber, double amount)
        {

        }

        public void CloseAccount(int accountNumber)
        {

        }
    }

Pierwszą rzeczą, którą zrobiłbym przed napisaniem kodu, wiedząc, że mam te akcje do wykonania, byłoby rozpoczęcie pisania testów jednostkowych.

   [TestFixture]
    public class AccountServiceTests
    {
        [Test]
        public void DebitAccountTest()
        {

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

Napisz testy, aby sprawdzić poprawność kodu, który napisałeś, aby coś zrobić. Jeśli powtarzasz zbiór rzeczy i zmieniasz coś w każdym z nich, napisz test, który robi to samo i potwierdź, że faktycznie się wydarzyło.

Istnieje wiele innych podejść, które możesz zastosować, a mianowicie Behavoir Driven Development (BDD), które są bardziej zaangażowane i nie są dobrym miejscem do rozpoczęcia umiejętności testowania jednostkowego.

Tak więc morał tej historii jest taki, że testuj wszystko, co powoduje cokolwiek, o co możesz się martwić, trzymaj testy jednostkowe testujące określone rzeczy, które są małe, wiele testów jest dobrych.

Zachowaj logikę biznesową poza warstwą interfejsu użytkownika, aby łatwo napisać dla nich testy, a będziesz dobry.

Polecam TestDriven.Net lub ReSharper, ponieważ oba łatwo integrują się z Visual Studio.


1

Poleciłbym napisanie wielu testów dla metod uwierzytelniania i zapisywania. Oprócz przypadku powodzenia (gdzie podane są wszystkie parametry, wszystko jest poprawnie napisane itp.) Dobrze jest mieć testy dla różnych przypadków awarii (nieprawidłowe lub brakujące parametry, niedostępne połączenia z bazą danych, jeśli dotyczy, itp.). Polecam Pragmatic Unit Testing w C # z NUnit jako odniesieniem.

Jak powiedzieli inni, testy jednostkowe dla metod pobierających i ustawiających są przesadą, chyba że w twoich metodach pobierających i ustawiających istnieje logika warunkowa.


1

Chociaż możliwe jest prawidłowe odgadnięcie, gdzie kod wymaga przetestowania, generalnie myślę, że potrzebujesz danych, aby wykonać kopię zapasową tego przypuszczenia. Moim zdaniem testy jednostkowe idą w parze z metrykami pokrycia kodu.

Kod z wieloma testami, ale niewielki zasięg nie został dobrze przetestowany. To powiedziawszy, kod ze 100% pokryciem, ale bez testowania granic i przypadków błędów, również nie jest świetny.

Potrzebujesz równowagi między wysokim pokryciem (minimum 90%) a zmiennymi danymi wejściowymi.

Pamiętaj, aby przetestować pod kątem „śmieci w”!

Ponadto test jednostkowy nie jest testem jednostkowym, chyba że sprawdza błąd. Testy jednostkowe, które nie mają potwierdzeń lub są oznaczone znanymi wyjątkami, po prostu sprawdzą, czy kod nie umiera po uruchomieniu!

Musisz zaprojektować swoje testy tak, aby zawsze zgłaszały awarie lub nieoczekiwane / niechciane dane!


1

To sprawia, że ​​nasz kod jest lepszy ... kropka!

Jedną rzeczą, o której my, programiści zapominamy, gdy robimy programowanie sterowane testami, jest cel naszych działań. Jeśli test jednostkowy jest zapisywany po utworzeniu kodu produkcyjnego, wartość testu spada (ale nie jest całkowicie tracona).

W prawdziwym duchu testów jednostkowych testy te nie służą przede wszystkim do „testowania” większej ilości naszego kodu; lub aby uzyskać 90% -100% lepsze pokrycie kodu. Są to drugorzędne korzyści płynące z napisania najpierw testów. Dużą korzyścią jest to, że końce naszego kodu produkcyjnego są napisane znacznie lepiej ze względu na naturalny proces TDD.

Poniższe informacje mogą być pomocne w lepszym przedstawieniu tego pomysłu:

Wadliwa teoria testów jednostkowych
Celowe tworzenie oprogramowania

Jeśli uważamy, że napisanie większej liczby testów jednostkowych pomaga nam uzyskać produkt wyższej jakości, możemy cierpieć z powodu kultu Cargo związanego z rozwojem opartym na testach.


Nie zgadzam się z twierdzeniem, że testy jednostkowe nie mają wartości po wdrożeniu kodu produkcyjnego. Takie stwierdzenia nie uwzględniają ich użyteczności w replikowaniu warunków błędów znalezionych w środowisku produkcyjnym ani w rozumieniu kodu odziedziczonego po poprzednim deweloperze lub zespole.
Scott Lawrence,

Mogłem trafić niepoprawnie. Nie chodziło mi o to, że testy jednostkowe nie mają wartości po wprowadzeniu kodu produkcyjnego. Jednak ich wartość spada. Największą korzyść z testowania jednostkowego stanowi nieodłączna magia, która pojawia się, gdy pozwalamy im kierować rozwojem naszej produkcji.
Scott Saad,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.