简体   繁体   English

在单元测试中正确使用最小起订量

[英]Proper use of MOQ in a Unit Test

Given the following, is this the proper use of MOQ? 鉴于以下情况,这是否是最小起订量的正确使用? I am very new to "mocking", "stubbing", "faking", etc. and just trying to wrap my head around it. 我对“嘲笑”,“窃听”,“伪造”等等非常陌生,只是想把我的头缠住。

The way I understand it is that this mock is providing a known result, so when I test this service using it, the service reacts properly? 我的理解是,此模拟提供了已知的结果,因此当我使用该模拟测试该服务时,该服务是否能够正确响应?

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

        }
    }

I am using xUnit, FluentAssertions and MOQ. 我正在使用xUnit,FluentAssertions和MOQ。

The way I understand it is that this mock is providing a known result, so when I test this service using it, the service reacts properly? 我的理解是,此模拟提供了已知的结果,因此当我使用该模拟测试该服务时,该服务是否能够正确响应?

This statement is correct - the unit test should be verifying that the class you're testing (in this case, CustomerService ) is exhibiting the behavior you desire. 该声明是正确的-单元测试应验证您正在测试的类(在本例中为CustomerService )是否表现出所需的行为。 It's not intended to verify that its dependencies are behaving as expected (in this case, IRepository<Customer> ). 它并不是要验证其依赖项是否按预期方式运行(在这种情况下,是IRepository<Customer> )。

Your test is good* - you're setting up your mock for the IRepository and injecting into your SystemUnderTest, and verifying that the CustomerService.CreateCustomer() function is exhibiting the behavior that you expect. 您的测试很好*-您正在为IRepository设置模拟并将其注入到SystemUnderTest中,并验证CustomerService.CreateCustomer()函数是否表现出预期的行为。

*The overall setup of the test is fine, but I'm not familiar with xUnit, so the final two line's syntax is foreign to me, but it looks like it's correct based on the semantics. *测试的总体设置很好,但是我对xUnit并不熟悉,所以最后两行的语法对我来说是陌生的,但根据语义看来它是正确的。 For reference, you would do the last two lines in NUnit like so: 作为参考,您可以在NUnit中执行最后两行,如下所示:

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

The test looks fine to me, the mock just provides a fake repository that returns a hardcoded answer just for the test, so the test only cares about the service you're testing and don't deals with a real-life database or whatever, since you're not testing it here. 该测试对我来说看起来不错,该模拟程序仅提供了一个伪造的存储库,该存储库仅为该测试返回了一个硬编码的答案,因此该测试仅关心您正在测试的服务,而不处理真实​​数据库或任何其他内容,因为您不在这里进行测试。

I would only add one thing to the test to be even more complete. 我只会在测试中添加一件事以使其更加完整。 When you setup method calls on the mocks, make sure they were really called by the system under test . 在模拟程序上设置方法调用时,请确保被测试的系统确实调用了它们 After all, the service is supposed to ask the repo for some object and throw only under a certain return value. 毕竟,该服务应该向存储库询问某些对象,并仅在某个返回值下抛出。 Moq in particular provides a syntax for this: Moq特别为此提供了一种语法:

repo.VerifyAll();

What this does is simply checking that the setups you've placed before were actually called at least once. 这样做只是检查您之前放置的设置是否至少实际被调用过一次。 This can protect you from errors where the service just throws the exception right away without calling the repo (easy to spot in examples like yours, but with complex code it's easy to miss a call). 这可以保护您避免错误,即服务不立即调用该仓库就立即抛出异常(在您的示例中很容易发现,但是使用复杂的代码很容易错过一个调用)。 With that line, at the end of your test, if your service didn't called the repo asking for the list (and with that specific set of parameters), the test will fail too, even if the exception was properly thrown. 通过该行,在测试结束时,如果您的服务没有调用存储库的清单(以及特定的参数集),即使正确抛出了异常,测试也将失败。

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

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