我的视图模型的单元测试
本文关键字:单元测试 模型 视图 我的 | 更新日期: 2023-09-27 18:36:50
我是单元测试的新手,我真的被困在 atm 上,所以我真的可以使用一些帮助。
一些应用程序信息
我在 MVVM 中有一个 WPF 应用程序。它从数据库(通过 .edmx 生成的类)获取数据。
所有 linq 查询都由数据库类中的方法处理。在 CustomerListViewModel 中,它列出了要在 CustomerListView 中显示的所有客户。
我的问题
我是单元测试的新手。我已经读过它并试图让它工作。但据我了解,它应该/可以在不接触数据库的情况下完成。我试图找到尽可能多的信息,但它不适用于我所拥有的。现在我基本上被困住了。
我的问题如何对这段代码进行单元测试?如何知道是否已成功查询数据库(在单元测试中是否触及数据库)?
(如果我理解了这篇文章,我可以自己弄清楚其余的类和方法)
代码
客户列表视图模型:
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;
}
您当前设置ViewModel
的方式将使单元测试几乎不可能。问题出在这一行上:
ListCustomers = App.Database.GetCustomerList();
我认为App
是静态的,Database
是您用作数据访问层的类。因此,每次调用CustomerListViewModel
的构造函数时,您都会调用App
的实际Static
实现,您必须在创建视图模型之前设置这些实现,这意味着您将始终使用实际的数据库进行测试,这显然是您试图绕过的。
在我最喜欢的软件原理中,依赖反转原理,其前提是解耦模块,以便您的高级模块依赖于较低级别模块的抽象。而这些细节应该取决于这种抽象。实际上,您应该开发到一个接口,并将此接口提供给依赖项。
以您的示例为例,我将为您的数据库交互提取接口并将其提供给您的视图模型,但我会更进一步,将其提供给将提供给您的视图模型的模型。
IDatabase:
public interface IDatabase
{
IEnumerable<ICustomer> GetCustomerList();
}
ICustomerListModel:
public interface ICustomerListModel
{
ObservableCollection<ICustomer> Customers
{
get;
}
}
客户列表模型
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;
}
}
}
客户列表视图模型
public class CustomerListViewModel
{
private readonly ICustomerListModel customerListModel;
public CusomterListViewModel(ICustomerListModel customerListModel)
{
this.customerListModel = customerListModel;
}
public ObservableCollection<ICustomer> Customers
{
get
{
return this.customerListModel.Customers;
}
}
}
所以你可以在这里看到的是,我已经为我请求信息的数据库提取了一个接口,这意味着我不关心IDatabase
的实现,我刚才在我调用GetCustomerList()
时它为我提供了ICustomer
的集合。
因此,我将IDatabase
的副本inject
到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()
。这有一个设置,用于在调用GetCustomerList()
时返回ICustomer
。最后,我断言 Customers
集合已正确填充了IDatabase
调用的返回。
单元测试是一门艺术,一开始很难理解,但是当你一开始让它工作时,你会很快学会它。您可能希望查看一些内容,以帮助您生成单元可测试代码并实际测试:
- 坚实的原则,每个软件工程师至少应该熟悉的原则,将有助于生成可测试的代码。 依赖注入,
- 关于依赖注入的维基页面,概述了将依赖注入构造函数的利弊,何时以及如何在进一步的示例中使用它。
- Moq,一个友好且易于使用的 C# 模拟框架,我将其用作上面示例的一部分。