Espero que esta pergunta dê algumas respostas interessantes porque é uma que me incomoda por um tempo.
Existe algum valor real no teste de unidade de um controlador no ASP.NET MVC?
O que quero dizer com isso é que, na maioria das vezes, (e eu não sou um gênio), meus métodos de controle são, mesmo nos mais complexos, algo como isto:
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);
}
A maior parte do trabalho pesado é feita pelo pipeline MVC ou pela minha biblioteca de serviços.
Então, talvez as perguntas a serem feitas sejam:
- qual seria o valor da unidade testando esse método?
- não seria interrompido
Request.UserHostAddress
eModelState
com uma NullReferenceException? Devo tentar zombar deles? - se eu refatorar esse método em um "auxiliar" reutilizável (o que eu provavelmente deveria, considerando quantas vezes o faço!), testaria isso mesmo que vale a pena quando tudo o que realmente estou testando é principalmente o "pipeline" que, presumivelmente, foi testado para uma polegada de sua vida pela Microsoft?
Eu acho que realmente quero dizer , fazer o seguinte parece totalmente inútil e errado
[TestMethod]
public void Test_Home_Index()
{
var controller = new HomeController();
var expected = "Index";
var actual = ((ViewResult)controller.Index()).ViewName;
Assert.AreEqual(expected, actual);
}
Obviamente, estou sendo obtuso com este exemplo exageradamente inútil, mas alguém tem alguma sabedoria a acrescentar aqui?
Ansioso por isso ... Obrigado.
c#
unit-testing
asp.net-mvc
LiverpoolsNumber9
fonte
fonte
Respostas:
Mesmo para algo tão simples, um teste de unidade servirá a vários propósitos
Para essa ação específica, eu testaria o seguinte
Você apontou para verificar Request and Model for NullReferenceException e acho que o ModelState.IsValid cuidará do tratamento de NullReference for Model.
Zombar da solicitação permite que você se proteja contra uma solicitação nula, que geralmente é impossível na produção, mas que pode ocorrer em um teste de unidade. Em um Teste de Integração, permitiria fornecer valores diferentes de UserHostAddress (uma solicitação ainda é entrada do usuário no que diz respeito ao controle e deve ser testada de acordo)
fonte
Meus controladores também são muito pequenos. A maior parte da "lógica" nos controladores é manipulada usando atributos de filtro (interno e escrito à mão). Portanto, meu controlador geralmente possui apenas alguns trabalhos:
ActionResult
A maior parte da ligação do modelo é feita automaticamente pelo ASP.NET MVC. As DataAnnotations também lidam com a maior parte da validação.
Mesmo com tão pouco a testar, eu ainda os escrevo normalmente. Basicamente, testo se meus repositórios são chamados e se o
ActionResult
tipo correto é retornado. Eu tenho um método de conveniênciaViewResult
para garantir que o caminho de exibição correto seja retornado e o modelo de exibição tenha a aparência esperada. Eu tenho outro para verificar se o controlador / ação correto está definidoRedirectToActionResult
. Eu tenho outros testes paraJsonResult
, etc. etc.Um resultado infeliz da subclasse da
Controller
classe é que ela fornece muitos métodos de conveniência que usamHttpContext
internamente. Isso dificulta o teste da unidade do controlador. Por esse motivo, normalmente colocoHttpContext
chamadas dependentes atrás de uma interface e passo essa interface para o construtor do controlador (eu uso a extensão da web Ninject para criar meus controladores para mim). Essa interface geralmente é onde colo as propriedades auxiliares para acessar a sessão, as definições de configuração, os auxiliares de IPrinciple e URL.Isso requer muita diligência, mas acho que vale a pena.
fonte
BaseControllerTests
aula onde todos moram. Eu zombei dos meus repositórios. Eu os conecto usando o Ninject.ActionResult
é para inspecionar URLs passados, modelos, etc.Obviamente, alguns controladores são muito mais complexos que isso, mas baseados puramente no seu exemplo:
O que acontece se o myService lançar uma exceção?
Como uma nota rodapé.
Além disso, eu questionaria a sabedoria de passar uma lista por referência (não é necessário, pois o c # passa por referência de qualquer maneira, mas mesmo que não fosse) - passar uma ação errorAction (Action) que o serviço pode usar para bombear mensagens de erro para que pode ser manipulado da maneira que você deseja (talvez você queira adicioná-lo à lista, talvez você queira adicionar um erro de modelo, talvez queira registrá-lo).
No seu exemplo:
em vez de erros de ref, faça (string s) => ModelState.AddModelError ("", s), por exemplo.
fonte