Testy jednostkowe, TDD i nazewnictwo

0

Witam,

Zacząłem pisać projekt(dla siebie) w ASP.NET MVC i pomyślałem, że będzie to dobra chwila by w końcu zacząć pisać testy i wykorzystać TDD(Czy może to już przeżytek i tylko BDD).
Po kilku dniach pojawiły się pytania.

  1. Nazewnictwo: UnitOfWork_StateUnderTest_ExpectedBehavior

Rozumiem to tak, że mając klasę

public class HomeController 
{
	public ActionResult Index()
	{
		return View();
	}
	public ActionResult Index(int id)
	{
		if(id < 0)
			throw new Exception();
		/* jakiś kod */
		
		return View();
	}
}

To mam klasę HomeControllerTests a w niej powinno się znaleźć coś takiego:

public void Index_WithoutArguments_ReturnsViewWithEmptyName();
public void Index_PositiveIdNumber_ReturnsViewWithEmptyName();
public void Index_NegativeIdNumber_ThrowsException();

Dobrze rozumuje?

  1. TDD - pisanie testów przed implementacją metody
    Wydaje się to dość ciężkie ponieważ muszę mieć testy różnych scenariuszy przed napisaniem tych scenariuszy w wnętrzu metody.
    Jak sobie z tym radzicie?

W domu staram się pisać małe bardzo małe metody góra 20 linii ale w pracy są takie po 150-200 linii i bądź tu mądry pisząc do tego testy...
Wracając do tematu jak radzicie sobie ze stosowaniem TDD? Piszecie deklaracje metody i w głowie lub z dokumentacji(co metoda ma robić) ustalacie możliwe scenariusze?

Co w przypadku gdy owa metoda wykorzystuje inne fragmenty projektu do których jeszcze nie ma ani metod testujących ani w ogóle tych fragmentów jeszcze nie ma? Jak to zaplanować?

  1. Czy by dobrze przetestować aplikacje i w ogóle dobrze się z nią pracowało w kodzie warto poświecić kilka chwil i zaimplementować wzorzec Repozytorium i Unit of Work?
1
Pijany Szczur napisał(a):
public void Index_WithoutArguments_ReturnsViewWithEmptyName();
public void Index_PositiveIdNumber_ReturnsViewWithEmptyName();
public void Index_NegativeIdNumber_ThrowsException();

Dobrze rozumuje?

Tak.

  1. TDD - pisanie testów przed implementacją metody
    Wydaje się to dość ciężkie ponieważ muszę mieć testy różnych scenariuszy przed napisaniem tych scenariuszy w wnętrzu metody.
    Jak sobie z tym radzicie?

Trzeba sobie jakoś te scenariusze przemyśleć i zaplanować.

W domu staram się pisać małe bardzo małe metody góra 20 linii ale w pracy są takie po 150-200 linii i bądź tu mądry pisząc do tego testy...

To może jednak warto dzielić metody w pracy na mniejsze?

Wracając do tematu jak radzicie sobie ze stosowaniem TDD? Piszecie deklaracje metody i w głowie lub z dokumentacji(co metoda ma robić) ustalacie możliwe scenariusze?

Mniej więcej tak.

Co w przypadku gdy owa metoda wykorzystuje inne fragmenty projektu do których jeszcze nie ma ani metod testujących ani w ogóle tych fragmentów jeszcze nie ma? Jak to zaplanować?

Pytanie, czy to wtedy ciągle jest test jednostkowy czy już integracyjny. Ale tak ogólnie, to jeśli nie ma implementacji czegoś, ale jest deklaracja (interfejs) to można to zamockować.

  1. Czy by dobrze przetestować aplikacje i w ogóle dobrze się z nią pracowało w kodzie warto poświecić kilka chwil i zaimplementować wzorzec Repozytorium i Unit of Work?

A co ma jedno do drugiego? Własna implementacja repozytorium i UoW jest potrzebna tylko gdy stosujemy DDD i nie mamy ORMa (albo mamy jakąś jego parodię w stylu Entity Framework).
Testować jednostkowo da się praktycznie każdy kod. Tylko nie zawsze ma to sens. W szczególności zazwyczaj nie ma sensu testować jednostkowo operacji CRUD, kodu GUI (a więc np. kontrolerów), albo metod frameworka.

