简体   繁体   English

与Moq模拟懒惰的界面

[英]Mock lazy interface with Moq

I want mock lazy interface but I got object reference not set to an instance of an object exception. 我想要模拟懒惰的接口,但我没有将object reference not set to an instance of an object异常object reference not set to an instance of an object

‌Here is class under test: 这是被测试的课程:

public class ProductServiceService : IProductServiceService
{
    private readonly Lazy<IProductServiceRepository> _repository;
    private readonly Lazy<IProductPackageRepository> _productPackageRepository;

    public ProductServiceService(
        Lazy<IProductServiceRepository> repository,
        Lazy<IProductPackageRepository> productPackageRepository)
    {
        _repository = repository;
        _productPackageRepository = productPackageRepository;
    }

    public async Task<OperationResult> ValidateServiceAsync(ProductServiceEntity service)
    {
        var errors = new List<ValidationResult>();

        if (!await _productPackageRepository.Value.AnyAsync(p => p.Id == service.PackageId))
            errors.Add(new ValidationResult(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));

       .
       .
       .

        return errors.Any()
            ? OperationResult.Failed(errors.ToArray())
            : OperationResult.Success();
    }
}

and here is test class 这是测试课

[Fact, Trait("Category", "Product")]
public async Task Create_Service_With_Null_Financial_ContactPerson_Should_Fail()
{
    // Arrange
    var entity = ObjectFactory.Service.CreateService(packageId: 1);

    var fakeProductServiceRepository = new Mock<Lazy<IProductServiceRepository>>();

    var repo= new Mock<IProductPackageRepository>();
    repo.Setup(repository => repository.AnyAsync(It.IsAny<Expression<Func<ProductPackageEntity, bool>>>()));
    var fakeProductPackageRepository  = new Lazy<IProductPackageRepository>(() => repo.Object);

    var sut = new ProductServiceService(fakeProductServiceRepository.Object, fakeProductPackageRepository);

    // Act
    var result = await sut.AddServiceAsync(service);

    // Assert
    Assert.False(result.Succeeded);
    Assert.Contains(result.ErrorMessages, error => error.Contains(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));
}

fakeProductPackageRepository always is null. fakeProductPackageRepository始终为null。 I followed this blog post but still I'm getting null reference exception. 我关注了这篇博文,但我仍然得到null引用异常。

How to mock lazy initialization of objects in C# unit tests using Moq 如何使用Moq在C#单元测试中模拟对象的延迟初始化

Update: here is a screen that indicates fakeProductPackageRepository is null. 更新:这是一个屏幕,指示fakeProductPackageRepository为null。 在此输入图像描述 在此输入图像描述

Here is a refactored version of your example: 以下是您的示例的重构版本:

[Fact, Trait("Category", "Product")]
public async Task Create_Service_With_Null_Financial_ContactPerson_Should_Fail() {
    // Arrange
    var entity = ObjectFactory.Service.CreateService(packageId = 1);

    var productServiceRepositoryMock = new Mock<IProductServiceRepository>();

    var productPackageRepositoryMock = new Mock<IProductPackageRepository>();
    productPackageRepositoryMock
        .Setup(repository => repository.AnyAsync(It.IsAny<Expression<Func<ProductPackageEntity, bool>>>()))
        .ReturnsAsync(false);

    //Make use of the Lazy<T>(Func<T>()) constructor to return the mock instances
    var lazyProductPackageRepository = new Lazy<IProductPackageRepository>(() => productPackageRepositoryMock.Object);
    var lazyProductServiceRepository = new Lazy<IProductServiceRepository>(() => productServiceRepositoryMock.Object);

    var sut = new ProductServiceService(lazyProductServiceRepository, lazyProductPackageRepository);

    // Act
    var result = await sut.AddServiceAsync(service);

    // Assert
    Assert.False(result.Succeeded);
    Assert.Contains(result.ErrorMessages, error => error.Contains(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));
}

UPDATE UPDATE

The following Minimal, Complete, and Verifiable example of your stated issue passes when tested. 在测试时,您声明的问题的以下最小,完整和可验证示例将通过。

