在单元测试(Xunit - Moq)中模拟 Async 方法时获取 NullReferenceException
Getting NullReferenceException while mocking Async method in Unit Test (Xunit - 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 - Object reference not set to an instance of an object
不确定我错过了什么。
此答案的目的是从聊天讨论中获取有价值的信息。
正如 OP 所说,NullReferenceException
已在以下行抛出:
_repo.SaveAsync(model).Wait();
_repo
试图在单元测试中以下列方式初始化:
var mockRepo = Mock.Get(new Repo());
这不是正确的方法。请检查 Mock.Get
的 documentation 以了解何时应该使用它以及用于什么目的。
_repo
应按以下方式初始化:
var mockRepo = new Mock<IRepo>();
OP 在更改模拟创建后有以下观察结果:
However, I loose the DI setup that I have for Repo construction
简而言之:这就是重点。
每当您对组件进行单元测试时,您都会尝试模拟(简化和模仿)它的所有依赖项。它需要依赖 stubs 和 spies 才能验证在某些情况下调用了其中哪一个,但是 mocked 的具体实现对象无关紧要。
在这种特殊情况下,这意味着服务层不想依赖具体的 Repository 实例。它只需要一个repo,它可以捕获调用参数并验证它的调用。
仅供参考:测试 terminologies
- Dummy: returns bogus data
的简单代码
- Fake:一个可行的替代方案,可以走捷径
- Stub:自定义逻辑,带有预定义数据
- Mock:具有期望的自定义逻辑(交互式存根)
- Shim:自定义逻辑 at run-time
- 间谍:拦截器 记录通话
我在测试使用异步存储库方法的服务方法之一时遇到问题。
存储库层
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 - Object reference not set to an instance of an object
不确定我错过了什么。
此答案的目的是从聊天讨论中获取有价值的信息。
正如 OP 所说,NullReferenceException
已在以下行抛出:
_repo.SaveAsync(model).Wait();
_repo
试图在单元测试中以下列方式初始化:
var mockRepo = Mock.Get(new Repo());
这不是正确的方法。请检查 Mock.Get
的 documentation 以了解何时应该使用它以及用于什么目的。
_repo
应按以下方式初始化:
var mockRepo = new Mock<IRepo>();
OP 在更改模拟创建后有以下观察结果:
However, I loose the DI setup that I have for Repo construction
简而言之:这就是重点。
每当您对组件进行单元测试时,您都会尝试模拟(简化和模仿)它的所有依赖项。它需要依赖 stubs 和 spies 才能验证在某些情况下调用了其中哪一个,但是 mocked 的具体实现对象无关紧要。
在这种特殊情况下,这意味着服务层不想依赖具体的 Repository 实例。它只需要一个repo,它可以捕获调用参数并验证它的调用。
仅供参考:测试 terminologies
- Dummy: returns bogus data 的简单代码
- Fake:一个可行的替代方案,可以走捷径
- Stub:自定义逻辑,带有预定义数据
- Mock:具有期望的自定义逻辑(交互式存根)
- Shim:自定义逻辑 at run-time
- 间谍:拦截器 记录通话