C# 如何对向数据库添加项的方法进行单元测试?
我有一个项目,正在使用以下方法将其添加到数据库:C# 如何对向数据库添加项的方法进行单元测试?,c#,database,unit-testing,C#,Database,Unit Testing,我有一个项目,正在使用以下方法将其添加到数据库: public Messages addItem(Item item) { Messages resultMessage = Messages.Success; using (IUnitOfWork unitOfWork = new UnitOfWork()) { IItemRepository itemRep = new ItemRepository(unitOfWork); try
public Messages addItem(Item item)
{
Messages resultMessage = Messages.Success;
using (IUnitOfWork unitOfWork = new UnitOfWork())
{
IItemRepository itemRep = new ItemRepository(unitOfWork);
try
{
itemRep.Insert(item);
unitOfWork.Commit();
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
resultMessage = Messages.DB_Failure;
}
}
return resultMessage;
}
现在,我必须为这个方法编写一个单元测试,以检查该项是否被添加到数据库中。我不知道该怎么做,有人能帮我吗?由于您的方法目前的状况,它无法进行单元测试,因为它是硬编码的,无法写入数据库
解决这个问题的常规方法是将
IItemRepository
的实例传递到方法中,而不是让方法创建它。这样做之后,您就可以自由地创建一个模拟的IItemRepository
实现,该实现可以报告正在写入DB的内容。您的代码与ItemRepository和UnitOfWork实现耦合在一起。理想情况下,您应该将它们解耦,并使用mock来验证是否调用了正确的方法
一种可能的解决办法:
class MyClass
{
private readonly IUnitOfWorkFactory _factory;
public MyClass(IUnitOfWorkFactory factory)
{
_factory = factory;
}
public Messages addItem(Item item)
{
Messages resultMessage = Messages.Success;
using (IUnitOfWork unitOfWork = _factory.GetUnitOfWork())
{
try
{
unitOfWork.ItemRep.Insert(item);
unitOfWork.Commit();
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
resultMessage = Messages.DB_Failure;
}
}
return resultMessage;
}
public void Test()
{
// Arrange
var factoryMock = new Mock<IUnitOfWorkFactory>();
var uowMock = new Mock<IUnitOfWork>();
var repositoryMock = new Mock<IItemRepository>();
factoryMock.Setup(f => f.GetUnitOfWork()).Returns(uowMock.Object);
uowMock.Setup(u => u.ItemRep).Returns(repositoryMock.Object);
var sut = new MyClass(factoryMock.Object);
// Act
var item = new Item();
sut.addItem(item);
// Assert
repositoryMock.Verify(r => r.Insert(item), Times.Once);
uowMock.Verify(u => u.Commit(), Times.Once);
}
}
class-MyClass
{
私有只读IUnitOfWorkFactory\u工厂;
公共MyClass(IUnitOfWorkFactory)
{
_工厂=工厂;
}
公共消息附加项(项目)
{
Messages resultMessage=Messages.Success;
使用(IUnitOfWork unitOfWork=\u factory.GetUnitOfWork())
{
尝试
{
unitOfWork.ItemRep.Insert(项目);
unitOfWork.Commit();
}
捕获(例外e)
{
控制台写入线(如StackTrace);
resultMessage=Messages.DB\u失败;
}
}
返回结果消息;
}
公开无效测试()
{
//安排
var factoryMock=new Mock();
var uowMock=new Mock();
var repositoryMock=new Mock();
Setup(f=>f.GetUnitOfWork()).Returns(uowMock.Object);
Setup(u=>u.ItemRep).Returns(repositoryMock.Object);
var sut=新的MyClass(factoryMock.Object);
//表演
var item=新项();
补充条款(项目);
//断言
repositoryMock.Verify(r=>r.Insert(item),Times.Once);
验证(u=>u.Commit(),次.Once);
}
}
正如其他答案所建议的那样:尝试将测试中的类与数据库等难以/缓慢测试的依赖项分开。您可以使用多种方法来实现此结果,但它们都归结为相同的方法:
不要创建(新的)依赖项,这些依赖项会使要测试自身的代码(如unitofwork/repository)中的单元测试变得困难。相反,从外部世界询问这些依赖关系(google Dependency Inversion/DI获取更多信息)
如果您想用真实的数据库测试存储库的实现,我建议您通过存储库的公共API进行测试。不要自己编写“SELECT*FROM Items”查询,而是使用repository.GetItem(…)方法(如果可用)。这样,您的测试就不那么脆弱,并且与存储库类的实际实现分离。您说目标是“检查此项是否已添加到数据库” 这是您通常不会为其编写单元测试的内容,因为它是数据库的责任,您可能不是开发数据库的人 单元测试的一个更好的例子是模拟数据库并检查决定向数据库添加内容的逻辑。例如:
这是通过使用数据库的一个模拟来实现的,它正在测试您的代码,而不是数据库。看一看。您是否只为项目编写了一个单元测试?通常,当人们谈论单元测试时,他们谈论的是测试类的行为,而不涉及数据库之类的东西。为了测试的目的,它们通常是模拟的东西,比如您的存储库。如果您确实希望将测试写入数据库,那么这通常被称为集成测试。你真正感兴趣的是哪一个?我的想法很清楚,谢谢你的例子检索数据怎么样?你不需要模拟回购协议中的功能吗?假设您调用
addItem()
五次。然后调用函数getInventory()
,该函数返回一个带有Items
类型IEnumerable
属性的ViewModel。你将如何测试它?你实际上如何测试被测试的方法不会做更多的事情?可能它在添加之前而不是之后调用Save(),这将是一个bug。或者这超出了单元测试本身的范围?@stackcrash取决于您的模拟库。但您始终可以创建自己的模拟类来实现存储库,并检查方法的调用顺序是否正确。