J'espère que cette question donne des réponses intéressantes, car c'est une question qui me dérange depuis un moment.
Existe-t-il une réelle valeur dans le test unitaire d’un contrôleur dans ASP.NET MVC?
Ce que je veux dire par là, c'est la plupart du temps (et je ne suis pas un génie), mes méthodes de contrôleur sont, même les plus complexes, quelque chose comme ceci:
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);
}
Le gros du travail est effectué par le pipeline MVC ou par ma bibliothèque de services.
Alors peut-être que les questions à poser pourraient être:
- Quelle serait la valeur du test unitaire avec cette méthode?
- ne serait-il pas cassé sur
Request.UserHostAddress
etModelState
avec une exception NullReferenceException? Devrais-je essayer de me moquer de ceux-ci? - si je réfracte cette méthode dans une "aide" réutilisable (ce que je devrais probablement, compte tenu du nombre de fois que je le fais!), testerait que cela en valait la peine alors que tout ce que je teste réellement est principalement le "pipeline" qui, vraisemblablement, a été testé à un pouce de sa vie par Microsoft?
Je pense que ce que je veux vraiment dire, c'est que faire ce qui suit semble totalement inutile et faux
[TestMethod]
public void Test_Home_Index()
{
var controller = new HomeController();
var expected = "Index";
var actual = ((ViewResult)controller.Index()).ViewName;
Assert.AreEqual(expected, actual);
}
Évidemment, je suis obtus avec cet exemple exagérément inutile, mais est-ce que quelqu'un a quelque sagesse à ajouter ici?
Dans l'attente ... Merci.
la source
Réponses:
Même pour quelque chose d'aussi simple, un test unitaire servira à plusieurs fins
Pour cette action particulière, je testerais pour le suivant
Vous avez indiqué cocher Request et Model pour NullReferenceException et je pense que ModelState.IsValid se chargera de la gestion de NullReference for Model.
L'exécution de la requête vous permet de vous protéger contre une requête nulle qui est généralement impossible en production, mais peut se produire lors d'un test unitaire. Dans un test d'intégration, cela vous permettrait de fournir différentes valeurs UserHostAddress (une demande est toujours entrée par l'utilisateur en ce qui concerne le contrôle et doit être testée en conséquence).
la source
Mes contrôleurs sont également très petits. La plupart de la "logique" dans les contrôleurs est gérée à l'aide d'attributs de filtre (intégrés et écrits à la main). Donc, mon contrôleur n'a généralement qu'une poignée d'emplois:
ActionResult
La plupart des liaisons de modèles sont effectuées automatiquement par ASP.NET MVC. DataAnnotations gère également la majeure partie de la validation.
Même avec si peu de choses à tester, je les écris toujours en général. En gros, je teste que mes référentiels sont appelés et que le
ActionResult
type correct est renvoyé. J'ai une méthode pratique pourViewResult
m'assurer que le bon chemin de vue est renvoyé et que le modèle de vue ressemble à ce que j'attendais. J'ai un autre pour vérifier le bon contrôleur / action est défini pourRedirectToActionResult
. J'ai d'autres tests pourJsonResult
, etc. etc.Le résultat malheureux de la sous-classification de la
Controller
classe est qu’elle fournit de nombreuses méthodes pratiques utilisant l’HttpContext
interne. Cela rend difficile le test unitaire du contrôleur. Pour cette raison, je place généralement desHttpContext
appels dépendants derrière une interface et la transmet au constructeur du contrôleur (j'utilise l'extension Web Ninject pour créer mes contrôleurs pour moi). Cette interface est généralement l'endroit où je colle les propriétés d'aide pour accéder à la session, aux paramètres de configuration, aux aides IPrinciple et URL.Cela nécessite beaucoup de diligence raisonnable, mais je pense que cela en vaut la peine.
la source
BaseControllerTests
classe où ils vivent tous. Je me moque de mes dépôts. Je les branche en utilisant Ninject.ActionResult
s pour inspecter les URL, modèles, etc. transmis.Évidemment, certains contrôleurs sont beaucoup plus complexes que cela, mais reposent uniquement sur votre exemple:
Que se passe-t-il si myService lève une exception?
En note de côté.
De plus, je m'interroge sur l'opportunité de passer une liste par référence (c'est inutile car c # passe par référence de toute façon mais même si elle ne l'était pas) - passer une action error (Action) que le service peut ensuite utiliser pour afficher des messages d'erreur qui peut ensuite être traité comme vous le souhaitez (peut-être souhaitez-vous l'ajouter à la liste, peut-être souhaitez-vous ajouter une erreur de modèle, peut-être souhaitez-vous le consigner)?
Dans votre exemple:
au lieu d'erreurs de référence, faites (chaîne s) => ModelState.AddModelError ("", s) par exemple.
la source