[TestClass]
public class MockLazyOfTWithMoqTest {
    [TestMethod]
    public async Task Method_Under_Test_Should_Return_True() {
        // Arrange
        var productServiceRepositoryMock = new Mock<IProductServiceRepository>();

        var productPackageRepositoryMock = new Mock<IProductPackageRepository>();
        productPackageRepositoryMock
            .Setup(repository => repository.AnyAsync())
            .ReturnsAsync(false);

        //Make use of the Lazy<T>(Func<T>()) constructor to return the mock instances
        var lazyProductPackageRepository = new Lazy<IProductPackageRepository>(() => productPackageRepositoryMock.Object);
        var lazyProductServiceRepository = new Lazy<IProductServiceRepository>(() => productServiceRepositoryMock.Object);

        var sut = new ProductServiceService(lazyProductServiceRepository, lazyProductPackageRepository);

        // Act
        var result = await sut.MethodUnderTest();

        // Assert
        Assert.IsTrue(result);
    }

    public interface IProductServiceService { }
    public interface IProductServiceRepository { }
    public interface IProductPackageRepository { Task<bool> AnyAsync();}

    public class ProductServiceService : IProductServiceService {
        private readonly Lazy<IProductServiceRepository> _repository;
        private readonly Lazy<IProductPackageRepository> _productPackageRepository;

        public ProductServiceService(
            Lazy<IProductServiceRepository> repository,
            Lazy<IProductPackageRepository> productPackageRepository) {
            _repository = repository;
            _productPackageRepository = productPackageRepository;
        }

        public async Task<bool> MethodUnderTest() {
            var errors = new List<ValidationResult>();

            if (!await _productPackageRepository.Value.AnyAsync())
                errors.Add(new ValidationResult("error"));

            return errors.Any();
        }
    }
}

A Lazy<> as a parameter is somewhat unexpected, though not illegal (obviously). 作为参数的Lazy<>有些出乎意料,虽然不是非法的(显然)。 Remember that a Lazy<> wrapped around a service is really just deferred execution of a Factory method. 请记住,围绕服务的Lazy<>实际上只是延迟执行Factory方法。 Why not just pass the factories to the constructor? 为什么不将工厂传递给构造函数? You could still wrap the call to the factory in a Lazy<> inside your implementation class, but then you can just fake / mock your factory in your tests and pass that to your sut . 您仍然可以在实现类中的Lazy<>中将调用包装到工厂,但是您可以在测试中伪造/模拟您的工厂并将其传递给您的sut

Or, perhaps the reason that you're passing around a Lazy<> is because you're really dealing with a singleton. 或者,也许是因为你正在处理一个单身人士,你可能是因为你正在处理一个Lazy<> In that case, I'd still create a factory and take dependencies on the IFactory<> . 在那种情况下,我仍然会创建一个工厂并依赖于IFactory<> Then, the factory implementation can include the Lazy<> inside of it. 然后,工厂实现可以在其中包含Lazy<>

Often, I solve the singleton requirement (without the lazy loading) via setting a custom object scope for the dependency in my IoC container. 通常,我通过在IoC容器中为依赖项设置自定义对象作用域来解决单例要求(没有延迟加载)。 For instance, StructureMap makes it easy to set certain dependencies as singleton or per-request-scope in a web application. 例如,StructureMap可以轻松地将某些依赖关系设置为Web应用程序中的singleton或per-request-scope。

I rarely need to assert that I've done a lazy initialization on some service inside of a system-under-test. 我很少需要断言我在被测系统内的某些服务上做了一个懒惰的初始化。 I might need to verify that I've only initialized a service once per some scope, but that's still easily tested by faking the factory interface. 我可能需要验证我是否只在某个范围内初始化了一次服务,但是仍然可以通过伪造工厂界面来轻松测试。

The thing is that you are creating a Mock of Lazy as fakeProductServiceRepository and later on are returning that instance where just a Mock is needed. 问题是你正在创建一个模拟懒人作为fakeProductServiceRepository,稍后将返回那个只需要一个模拟的实例。

You should change 你应该改变

var fakeProductServiceRepository = new Mock<Lazy<IProductServiceRepository>>();

to

var fakeProductServiceRepository = new Mock<IProductServiceRepository>();

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

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