我正在使用一个模块构建基本的复合WPF Shell。我想对我的模块进行单元测试。显然,复合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容器的构造函数注入传递到我的Controller。为了配置它并做一个简单的测试,我的单元测试看起来如下......
(看看这个方法的长度!肯定有一个更好的测试方法吗?复合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();
}
有没有更好的方法来测试我的课程?任何建议将不胜感激。
答案 0 :(得分:1)
我自己遇到了很多依赖的类。实际上并没有什么办法。
你的方法真的依赖于所有这些类吗?我只看到这里使用了两个或三个依赖项(IRegionManager,IPlantTreePresentationModel)。这些应该是你拥有来模拟测试方法的唯一方法。您可以使用一组适合您的测试的依赖项来测试它,而不是对该对象的任何测试。
您可能会考虑的另一件事是,您可以将多少这些依赖项纳入测试的启动代码(使用[TestInitialize]修饰的方法)。一些常见的依赖项和容器可以存在于整个测试套件的范围内,特别是如果它们不会因每次测试而改变。
依赖注入肯定会让您的生活更轻松,即使您没有意识到这一点。我发现很多人在进行“单元测试”并没有真正做到正确并且正在进行功能测试,因为他们没有与应用程序的其他部分进行适当的隔离。
依赖注入几乎会迫使您进入一个模型,在这个模型中,您可以完全隔离实际想要测试的代码,使其远离应用程序的所有其他部分。它可能需要花费一些时间,但是单元测试的质量以及从中获得的反馈的粒度将超过这些成本,尤其是当您在应用程序的生命周期中稍后开始重构时。那么你会感谢你自己。坚持下去。