我是单元测试的新手,我真的很困难,所以我真的可以使用一些帮助。
部分申请信息
我在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;
}
答案 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()
的集合。
所以我inject
将IDatabase
的副本放入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
调用的返回值。
单元测试是一门艺术,一开始很难理解,但是当你开始工作时,你会很快接受它。您可能希望了解一些有助于生成单元可测试代码并实际测试的内容:
答案 1 :(得分:-3)
我建议你阅读这篇博文:http://blog.qmatteoq.com/the-mvvm-pattern-dependency-injection/
如果您有任何问题可以随意提问!