NSubstitute模拟异步方法返回参数时遇System.InvalidCastException问题
问题:C#测试中抛出InvalidCastException异常
我有一段可正常编译的C#代码,但运行测试时抛出System.InvalidCastException异常,错误信息如下:
Unable to cast object of type 'DomainObject' to type 'System.Threading.Tasks.Task`1[DomainObject]'
测试及相关代码如下:
public class Test { [Fact] public async Task ThisShouldWork() { // arrange var dto = new Dto { FName = "John", LName = "Doe" }; var mock = Substitute.For<IRepository>(); mock .SaveAsync(Arg.Any<DomainObject>()) .Returns(args => args[0]); var service = new BusinessService(mock); // act var result = await service.DoSomethingAsync(dto); // assert await mock .Received(1) .SaveAsync(result); } } public class BusinessService { private readonly IRepository _repository; public BusinessService(IRepository repository) { _repository = repository; } public async Task<DomainObject> DoSomethingAsync(Dto dto) { var domain = new DomainObject { FirstName = dto.FName, LastName = dto.LName }; return await _repository.SaveAsync(domain); } } public class DomainObject { public string FirstName { get; set; } public string LastName { get; set; } } public interface IRepository { Task<DomainObject> SaveAsync(DomainObject domain); } public class Dto { public string FName { get; set; } public string LName { get; set; } }
问题原因及修复方案
原因
IRepository.SaveAsync方法的返回类型是Task<DomainObject>,但配置NSubstitute模拟对象时,直接返回了DomainObject实例,NSubstitute尝试将该实例强制转换为Task<DomainObject>类型,从而触发类型转换异常。
修复方式
有两种简洁的修复方法:
- 使用
Task.FromResult手动包装Task
将模拟配置代码修改为:
mock .SaveAsync(Arg.Any<DomainObject>()) .Returns(args => Task.FromResult((DomainObject)args[0]));
- 使用NSubstitute的
ReturnsAsync方法(推荐)ReturnsAsync是NSubstitute专门为异步方法提供的快捷方法,会自动将返回值包装为已完成的Task:
mock .SaveAsync(Arg.Any<DomainObject>()) .ReturnsAsync(args => (DomainObject)args[0]);
修改后,模拟对象会正确返回Task<DomainObject>类型的结果,测试即可正常运行。
内容的提问来源于stack exchange,提问作者baynezy
相关产品推荐
相关产品推荐

