在 Composite WPF (Prism) 中,我应该如何对控制器进行单元测试? [英] In Composite WPF (Prism), how should I unit test my controllers?

查看:35
本文介绍了在 Composite WPF (Prism) 中,我应该如何对控制器进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm building a basic Composite WPF Shell with one Module. I would like to unit test my module. Apparently Composite WPF modularizes my code in such a way that it should be easy to unit test.

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

Below is the code I would like to Unit Test. It resides in my Module's Controller. Note the use of the standard Composite WPF entities like Regions, Presenters, Models 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);
    }

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

This is only seven lines of code I want to unit test. Not too bad. The trouble is that it depends on a number of external components - RegionManager, View, PresentationModel etc.

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

In order to test this independently I Mock the external components. These are passed into my Controller through Constructor Injection using the Unity Container. In order to configure this and do a simple test, my unit test looks as follows...

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

(Look at the length of this method! Surely there must be a better way to test? Does composite WPF really make my life easier? And I have to do this for every 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();
    }

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

Is there a better way to test my class? Any advice would be appreciated.

推荐答案

我自己也遇到过这个问题,其中的类有很多依赖项.真的没有太多办法解决它.

I've run across this myself with classes with a lot of dependencies. There's really not much of a way around it.

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

Does your method really depend on all of these classes? I only see two or three dependencies being utilized here (IRegionManager, IPlantTreePresentationModel). Those should be the only ones you have to mock to test your method. You might test this using a set of dependencies that is appropriate for your test, rather than for any test of this object.

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

The other thing you might consider is how many of these dependencies you can factor into your test's startup code (method decorated with [TestInitialize]). Some common dependencies and your container could live in a scope for your entire test suite, especially if they don't change per test.

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

Dependency injection certainly does make your life easier, even if you don't realize it. I find that a lot of people doing "unit testing" are not really doing it right and are doing functional testing due to the fact that they don't have proper isolation from other parts of their app.

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

Dependency injection almost forces you into a model where you are able to completely isolate the code you actually want to test away from all of the other parts of your app. It might cost a little up front, but the quality of your unit tests and the granularity of the feedback you are getting from them will outweigh those costs, especially when you get later in your app's lifecycle and start refactoring. You'll thank yourself then. Stick with it.

这篇关于在 Composite WPF (Prism) 中,我应该如何对控制器进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