繁体   English   中英

与Moq模拟懒惰的界面

[英]Mock lazy interface with Moq

我想要模拟懒惰的接口,但我没有将object reference not set to an instance of an object异常object reference not set to an instance of an object

这是被测试的课程:

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

这是测试课

[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始终为null。 我关注了这篇博文,但我仍然得到null引用异常。

如何使用Moq在C#单元测试中模拟对象的延迟初始化

更新:这是一个屏幕,指示fakeProductPackageRepository为null。 在此输入图像描述 在此输入图像描述

以下是您的示例的重构版本:

[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

在测试时,您声明的问题的以下最小,完整和可验证示例将通过。

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

作为参数的Lazy<>有些出乎意料,虽然不是非法的(显然)。 请记住,围绕服务的Lazy<>实际上只是延迟执行Factory方法。 为什么不将工厂传递给构造函数? 您仍然可以在实现类中的Lazy<>中将调用包装到工厂,但是您可以在测试中伪造/模拟您的工厂并将其传递给您的sut

或者,也许是因为你正在处理一个单身人士,你可能是因为你正在处理一个Lazy<> 在那种情况下,我仍然会创建一个工厂并依赖于IFactory<> 然后,工厂实现可以在其中包含Lazy<>

通常,我通过在IoC容器中为依赖项设置自定义对象作用域来解决单例要求(没有延迟加载)。 例如,StructureMap可以轻松地将某些依赖关系设置为Web应用程序中的singleton或per-request-scope。

我很少需要断言我在被测系统内的某些服务上做了一个懒惰的初始化。 我可能需要验证我是否只在某个范围内初始化了一次服务,但是仍然可以通过伪造工厂界面来轻松测试。

问题是你正在创建一个模拟懒人作为fakeProductServiceRepository,稍后将返回那个只需要一个模拟的实例。

你应该改变

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

var fakeProductServiceRepository = new Mock<IProductServiceRepository>();

暂无
暂无

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

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