我的ViewModel的单元测试?

时间:2016-03-01 14:22:38

标签: c# unit-testing

我是单元测试的新手,我真的很困难,所以我真的可以使用一些帮助。

部分申请信息
我在MVVM中有一个WPF应用程序。它从数据库获取数据(通过.edmx生成的类) 所有linq查询都由Database类中的方法处理。 在CustomerListViewModel中,它列出了要在CustomerListView中显示的所有Customers。

我的问题
我是Unit Testing的新手。我已经读过它并尝试使其工作。但据我所知,它应该/可以在不触及数据库的情况下完成。我试图找到尽可能多的信息,但它不适用于我所拥有的。现在我基本上卡住了。

我的问题 我如何对这段代码进行单元测试?如何知道我是否已成功查询数据库(在单元测试中是否触摸数据库)? (如果我理解这篇文章,我可以自己计算其余的课程和方法)

代码
CustomerListViewModel:

public CustomerListViewModel()
{
    MyObservableCollection<Customer> listCustomers = new MyObservableCollection<Customer>();
    ListCustomers = App.Database.GetCustomerList();
}

private void GetListCustomers()
{
    ListCustomers = App.Database.GetCustomerList();
    if (App.Database.hasError)
        App.Messenger.NotifyColleagues("SetStatus", App.Database.errorMessage);
}

数据库:

public MyObservableCollection<Customer> GetCustomerList()
{
    hasError = false;
    MyObservableCollection<Customer> customerList = new MyObservableCollection<Customer>();
    try
    {
        QRM_Entities dc = new QRM_Entities();
        var query =
            from customers in dc.Customer
            select customers;

        foreach (Customer cust in query)
        {
            customerList.Add(cust);
        }
    }
    catch (Exception ex)
    {
        errorMessage = "GetCustomerList() error, " + ex.Message;
        hasError = true;
    }
    return customerList;
}

2 个答案:

答案 0 :(得分:3)

当前设置ViewModel的方式将使单元测试几乎不可能。 问题出在这一行:

ListCustomers = App.Database.GetCustomerList();

我认为App是静态的,而Database是您用作数据访问层的类。因此,每次调用CustomerListViewModel的构造函数时,您都会调用实际的Static App实现,在创建视图模型之前必须先设置它,这意味着您将始终在测试使用实际的数据库,这显然是你试图绕过的。

我最喜欢的软件原则Dependency Inversion Principle,其前提是解耦模块,以便您的高级模块依赖于较低级别模块的抽象。而这些细节应该取决于抽象。实际上,您应该开发一个接口并为依赖者提供此接口。

以您的示例为例,我将为您的数据库交互提取接口,并将这些接口提供给您的View模型,但我会更进一步,并将其提供给将提供给您的视图模型的模型。

了IDatabase:

public interface IDatabase
{
    IEnumerable<ICustomer> GetCustomerList();
}

ICustomerListModel:

public interface ICustomerListModel
{
    ObservableCollection<ICustomer> Customers
    {
        get;
    }
}

CustomerListModel

public class CustomerListModel : ICustomerListModel
{
    private readonly IDatabase database;

    private readonly ObservableCollection<ICustomer> customers;

    public CustomerListModel(IDatabase database)
    {
        this.database = database;
        this.customers = new ObservableCollection(database.GetCustomerList());
    }

    public ObservableCollection<ICustomer> Customers
    {
        get
        {
            return this.customers;
        }
    }
}

CustomerListViewModel

public class CustomerListViewModel
{
    private readonly ICustomerListModel customerListModel;

    public CusomterListViewModel(ICustomerListModel customerListModel)
    {
        this.customerListModel = customerListModel;
    }

    public ObservableCollection<ICustomer> Customers
    {
        get
        {
            return this.customerListModel.Customers;
        }
    }
}

所以你在这里看到的是我已经为我请求信息的数据库提取了一个界面,这意味着我不关心IDatabase的实现,我刚才当我致电ICustomer时,它为我提供了GetCustomerList()的集合。

所以我injectIDatabase的副本放入CusomterListModel课程中,然后我可以查询知道我能够正确获得我想要的内容。然后我将ICustomerListModel注入ICustomerListViewModel,以便可以将该集合呈现给View

所以为了测试CustomerListModel,我会有一个测试:

[Fact]
public void Customers_IsCorrectlyInitialisedAtStartup_Test()
{
    var databaseMock = new Mock<IDatabse>();
    var customer = new Customer();

    var customers = new [] { customer };

    databaseMock.Setup(mock => mock.GetCustomerList())
        .Returns(customers);

    var sut = new CustomerListModel(databaseMock.Object);

    Assert.Equal(customers, sut.Customers);
}

在这里我嘲笑了IDatabase的一个版本,现在你可以看到我对CustomerListModel IDatabase版本GetCustomerList()的关注程度如何我可以致电ICustomer。这有一个设置,可以在调用GetCustomerList()时调用Customers。最后,我断言IDatabase集合已正确填充了p调用的返回值。

单元测试是一门艺术,一开始很难理解,但是当你开始工作时,你会很快接受它。您可能希望了解一些有助于生成单元可测试代码并实际测试的内容:

  • Solid Principles,每个软件工程师至少应该熟悉的原则,将有助于生成可测试的代码。
  • Dependency Injection,依赖注入的wiki页面概述了将依赖项注入构造函数的优缺点,以及何时在其他示例中使用它。
  • Moq,一个友好且易于使用的C#模拟框架,我将其作为上述示例的一部分。

答案 1 :(得分:-3)

我建议你阅读这篇博文:http://blog.qmatteoq.com/the-mvvm-pattern-dependency-injection/

如果您有任何问题可以随意提问!