模拟与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
始终为空。 我跟着这个博客文章,但仍然收到空引用异常。
如何在使用Moq的C#单元测试中模拟对象的惰性初始化
更新:这是一个屏幕,指示fakeProductPackageRepository
为空。
这是你的例子的重构版本:
[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应用程序中的单例或每个请求范围。
我很少需要断言我对被测系统内的某个服务进行了懒惰初始化。 我可能需要验证我是否仅在某个范围内初始化了一个服务,但仍然可以通过伪造工厂界面轻松进行测试。
问题在于你正在创建一个懒惰模拟为fakeProductServiceRepository,稍后将返回只需要Mock的那个实例。
你应该改变
var fakeProductServiceRepository = new Mock<Lazy<IProductServiceRepository>>();
至
var fakeProductServiceRepository = new Mock<IProductServiceRepository>();