2009-07-24 5 views
2

Je construis un shell WPF composite de base avec un module. Je voudrais tester mon module unitaire. Apparemment Composite WPF modélise mon code de manière à ce qu'il soit facile de faire un test unitaire.En composite WPF (Prism), comment dois-je tester mes contrôleurs?

Ci-dessous le code que je voudrais tester. Il réside dans le contrôleur de mon module. Notez l'utilisation des entités standards Composite WPF comme régions, présentateurs, modèles, etc.

public void ShowPlantTreeView() 
    { 
     IRegion navRegion = this.regionManager.Regions[RegionNames.NavigationRegion]; 
     IPlantTreeView view = navRegion.GetView(typeof(IPlantTreeView).Name) as IPlantTreeView; 
     if (view == null) 
     { 
      view = this.container.Resolve<IPlantTreePresentationModel>().View; 
      navRegion.Add(view, typeof(IPlantTreeView).Name); 
     } 

     view.Model.LastRefreshDateTime = DateTime.Now; 
     navRegion.Activate(view); 
    } 

Ceci est seulement sept lignes de code que je veux tester l'unité. Pas mal. Le problème est que cela dépend d'un certain nombre de composants externes - RegionManager, View, PresentationModel etc.

Afin de tester cela indépendamment, je simule les composants externes. Ceux-ci sont passés dans mon contrôleur via l'injection de constructeur en utilisant le conteneur Unity. Pour configurer ceci et faire un test simple, mon test d'unité ressemble à ceci:

(Regardez la longueur de cette méthode! Il doit sûrement y avoir une meilleure façon de tester? Est-ce que le composite WPF fait vraiment ma vie Et je dois le faire pour chaque test?!)

[TestMethod] 
    public void TestShowPlantTree() 
    { 
     //Setup Mocks. 
     var plantTreePresentationModel = new Mock<IPlantTreePresentationModel>(); 
     var plantTreeViewMock = new Mock<IPlantTreeView>(); 
     var navRegionMock = new Mock<IRegion>(); 
     var plantTreeModuleMock = new Mock<IPlantTreeModule>(); 
     var regionManagerMock = new Mock<IRegionManager>(); 
     var eventAggregatorMock = new Mock<IEventAggregator>(); 
     var shellControllerMock = new Mock<IShellController>(); 
     var plantTreeNodeSelectedEventMock = new Mock<PlantTreeNodeSelectedEvent>(); 

     plantTreeViewMock.Setup(v => v.Model).Returns(plantTreePresentationModel.Object); 
     container.RegisterInstance<IPlantTreePresentationModel>(plantTreePresentationModel.Object); 
     regionManagerMock.Setup(o => o.Regions[RegionNames.NavigationRegion]).Returns(navRegionMock.Object); 
     navRegionMock.Setup(r => r.GetView(typeof(IPlantTreeView).Name)).Returns(plantTreeViewMock.Object); 
     navRegionMock.Setup(r => r.Activate(plantTreeViewMock.Object)); 
     plantTreePresentationModel.SetupSet(m => m.LastRefreshDateTime); 
     eventAggregatorMock.Setup(a => a.GetEvent<PlantTreeNodeSelectedEvent>()).Returns(plantTreeNodeSelectedEventMock.Object); 


     //Setup container. 
     container.RegisterType<IPlantTreeController, PlantTreeController>(); 
     container.RegisterInstance<IPlantTreePresentationModel>(plantTreePresentationModel.Object); 
     container.RegisterInstance<IPlantTreeView>(plantTreeViewMock.Object); 
     container.RegisterInstance<IRegion>(navRegionMock.Object); 
     container.RegisterInstance<IPlantTreeModule>(plantTreeModuleMock.Object); 
     container.RegisterInstance<IRegionManager>(regionManagerMock.Object); 
     container.RegisterInstance<IEventAggregator>(eventAggregatorMock.Object); 
     container.RegisterInstance<IShellController>(shellControllerMock.Object); 
     container.RegisterInstance<PlantTreeNodeSelectedEvent>(plantTreeNodeSelectedEventMock.Object); 


     //Initialize controller to be tested. 
     IPlantTreeController controllerToTest = container.Resolve<IPlantTreeController>(); 

     controllerToTest.ShowPlantTreeView(); 


     //Test if controller interacted with the mocks as expected. 
     plantTreePresentationModel.VerifyAll(); 
     regionManagerMock.VerifyAll(); 
     navRegionMock.VerifyAll(); 
    } 

Existe-t-il une meilleure façon de tester ma classe? Tout avis sera le bienvenu.

Répondre

1

Je l'ai moi-même traversé avec des classes avec beaucoup de dépendances. Il n'y a vraiment pas moyen de contourner cela.

Votre méthode dépend-elle vraiment de toutes ces classes? Je vois seulement deux ou trois dépendances étant utilisées ici (IRegionManager, IPlantTreePresentationModel). Ceux-ci devraient être les seuls avoir à simuler pour tester votre méthode. Vous pouvez tester cela en utilisant un ensemble de dépendances qui est approprié pour votre test, plutôt que pour tout test de cet objet.

L'autre chose que vous pourriez considérer est le nombre de ces dépendances que vous pouvez prendre en compte dans le code de démarrage de votre test (méthode décorée avec [TestInitialize]). Certaines dépendances courantes et votre conteneur peuvent être dans une portée pour l'ensemble de votre suite de tests, en particulier s'ils ne changent pas par test.

L'injection de dépendance vous facilite certainement la vie, même si vous ne le réalisez pas. Je trouve que beaucoup de gens effectuant des «tests unitaires» ne le font pas vraiment bien et font des tests fonctionnels en raison du fait qu'ils ne sont pas correctement isolés des autres parties de leur application.

L'injection de dépendances vous force presque dans un modèle où vous pouvez isoler complètement le code que vous voulez vraiment tester loin de toutes les autres parties de votre application. Cela peut coûter un peu d'avance, mais la qualité de vos tests unitaires et la granularité des commentaires que vous obtenez d'eux l'emportent sur ces coûts, en particulier lorsque vous arrivez plus tard dans le cycle de vie de votre application et commencez le refactoring. Vous vous remercierez alors. Restez avec.

+0

Excellent conseil, merci Anderson. Je vais voir comment je peux le simplifier. Déplacer certaines des dépendances dans la méthode d'installation a du sens. Je me demande si je devrais peut-être utiliser certaines des classes concrètes Prism? (c'est-à-dire devrais-je vraiment me moquer de l'IEventAggregator par exemple?) – willem

Questions connexes