0

Prosto i klarownie ;)

Staram się pisać w pracy małe metody ale były już kolosy gdy dołączyłem.

Czemu mówisz takie rzeczy o Entity Framework? Dlaczego go trzeba opakować w uow i repozytorium czy on sam w sobie nie jest ich przedstawicielem?

Mówisz, że nie ma sensu testować jednostkowo kontrolerów? A to czemu? W nich jest sporo logiki. Tak samo widoków nie testować? Tylko co?
Co w takim razie zostaje? Nie widoki, nie kontrolery i pośredniki między bazą a aplikacją?

0

W nich jest sporo logiki

Znaczy że masz błąd w projekcie a nie że trzeba testować kontroler ;]
Logika to powinna być w serwisach biznesowych. Kontroler powinien tylko taki serwis wywołać a potem wynik przesłać do odpowiedniego widoku i tyle.

0
Zimny Szczur napisał(a):

Czemu mówisz takie rzeczy o Entity Framework?

Bo jest do bani.

Dlaczego go trzeba opakować w uow i repozytorium czy on sam w sobie nie jest ich przedstawicielem?

Masz rację, jest. Ale jeśli nie opakujesz Contextu we własne interfejsy, to go nie zamockujesz, więc nie napiszesz testów jednostkowych korzystających z UoW. M.in. dlatego EF jest do bani.

Co w takim razie zostaje? Nie widoki, nie kontrolery i pośredniki między bazą a aplikacją?

Te "pośredniki" nazywają się w MVC Model i powinny zawierać logikę biznesową, wszelkie algorytmy i obliczenia, które można łatwo i sensownie przetestować.

0

Mógłbyś rozszerzyć "do bani" dlaczego? Przecież jest częścią platformy to chyba musi być "dobry" skoro go tam umieścili i wszyscy co pracują z ASP.NET MVC chyba go wykorzystują?
Czy może jakieś alternatywy? Coś z rodziny N?

Moment moment, MVC Model ma zawierać logikę? Przerobiłem wiele "tutoriali" i kilka książek i w każdej model był jedynie czymś takim:

public class Person
{
public int Id {get;set;}
public String Name {get;set;}

public ICollection<Address> Addresses {get;set;}

public Person()
{
Addresses = new HashSet<Address>();
}
} 

To są klasy POCO tak? I to jest model.

Po takich postach czuć, że nic się nie wie lecz przed chwilą była świadomość tej wiedzy :D

0

A czy taka nazwa jest poprawna?

 
 Create_SampleModelWithOrderTypeOtherThanConstantAndresultIdHasValue_RedirectsToActionEditWithTempDataNewOrderKey()

Wydaje się dość długa ale kod w kontrolerze jest spory więc by opisać dobrze przebieg scenariusza bez dogłębnej analizy ifów i switchy wew. metody potrzeba czegoś takiego.

1

@Zimny Szczur bo tutoriale zwykle prezentują banalne CRUDy gdzie nie ma zadnej logiki biznesowej a "model" stanowią tylko dane. W prawdziwym życiu "operacje na danych" czyli to co w Modelu to jest 95% całej aplikacji.

kod w kontrolerze jest spory

Wywal go z kontrolera, to raz. A dwa podziel go na mniejsze kawałki. Kontroler w MVC powinien:

  • wywołać akcje na modelu (czyli odpalic jakiś serwis)
  • ewentualnie załadować odpowiedni widok
    Kontroler/Presenter w MVP powinien:
  • wywołać akcje na modelu (czyli odpalic jakiś serwis)
  • pobrać wynik tej akcji w formacie strawnym dla widoku
  • załadować odpowiedni widok przesyłając uzyskane dane
    Jeśli masz coś wiecej w kontroletrze to jest to zwyczajnie błąd.
0

Dzięki za odpowiedź.
Będę starać się trzymać tych wytycznych.

Kod o którym mówiłem w kontrolerze to w pracy(nie ja za niego odpowiadam przyszedłem niedawno i zapoznaje się z aplikacją) dzisiaj znalazłem kontroler na 207 linii kodu.
Sporo ifów sporo tworzenia modeli typu

