【问题标题】:Proper use of MOQ in a Unit Test在单元测试中正确使用 MOQ
【发布时间】:2013-08-16 23:31:00
【问题描述】:

鉴于以下情况,这是正确使用最小起订量吗?我对“嘲笑”、“存根”、“假装”等很陌生,只是想把头绕过去。

我的理解是这个模拟提供了一个已知的结果,所以当我使用它测试这个服务时,服务会正确反应吗?

public interface IRepository<T> where T : class
{
    void Add(T entity);
    void Delete(T entity);
    void Update(T entity);
    IQueryable<T> Query();
}

public interface ICustomerService
{
    void CreateCustomer(Customer customer);
    Customer GetCustomerById(int id);
}

public class Customer
{
    public int Id { get; set; }

}

public class CustomerService : ICustomerService
{
    private readonly IRepository<Customer> customerRepository;

    public CustomerService(IRepository<Customer> customerRepository)
    {
        this.customerRepository = customerRepository;
    }

    public Customer GetCustomerById(int id)
    {
        return customerRepository.Query().Single(x => x.Id == id);
    }

    public void CreateCustomer(Customer customer)
    {
        var existingCustomer = customerRepository.Query().SingleOrDefault(x => x.Id == customer.Id);

        if (existingCustomer != null)
            throw new InvalidOperationException("Customer with that Id already exists.");

        customerRepository.Add(customer);
    }
}

public class CustomerServiceTests
    {
        [Fact]
        public void Test1()
        {
            //var repo = new MockCustomerRepository();
            var repo = new Mock<IRepository<Customer>>();
            repo.Setup(x => x.Query()).Returns(new List<Customer>() { new Customer() { Id = 1 }}.AsQueryable());

            var service = new CustomerService(repo.Object);

            Action a = () => service.CreateCustomer(new Customer() { Id = 1 });

            a.ShouldThrow<InvalidOperationException>();

        }
    }

我正在使用 xUnit、FluentAssertions 和 MOQ。

【问题讨论】:

  • 测试对我来说很好。用例的有效测试。
  • 我建议您让测试名称代表正在测试的内容。让您稍后回到测试时更容易理解。因此,不要将“Test1”称为“CannotCreateACustomerWithSameIdAsExistingCustomer”

标签: c# unit-testing moq xunit xunit.net


【解决方案1】:

我的理解是这个模拟提供了一个已知的结果, 那么当我使用它测试该服务时,该服务会正确反应吗?

这句话是正确的 - 单元测试应该验证您正在测试的类(在本例中为 CustomerService)是否表现出您想要的行为。它并非旨在验证其依赖项是否按预期运行(在本例中为IRepository&lt;Customer&gt;)。

您的测试很好* - 您正在为 IRepository 设置模拟并注入您的 SystemUnderTest,并验证 CustomerService.CreateCustomer() 函数是否表现出您期望的行为。

*测试的整体设置很好,但我对xUnit不熟悉,所以最后两行的语法对我来说是陌生的,但从语义上看起来它是正确的。作为参考,您可以像这样在 NUnit 中执行最后两行:

Assert.Throws<InvalidOperationException>(() => service.CreateCustomer(...));

【讨论】:

    【解决方案2】:

    测试对我来说很好,模拟只是提供了一个虚假的存储库,它只为测试返回一个硬编码的答案,所以测试只关心你正在测试的服务而不处理真实​​的数据库或者其他什么,因为你没有在这里测试它。

    我只会在测试中添加一件事以使其更加完整。 当您在模拟上设置方法调用时,请确保它们确实由被测系统调用。毕竟,服务应该向 repo 请求某个对象,并且只在某个返回值下抛出。 Moq 特别为此提供了一种语法:

    repo.VerifyAll();
    

    它所做的只是检查您之前放置的设置是否至少被调用过一次。这可以保护您免受服务立即抛出异常而不调用 repo 的错误(在像您这样的示例中很容易发现,但是使用复杂的代码很容易错过调用)。使用该行,在测试结束时,如果您的服务没有调用 repo 请求列表(并且使用特定的参数集),即使正确抛出异常,测试也会失败。

    【讨论】:

      猜你喜欢
      • 2011-12-24
      • 2015-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-13
      • 2014-05-02
      • 1970-01-01
      相关资源
      最近更新 更多