【发布时间】:2012-06-24 20:36:02
【问题描述】:
我正在 WPF 中构建类似 Visual Studio 的应用程序,但在确定组件的最佳架构设计组织时遇到了一些问题。我计划使用 Unity 作为我的依赖注入容器和 Visual Studio 单元测试框架,并且可能使用 moq 来模拟库。
我将首先描述我的解决方案的结构,然后是我的问题:
我有一个 WPF 项目,其中包含:
- 我的 Unity 容器在应用程序启动时初始化(引导程序)(在 App.xaml.cs 中)
- 我的所有应用程序视图 (XAML)。
另一个名为 ViewModel 的项目包含:
- 我的所有应用程序视图模型。我所有的 ViewModel 都继承自 ViewModelBase,它公开了 ILogger 属性
我的初始化逻辑如下:
- 应用程序启动
- Unity 容器创建和注册类型:MainView 和 MainViewModel
- 解决我的 MainView 并显示它。
var window = Container.Resolve<MainView>();
window.Show();
我的 MainView 构造函数在其构造函数中接收到一个 MainViewModel 对象:
public MainView(MainViewModel _mvm)
-
我的 MainViewModel 的每个面板都有一个子 ViewModel:
public ToolboxViewModel ToolboxVM{get; set;} public SolutionExplorerViewModel SolutionExplorerVM { get; set; } public PropertiesViewModel PropertiesVM { get; set; } public MessagesViewModel MessagesVM { get; set; }
我打算创建一个 InitializePanels() 方法来初始化每个面板。
现在我的问题是: 我的 MainViewModel.InitializePanels() 如何初始化所有这些面板?给定以下选项:
选项 1: 手动初始化 ViewModel:
ToolboxVM = new ToolboxViewModel();
//Same for the rest of VM...
缺点:
- 我没有使用 Unity 容器,因此我的依赖项(例如 ILogger)不会自动解析
选项 2: 通过注释我的属性来使用 setter 注入:
[Dependency]
public ToolboxViewModel ToolboxVM{get; set;}
//... Same for rest of Panel VM's
缺点:
- 我读过应该避免 Unity Setter 依赖项,因为在这种情况下它们会与 Unity 产生依赖关系
- 我还读到您应该避免使用 Unity 进行单元测试,那么如何在我的单元测试中明确这种依赖关系?拥有许多依赖属性可能是一场噩梦。
选项 3: 使用 Unity 构造函数注入将我的所有面板视图模型传递给 MainViewModel 构造函数,以便它们由 Unity 容器自动解析:
public MainViewModel(ToolboxViewModel _tbvm, SolutionExploerViewModel _sevm,....)
优点:
- 依赖关系在创建时会很明显且清晰,这有助于构建我的 ViewModel 单元测试。
缺点:
- 有这么多的构造函数参数很快就会变得丑陋
选项 4: 在容器构建时注册我的所有 VM 类型。然后通过构造函数注入将 UnityContainer 实例传递给我的 MainViewModel:
public MainViewModel(IUnityContainer _container)
这样我可以做这样的事情:
Toolbox = _container.Resolve<ToolboxViewModel>();
SolutionExplorer = _container.Resolve<SolutionExplorerViewModel>();
Properties = _container.Resolve<PropertiesViewModel>();
Messages = _container.Resolve<MessagesViewModel>();
缺点:
- 如果我像许多人建议的那样决定不将 Unity 用于我的单元测试,那么我将无法解析和初始化我的面板视图模型。
鉴于这个冗长的解释,我可以利用依赖注入容器并最终获得可单元测试的解决方案的最佳方法是什么?
提前致谢,
【问题讨论】:
-
你完全正确。我应该针对接口而不是具体实现进行编码,但是,我通常开始对具体类进行编码,然后使用 Resharper 提取它们的接口,还没有达到这一点,但我很快就会!
-
我对此的快速而肮脏的解决方案是将 app.xaml 中的视图模型实例化为资源,然后根据需要进行组合。
<t:ConsoleViewModel x:Key="cvm"/> <t:MainViewModel Console="{StaticResource cvm}" />这使得单元测试易于设置。
标签: c# unit-testing architecture mvvm unity-container