简体   繁体   English

如何单元测试将项添加到数据库的方法?

[英]How do I unit test a method to add an item to a database?

I have an item and I am adding it to the database using this method: 我有一个项目,我使用此方法将其添加到数据库:

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

Now I have to make write a unit test for this method to check if the item is being added to the database. 现在,我必须为此方法编写单元测试,以检查项目是否正在添加到数据库中。 I have no idea how I should do that, can someone help me? 我不知道我应该怎么做,有人可以帮助我吗?

Your code is coupled with the ItemRepository and the UnitOfWork implementations. 您的代码与ItemRepository和UnitOfWork实现相结合。 Ideally you should decouple them and use mocks to verify that the right methods are called. 理想情况下,您应该将它们分离并使用模拟来验证是否调用了正确的方法。

A possible solution: 可能的解决方案:

  1. Make the Repository a property on your unit of work 使存储库成为您工作单元的属性
  2. Don't create the Unit of Work directly, use a factory for that 不要直接创建工作单元,为此使用工厂
  3. Make the factory a dependency of your class 使工厂成为您班级的依赖项
  4. In your test pass a mock of the factory to the class you are testing that returns a mock of the Unit Of Work 在您的测试中,将工厂的模拟传递给您正在测试的类,返回模拟工作单元
  5. Return a mock of the Repository on your UoW mock 在您的UoW模拟上返回存储库的模拟
  6. Verify that the right methods are called on your Repository mock and Unit of Work mocks 验证是否在Repository mock和Unit of Work模拟中调用了正确的方法

This would be an example. 这将是一个例子。 I have used Moq as the mocking framework. 我使用Moq作为模拟框架。 And put the test method inside the class, but you can get the idea: 并将测试方法放在课堂上,但你可以得到这个想法:

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

You say that the goal is to "check if this item is added to the database". 您说目标是“检查此项是否已添加到数据库”。

This is something you do not normally write a unit test for because it is the responsibility of the database, which presumably you are not the one developing. 这是你通常不会编写单元测试的东西,因为它是数据库的责任,可能你不是那个正在开发的数据库。

A better case for a unit test is to mock out the database and check the logic that decides to add something to the database. 单元测试的一个更好的例子是模拟数据库并检查决定向数据库添加内容的逻辑。 For instance: 例如:

  1. A Unit of Work is described by a customer/operator. 工作单元由客户/操作员描述。
  2. Your component queries the database for the existence of the item. 您的组件在数据库中查询项目是否存在。
  3. No corresponding item exists. 没有相应的项目。
  4. Your component adds the item to the database. 您的组件将该项添加到数据库。

This is achieved by using just a mock of the database and it is testing your code, rather than the database. 这是通过仅使用数据库的模拟来实现的,它正在测试您的代码,而不是数据库。

As your method currently stands, it cannot be unit tested as it's hard-coded to write to the database. 正如您目前所使用的方法一样,它无法进行单元测试,因为它是硬编码的,可以写入数据库。

The conventional way around this is to pass an instance of IItemRepository into the method, rather than having the method create it. 传统方法是将IItemRepository的实例传递给方法,而不是让方法创建它。 Do that and then you are free to create a mocked IItemRepository implementation that can report what's being written to the DB. 这样做然后你可以自由地创建一个IItemRepository实现,它可以报告写入数据库的内容。

As other answers suggested: try to separate your class under test from difficult/slow to test dependencies like the database. 正如其他答案所建议的那样:尝试将测试中的类与困难/慢速分开,以测试数据库之类的依赖关系。 You can use a number of approaches to achieve this result, but they all come down to the same: Don't create (new up) dependencies that make unit testing difficult in the code you want to test itself (like your unitofwork/repository). 您可以使用多种方法来实现此结果,但它们都归结为相同:不要创建(新的)依赖项,使您希望自己测试的代码中的单元测试变得困难(例如您的unitofwork / repository) 。 Rather, ask these dependencies from the outside world (google Dependency Inversion/DI for further info). 相反,请问外部世界的这些依赖关系(google Dependency Inversion / DI获取更多信息)。

If you want to test the implementation of the repository with a real database, I suggest you test through the public API of your repository. 如果您想使用真实数据库测试存储库的实现,我建议您通过存储库的公共API进行测试。 Don't go writing "SELECT * FROM Items" queries yourself, but use a repository.GetItem(...) method if available. 不要自己编写“SELECT * FROM Items”查询,但如果可用,请使用repository.GetItem(...)方法。 That way your tests are less brittle and decoupled from the actual implementation of your repository class. 这样,您的测试就不那么脆弱,并且与存储库类的实际实现分离。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM