Mam nadzieję, że to pytanie daje kilka interesujących odpowiedzi, ponieważ jest to pytanie, które mnie trochę denerwowało.
Czy jest jakaś rzeczywista wartość w jednostce testującej kontroler w ASP.NET MVC?
Rozumiem przez to, że przez większość czasu (i nie jestem geniuszem), moje metody kontrolera są, nawet w ich najbardziej skomplikowanych, czymś takim:
public ActionResult Create(MyModel model)
{
// start error list
var errors = new List<string>();
// check model state based on data annotations
if(ModelState.IsValid)
{
// call a service method
if(this._myService.CreateNew(model, Request.UserHostAddress, ref errors))
{
// all is well, data is saved,
// so tell the user they are brilliant
return View("_Success");
}
}
// add errors to model state
errors.ForEach(e => ModelState.AddModelError("", e));
// return view
return View(model);
}
Większość ciężkich zadań jest wykonywana przez potok MVC lub bibliotekę usług.
Być może więc pytania mogą być następujące:
- jaka byłaby wartość jednostki testującej tę metodę?
- czy nie złamać na
Request.UserHostAddress
iModelState
z NullReferenceException? Czy powinienem próbować się z nich kpić? - jeśli zmienię tę metodę w „pomocnika” wielokrotnego użytku (co prawdopodobnie powinienem, biorąc pod uwagę, ile razy to robię!), przetestowałbym to nawet, gdy warto przetestować głównie „potok”, który, przypuszczalnie, czy został przetestowany w ciągu swojego życia przez Microsoft?
Wydaje mi się, że moim celem jest to, że wykonanie poniższych czynności wydaje się całkowicie bezcelowe i niewłaściwe
[TestMethod]
public void Test_Home_Index()
{
var controller = new HomeController();
var expected = "Index";
var actual = ((ViewResult)controller.Index()).ViewName;
Assert.AreEqual(expected, actual);
}
Oczywiście, że mam obsesję na punkcie tego przesadnie bezcelowego przykładu, ale czy ktoś ma tutaj jakąś mądrość?
Nie mogę się doczekać ... Dzięki.
c#
unit-testing
asp.net-mvc
LiverpoolsNumber9
źródło
źródło
Odpowiedzi:
Nawet dla czegoś tak prostego test jednostkowy będzie służył wielu celom
W przypadku tej konkretnej czynności przetestuję następujące elementy
Zwróciłeś uwagę na sprawdzenie Żądania i Modelu dla NullReferenceException i myślę, że ModelState.IsValid zajmie się obsługą NullReference dla Modelu.
Egzekwowanie wniosku pozwala uchronić się przed żądaniem zerowym, które, jak sądzę, jest generalnie niemożliwe, ale może się zdarzyć podczas testu jednostkowego. W teście integracji pozwoliłby ci podać różne wartości UserHostAddress (Żądanie jest nadal danymi wejściowymi użytkownika, jeśli chodzi o kontrolę i należy je odpowiednio przetestować)
źródło
Moje kontrolery są również bardzo małe. Większość „logiki” w kontrolerach jest obsługiwana przy użyciu atrybutów filtru (wbudowanych i odręcznych). Więc mój kontroler zwykle ma tylko kilka zadań:
ActionResult
Większość powiązań modelu jest wykonywana automatycznie przez ASP.NET MVC. DataAnnotations obsługuje także większość sprawdzania poprawności.
Nawet z tak małą ilością do przetestowania, nadal zazwyczaj je piszę. Zasadniczo sprawdzam, czy moje repozytoria są wywoływane i czy
ActionResult
zwracany jest właściwy typ. Mam wygodną metodęViewResult
sprawdzania, czy zwrócona jest właściwa ścieżka widoku, a model widoku wygląda tak, jak tego oczekuję. Mam inny do sprawdzenia, czy ustawiony jest odpowiedni kontroler / akcjaRedirectToActionResult
. Mam inne testyJsonResult
itp. Itp.Niefortunnym wynikiem podklasowania
Controller
klasy jest to, że zapewnia ona wiele wygodnych metod, które wykorzystująHttpContext
wewnętrznie. Utrudnia to jednostkowe przetestowanie kontrolera. Z tego powodu zazwyczaj umieszczamHttpContext
wywołania -zależne za interfejsem i przekazuję ten interfejs do konstruktora kontrolera (używam rozszerzenia internetowego Ninject do tworzenia moich kontrolerów). W interfejsie tym zwykle umieszczam właściwości pomocnika w celu uzyskania dostępu do sesji, ustawień konfiguracji, IPrinciple i pomocników URL.Wymaga to dużej staranności, ale myślę, że warto.
źródło
BaseControllerTests
klasę, w której wszyscy mieszkają. Kpię z moich repozytoriów. Podłączam je za pomocą Ninject.ActionResult
celu kontroli przekazywane adresy URL, modele itpOczywiście niektóre kontrolery są znacznie bardziej złożone, ale oparte wyłącznie na twoim przykładzie:
Co się stanie, jeśli myService zgłosi wyjątek?
Na marginesie.
Ponadto kwestionowałbym zasadność przekazywania listy przez referencję (nie jest to konieczne, ponieważ c # i tak przechodzi przez referencję, ale nawet jeśli nie była) - przekazywanie akcji errorAction (akcji), którą usługa może następnie wykorzystać do pompowania komunikatów o błędach do które można następnie obsłużyć w dowolny sposób (być może chcesz dodać go do listy, może chcesz dodać błąd modelu, a może chcesz go zalogować).
W twoim przykładzie:
zamiast błędów ref, na przykład do (string s) => ModelState.AddModelError ("", s).
źródło