在单元测试中正确使用MOQ

时间:2013-08-16 23:31:00

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

鉴于以下情况,这是MOQ的正确使用吗?我对“嘲弄”,“瞎扯”,“假装”等非常陌生,只是试图绕过它。

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

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。

2 个答案:

答案 0 :(得分:3)

  

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

这个陈述是正确的 - 单元测试应该验证您正在测试的类(在这种情况下,CustomerService)是否表现出您想要的行为。它无意验证其依赖项是否按预期运行(在本例中为IRepository<Customer>)。

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

*测试的总体设置很好,但是我不熟悉xUnit,所以最后两行的语法对我来说很陌生,但看起来它基于语义是正确的。作为参考,您将在NUnit中执行最后两行,如下所示:

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

答案 1 :(得分:1)

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

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

repo.VerifyAll();

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