如何对向数据库添加项的方法进行单元测试?

本文关键字:方法 单元测试 数据库 添加 | 更新日期: 2023-09-27 17:50:18

我有一个项目,我正在使用这个方法将它添加到数据库:

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;   
}

现在我必须为这个方法编写一个单元测试,以检查项目是否被添加到数据库中。我不知道该怎么做,有人能帮我吗?

如何对向数据库添加项的方法进行单元测试?

您的代码与ItemRepository和UnitOfWork实现耦合在一起。理想情况下,您应该将它们解耦,并使用模拟来验证调用了正确的方法。

一个可能的解决方案:

  1. 将Repository设置为工作单元的属性
  2. 不要直接创建工作单元,使用工厂创建
  3. 使工厂成为类的依赖项
  4. 在您的测试中,将工厂的模拟传递给您正在测试的类,该类返回工作单元的模拟
  5. 返回UoW模拟上的存储库模拟
  6. 验证在您的存储库模拟和工作单元模拟上调用了正确的方法

这就是一个例子。我使用Moq作为mock框架。然后把test方法放在类中,但是你可以明白这个意思:

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);
    }
}

您说目标是"检查此条目是否已添加到数据库"。

这是你通常不写单元测试的事情,因为它是数据库的责任,想必你不是开发的人。

一个更好的单元测试案例是模拟数据库并检查决定向数据库添加内容的逻辑。例如:

  1. 工作单元由客户/操作员描述。
  2. 组件向数据库查询项目是否存在。
  3. 对应项不存在
  4. 你的组件将条目添加到数据库。

这是通过使用数据库的模拟来实现的,它正在测试您的代码,而不是数据库。

作为您的方法目前的状态,它不能被单元测试,因为它是硬编码写入数据库。

解决这个问题的常规方法是将IItemRepository的实例传递给方法,而不是让方法创建它。这样做,然后你就可以自由地创建一个模拟的IItemRepository实现,可以报告正在写入DB的内容。

正如其他答案所建议的那样:尝试将被测试的类与数据库等难以/缓慢测试的依赖项分开。你可以使用许多方法来达到这个结果,但它们都归结为相同的:不要在想要测试的代码中创建(新建)依赖项,这会使单元测试变得困难(比如单元工作/存储库)。相反,从外部世界询问这些依赖项(google Dependency Inversion/DI获取更多信息)。

如果您想用真实的数据库测试存储库的实现,我建议您通过存储库的公共API进行测试。不要自己编写"SELECT * FROM Items"查询,而是使用repository.GetItem(…)方法。这样,您的测试就不那么脆弱,并且与存储库类的实际实现解耦。