【问题标题】:How to unit test using a ViewModelLocator如何使用 ViewModelLocator 进行单元测试
【发布时间】:2014-01-12 21:36:33
【问题描述】:

我使用 Autofac 创建了一个自定义视图模型定位器,并通过 App.xaml 正常设置它,就像大多数通常使用的那样。我的问题是我现在如何进行单元测试?每次我尝试测试初始化​​视图的方法时都会出错

在我的 app.xaml 中:

<desktop:ViewModelLocator xmlns:local="clr-namespace:MyProject.Desktop" x:Key="ViewModelLocator" />

在每个视图中:

DataContext="{Binding MyFirstViewModel, Source={StaticResource ViewModelLocator}}"

单元测试错误:

{"Cannot find resource named 'ViewModelLocator'. Resource names are case sensitive."}

我明白为什么在您进行单元测试时,实际上没有实际应用程序的实例,那么有什么好的方法可以解决这个问题?

ViewModelLocator 代码:

/// <summary>
/// Autofac object container
/// </summary>
private readonly IContainer objectContainer;

#region Constructor

/// <summary>
/// Constructor for view model locator
/// </summary>
public ViewModelLocator()
{
    objectContainer = App.ObjectContainer;
    //objectContainer.BeginLifetimeScope();
}

#endregion

#region Properties

/// <summary>
/// Gets the resolved instance of a main window view model
/// </summary>
public MainWindowViewModel MainWindowViewModel
{
    get
    {
        return objectContainer.Resolve<MainWindowViewModel>();
    }
}

public FirstViewModel MyFirstViewModel 
{
    get
    {
        return objectContainer.Resolve<FirstViewModel>();
    }
}

public SecondViewModel MySecondViewModel 
{
    get
    {
        return objectContainer.Resolve<SecondViewModel>();
    }
}

【问题讨论】:

  • 单元测试应该针对ViewModel class (business logic)而不是View。如果在您的 ViewModel 类中您正在初始化一个视图,那么设计中就存在严重错误。
  • 是的,但是这个视图模型更像是一个容器视图模型,它动态创建/设置视图 (IView) 并在 xaml 的内容控件中使用它们
  • 我确实明白你的观点,我会尝试看看我是否可以选择在 xaml 中设置我的视图而不是视图
  • 我也喜欢在适用的视图模型中设置我的视图,因为这是我可以对实际视图进行单元测试的最接近的东西。现在我知道你会说什么“我为什么还要尝试测试我的观点?”测试覆盖率越高,应用程序的质量就越好。一切尽在其中。
  • 这完全是基于意见的。如果您喜欢这种方式,那是您的选择。我不会对此发表评论。圣诞快乐..!!

标签: wpf unit-testing mvvm viewmodellocator


【解决方案1】:

这有点晚了,但也许有用。而不是在构造函数中解析objectContainer,而是通过属性来完成:

//note this is a lazy getter, i.e. will be resolved when needed on the first call
private IContainer ObjectContainer
{
   get
   {
       if(objectContainer == null)
           objectContainer = App.ObjectContainer;
       return objectContainer:
   }
}

然后通过您的代码使用属性,而不是字段。此外,当我担心其他人使用我想通过属性使用强制执行的字段时,我会将其重命名为在 IntelliSence 中不易识别的名称(例如 zREgdnlksfObjectContainer :))注意该属性是私有的,所以没有什么真正改变。您可以将属性设为内部并将您的库标记为对单元测试可见,以便在单元测试中您可以将其模拟为WhenCalled()return/resolve IContainer。

【讨论】:

    猜你喜欢
    • 2012-01-08
    • 2021-03-15
    • 2020-01-06
    • 2021-11-16
    • 2019-12-11
    • 1970-01-01
    • 2011-08-04
    • 2021-09-22
    • 2018-12-31
    相关资源
    最近更新 更多