new model()
{
dziesiątki zmiennych
};

dodanie znacznika <code class="csharp"> - @furious programming

1
Zimny Szczur napisał(a):

Mógłbyś rozszerzyć "do bani" dlaczego? Przecież jest częścią platformy to chyba musi być "dobry" skoro go tam umieścili i wszyscy co pracują z ASP.NET MVC chyba go wykorzystują?

Nie jest dobry, po prostu M$ promuje swoje produkty razem. No i to chyba jest przyczyna, dla której większość projektów w .NET korzysta z EF.

Czemu EF jest do bani?

  • Generuje okropny kod SQL.
  • Wbrew prawom matematyki i ludzkiego rozumu, domyślnie ucina liczby dziesiętne zamiast zaokrąglać: (https://msdn.microsoft.com/en-us/library/system.data.entity.sqlserver.sqlproviderservices.truncatedecimalstoscale%28v=vs.113%29.aspx)
  • Jego twórcom trzy lata zajęła implementacja dialektu SQL 2012 (chodzi mi o offset i fetch). Powtarzam - trzy lata dostosowanie jednego produktu M$ do innego produktu tej samej firmy.
  • Nie potrafi samo generować ID.
  • Obsługuje tylko jeden typ kolekcji, nie można np. zmapować słownika, listy ani baga, tylko ten DbSet.
  • Nie posiada wbudowanego second level cache.
  • Wspiera niewiele baz danych (w praktyce jedną).
  • Ma niewielkie możliwości rozszerzania, np. brak oddzielnych zdarzeń przed/po insert/update/delete/load.
  • Brak operacji wsadowych - skasowanie np. wszystkich rekordów spełniających dany warunek wymaga najpierw ich załadowania z bazy albo napisania SQL.
  • Brak możliwości leniwego ładowania pojedynczych właściwości.
  • Brak możliwości utworzenia obiektu Proxy bez ładowania go z bazy.
  • Optimistic concurency tylko na rowversion albo na wszystkich kolumnach na raz.
  • Brak wyliczanych właściwości.
  • Brak filtrów globalnych.
  • Brak query-by-example.
  • Brak prostej możliwości budowania zapytań po nazwach właściwości. (Z GUI zazwyczaj nazwę właściwości, po której sortujesz albo filtrujesz dostajesz jako string, a EF jest tak nowoczesny tępy, że łyka tylko LINQ.)
  • Ma bezsensowną architekturę - *Context trzyma konfigurację, model, metadane oraz jest UoW i trzyma listę śledzonych encji. Gdzie SRP ja się pytam?

Czy może jakieś alternatywy? Coś z rodziny N?

Ja preferuję NHibernate, ale nie wiem, czy to alternatywa, bo to tak jakby nazwać prom kosmiczny alternatywą trampek...

Moment moment, MVC Model ma zawierać logikę? Przerobiłem wiele "tutoriali" i kilka książek i w każdej model był jedynie czymś takim:

Takie klasy mogą też być w modelu. Ale model, to wszystko, co nie jest ani widokiem ani kontrolerem. Logika biznesowa, operacje na danych - to wszystko powinno mieć miejsce w modelu. Model to wiele warstw, a nie jeden katalog z klasami w MVC.

Tutoriale i książki po pierwsze upraszczają wszystko, po drugie ich autorzy często nie wiedzą co to jest MVC.
Tak jak możesz pisać nieobiektowo w języku obiektowym, tak samo możesz nie stosować się do wzorca MVC korzystając z frameworka MVC.

Proponuję zajrzeć do źródeł: http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html

In the MVC paradigm the user input, the modeling of the external world, and the visual feedback to the user are explicitly separated and handled by three types of object, each specialized for its task. The view manages the graphical and/or textual output to the portion of the bitmapped display that is allocated to its application. The controller interprets the mouse and keyboard inputs from the user, commanding the model and/or the view to change as appropriate. Finally, the model manages the behavior and data of the application domain, responds to requests for information about its state (usually from the view), and responds to instructions to change state (usually from the controller).

(pogrubienie moje)

1 użytkowników online, w tym zalogowanych: 0, gości: 1