在单元测试中模拟Async方法时获取NullReferenceExceptionXunit-Moq 存储库层服务层针对服务层的单元测试

问题描述

在测试一种使用异步存储库方法的服务方法时遇到问题。

存储库层

public interface IRepo
{
    Task<Model> GetByIdAsync(string Id);
    Task SaveAsync(Model model);
}

服务层

void Process(string Id)
{
    var model = _repo.GetByIdAsync(Id).Result;
        
    model.Field1 = "update";
        
    _repo.SaveAsync(model).Wait();
}

针对服务层的单元测试

[Fact]
SUTServiceTest()
{
    //Arrange
    Model model = new Model();
    var mockRepo = Mock.Get(new Repo());

    mockRepo.Setup(x => x.GetByIdAsync(It.IsNotNull<string>()))
        .Returns(() => Task.FromResult(model));

    mockRepo.Setup(x => x.SaveAsync(It.IsAny<Model>()))
        .Callback<Model>((obj) => model = obj);

    var _service = new SUTService(mockRepo);

    //Act
    _service.Process("1");

    //Assert
    Assert.Equal("update",model.Field1);
}

我在_repo.SaveAsync(model).await();遇到以下错误:

System.NullReferenceException-对象引用未设置为对象的实例

不确定我想念什么。

解决方法

此答案的目的是从聊天讨论中获取有价值的信息。

正如OP所说的,NullReferenceException已被抛出以下行:

_repo.SaveAsync(model).Wait();

_repo试图通过以下方式在单元测试中初始化:

var mockRepo = Mock.Get(new Repo());

那不是正确的方法。请检查Mock.Get中的documentation,以了解何时应使用它以及用于什么目的。

_repo应该用以下方式初始化:

var mockRepo = new Mock<IRepo>();

在更改模拟创建后,OP有以下​​观察结果:

但是,我放弃了用于回购构建的DI设置

简而言之:这就是重点。
每当您对组件进行单元测试时,您都将尝试模拟(简化和模仿)其所有依赖项。它需要依赖 stub spy 来验证在某些情况下已调用了其中之一,但是模拟对象的具体实现并不重要。 / p>

在这种特殊情况下,这意味着服务层不想依赖于具体的Repository实例。它只需要一个仓库,它可以捕获调用参数并可以验证其调用。


仅供参考:测试terminologies

  • 虚拟:返回伪造数据
  • 的简单代码
  • 伪造:可以使用快捷方式的有效的替代
  • 存根:具有预定义数据的自定义逻辑
  • 模拟:具有期望(交互式存根)的自定义逻辑
  • 填充:运行时自定义逻辑
  • 间谍拦截器以记录通话

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...