2010-05-22 3 views
0

J'ai beaucoup édité et simplifié cette question.Pourquoi ce test d'unité asp.net mvc échoue-t-il?

Si je cette méthode sur mon HomeController:

public ActionResult Strangeness(int id) 
    { 
     StrangenessClass strangeness = null; 

     if(id == 1) 
     { 
      strangeness = new StrangenessClass() { Name="Strangeness", Desc="Really weird behavior" }; 
     } 

     return View(strangeness); 
    } 

Et cette classe:

public class StrangenessClass 
{ 
    public string Name { get; set; } 
    public string Desc { get; set; } 
} 

Pourquoi ne satisfait pas à ce test unitaire?

[TestMethod] 
    public void Strangeness() 
    { 
     HomeController controller = new HomeController(); 

     ViewResult result = controller.Strangeness(1) as ViewResult; 
     var model = result.ViewData.Model; 
     result = controller.Strangeness(2) as ViewResult; 
     model = result.ViewData.Model; 

     Assert.IsNull(model); 
    } 

Je comprends que normalement, je devrais un test pour tester l'état nul et une autre pour tester un bon état, mais je suis tombé sur ce problème lors du test mon contrôleur de suppression. Lors d'un test de suppression, je récupérais normalement l'enregistrement, supprimais l'enregistrement, puis essayais de le récupérer. Il devrait être nul la deuxième fois que je le prends, mais ce n'était pas le cas. Donc, j'ai fait bouillir le problème comme décrit ci-dessus.

Si ce n'est pas le bon moyen de tester les suppressions, comment le feriez-vous? Vous n'avez pas besoin de vous assurer que l'enregistrement a bien été effacé?

Répondre

1

Vous ne devriez pas réutiliser un contrôleur pour gérer plusieurs demandes, ce qui est exactement ce que vous faites ici.

Quoi qu'il en soit, si vous cochez la source code for MVC, vous trouverez la raison de ce comportement:

protected internal virtual ViewResult View(string viewName, string masterName, object model) 
{ 
    if (model != null) 
    { 
     base.ViewData.Model = model; 
    } 
    return new ViewResult { ViewName = viewName, MasterName = masterName, ViewData = base.ViewData, TempData = base.TempData }; 
}

Si le modèle est nul, ce n'est pas affectée à la propriété ViewData.Model. Si vous voulez le comportement correct, créez un nouveau contrôleur pour votre deuxième appel à HomeController.Strangeness.

0

Ce que vous testez n'est pas clair. Dans la section Arrangement de votre méthode de test, vous appelez la première action Supprimer et dans la section Act vous appelez la seconde. Alors testez-vous le contrôleur? Si oui alors pourquoi appelez-vous la première méthode Delete dans la section Arrangement?

Aussi, quelle est la variable _stateService? Est-ce une interface ou supprimez-vous réellement des enregistrements dans la base de données de votre test d'unité/d'intégration? Par conséquent, je vous recommande d'écrire plusieurs tests, chacun vérifiant un comportement précis du sujet à tester que je suppose être le contrôleur. Vous devez donc séparer les tests unitaires des différentes actions Delete que vous testez.

En supposant que _stateService est une interface qui pourrait être moqué, ce qui est ce que je vous recommande de concevoir votre contrôleur, votre test pourrait ressembler à ceci (en utilisant Rhino Mocks et MVCContrib.TestHelper):

[TestClass] 
public class DevisControllerTests : TestControllerBuilder 
{ 
    private HomeController _sut; // Subject Under Test 
    private IStateService _stateServiceStub; // Dependency of the SUT 

    [TestInitialize()] 
    public void MyTestInitialize() 
    { 
     _stateServiceStub = MockRepository.GenerateStub<IStateService>(); 
     _sut = new HomeController(_stateServiceStub); 
     InitializeController(_sut); // this method comes from the base class TestControllerBuilder 
    } 

    [TestMethod] 
    public void HomeController_Delete_Action_Should_Fetch_State_From_Db_And_Pass_It_To_The_View() 
    { 
     // arrange 
     var id = 4; 
     var expectedState = new State(); 
     _stateServiceStub.Stub(x => x.GetById(id)).Return(expectedState); 

     // act 
     var actual = _sut.Delete(id); 

     // assert 
     actual 
      .AssertViewRendered() 
      .WithViewData<State>() 
      .ShouldBe(expectedState); 
    } 

    [TestMethod] 
    public void HomeController_Delete_Action_Handler_Should_Return_Default_View_If_Model_Null() 
    { 
     // act 
     var actual = _sut.Delete(null); 

     // assert 
     actual.AssertViewRendered(); 
    } 

    [TestMethod] 
    public void HomeController_Delete_Action_Handler_Should_Return_View_If_Exception_Thrown_From_Service() 
    { 
     // arrange 
     var model = new State(); 
     _stateServiceStub.Stub(x => x.Delete(model)).Throw(new Exception("oops")); 

     // act 
     var actual = _sut.Delete(state); 

     // assert 
     actual 
      .AssertViewRendered() 
      .WithViewData<State>() 
      .ShouldBe(model); 
    } 


    [TestMethod] 
    public void HomeController_Delete_Action_Handler_Should_Redirect_If_Model_Successfully_Deleted() 
    { 
     // arrange 
     var model = new State(); 

     // act 
     var actual = _sut.Delete(state); 

     // assert 
     actual 
      .AssertActionRedirect() 
      .ToAction<HomeController>(c => c.Index()); 

     _stateServiceStub.AssertWasCalled(x => x.Delete(model)); 
    } 

} 
+0

Il y a deux méthodes de contrôleur de suppression que j'ai posté. Le premier à récupérer l'état à supprimer, et la deuxième méthode de publication pour effectuer la suppression. La variable _stateService est une instance de mon service d'état qui est basée sur une interface et renvoie tout à un FakeStateRepoditory pour les tests unitaires. Mon TestInitialize définit tout cela afin que j'ai des données constituées pour chaque test. L'arrangement obtient l'enregistrement que je veux supprimer, l'acte essaye de le supprimer et le récupère ensuite, et l'actif s'assure qu'il est nul et que nous avons été renvoyés à la méthode d'index. –

+0

Pour clarifier davantage, j'ai plusieurs tests de suppression pour m'assurer que la première méthode de contrôleur de suppression renvoie les données d'affichage correctes, pour m'assurer que je suis redirigé vers l'endroit approprié si j'essaye de supprimer un enregistrement qui n'existe pas, etc. Ce test particulier fait en sorte que l'enregistrement soit effectivement supprimé. Pour ce faire, je dois le récupérer, l'envoyer pour être supprimé et essayer de le récupérer pour m'assurer qu'il est parti. –

+0

Lorsque j'essaie d'utiliser votre méthode, j'obtiens l'erreur suivante sur le test HomeController_Delete_Action_Handler_Should_Redirect_If_Model_Successfully_Delete: Impossible de trouver un paramètre nommé 'controller' dans la collection Valeurs du résultat. Si je le change en: réelle .AssertActionRedirect() .ToAction ("Index"); cela fonctionne. Mais, je préfère ta méthode. Savez-vous ce que je fais mal? –

Questions connexes