在单元测试(Xuit-Moq)中模拟异步方法时获取NullReferenceException



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

存储库层

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的文档,了解它应该在何时使用以及用于什么目的。

_repo应按以下方式初始化:

var mockRepo = new Mock<IRepo>();

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

然而,我放松了我为Repo构建所做的DI设置

简而言之:这就是重点
无论何时对组件进行单元测试,都要尝试模拟(简化和模拟(其所有依赖项。它需要依赖存根间谍来验证在某些情况下调用了其中的哪一个,但模拟对象的具体实现无关紧要。

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


FYI:测试术语

  • :返回伪造数据的简单代码
  • 伪造:一个可以走捷径的工作替代方案
  • Stub:带有预定义数据的自定义逻辑
  • 模拟:具有期望值的自定义逻辑(交互式存根(
  • 垫片:运行时的自定义逻辑
  • 间谍拦截器记录呼叫

最新更新