【问题标题】:In Composite WPF (Prism), how should I unit test my controllers?在 Composite WPF (Prism) 中,我应该如何对我的控制器进行单元测试?
【发布时间】:2009-07-24 13:25:47
【问题描述】:

我正在用一个模块构建一个基本的复合 WPF 外壳。我想对我的模块进行单元测试。显然 Composite WPF 以一种易于单元测试的方式模块化了我的代码。

以下是我要进行单元测试的代码。它驻留在我的模块的控制器中。请注意标准复合 WPF 实体的使用,例如区域、演示者、模型等。

    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);
    }

这只是我想要单元测试的七行代码。还不错。问题在于它依赖于许多外部组件——RegionManager、View、PresentationModel 等。

为了独立测试这个,我模拟了外部组件。这些通过使用 Unity 容器的构造函数注入传递到我的控制器中。为了配置这个并做一个简单的测试,我的单元测试如下...

(看看这个方法的长度!肯定有更好的测试方法?复合 WPF 真的让我的生活更轻松吗?而且我每次测试都必须这样做?!)

    [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();
    }

有没有更好的方法来测试我的课程?任何建议将不胜感激。

【问题讨论】:

    标签: wpf unit-testing prism


    【解决方案1】:

    我自己也遇到过具有很多依赖项的类。真的没有什么办法。

    你的方法真的依赖于所有这些类吗?我只看到这里使用了两个或三个依赖项(IRegionManager、IPlantTreePresentationModel)。这些应该是你拥有唯一可以模拟来测试你的方法的。您可以使用一组适合您的测试的依赖项来测试它,而不是针对此对象的任何测试。

    您可能会考虑的另一件事是您可以将多少这些依赖项纳入测试的启动代码(用 [TestInitialize] 修饰的方法)。一些常见的依赖项和您的容器可能存在于您的整个测试套件的范围内,尤其是如果它们不会在每次测试中发生变化。

    依赖注入确实让您的生活更轻松,即使您没有意识到这一点。我发现很多做“单元测试”的人并没有真正做对,而是在做功能测试,因为他们没有与应用程序的其他部分进行适当的隔离。

    依赖注入几乎迫使您进入一个模型,在该模型中,您可以将实际想要测试的代码与应用程序的所有其他部分完全隔离开来。这可能会花费一些前期成本,但是单元测试的质量和您从中获得的反馈的粒度将超过这些成本,尤其是当您进入应用程序生命周期的后期并开始重构时。到时候你会感谢自己的。坚持下去。

    【讨论】:

    • 很好的建议,谢谢安德森。我会看看如何简化它。将一些依赖项移动到 setup 方法中确实有意义。我想知道我是否应该使用一些具体的 Prism 类? (例如,我真的应该模拟 IEventAggregator 吗?)
    猜你喜欢
    • 1970-01-01
    • 2013-05-31
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    • 2011-01-01
    • 2010-09-05
    相关资源
    最近更新 更多