具有类型变量的模拟通用方法 - NSubstitute
我有一个如下所示的客户端界面:
public interface IDiscosClient
{
public Task<DiscosResponse<T>?> Get<T>(string queryUrl) where T : DiscosModelBase;
// The rest
}
DiscosResponse
如下所示:
public record DiscosResponse<T> where T: DiscosModelBase
{
public DiscosResponse()
{
}
internal DiscosResponse(T attributes)
{
Attributes = attributes;
}
[JsonPropertyName("type")]
public ResponseType Type { get; init; }
// TODO - This is wrong an probably needs renaming to something like Object
[JsonPropertyName("attributes")]
public T Attributes { get; init; }
[JsonPropertyName("id")]
[JsonConverter(typeof(JsonStringIntConverter))]
public int Id { get; init; }
}
我希望能够动态创建 Substitute.For
T
的实例。
所以我知道如何构造和调用泛型方法。我还设置了 AutoFixture
,以便我可以根据需要创建 T
的新实例。
然而,我不知道的是如何告诉 NSubstitute
在调用这个构造方法时返回这个新实例。
作为参考,在没有反射的情况下执行此操作的常用语法是:
MyType myMock = Substitute.For<MyType>();
myMock.MyMethod().Returns(myInstance);
编辑:
我必须在其中的自动修复部分中放置一个引脚,因为它会导致递归问题。但是,我现在想出了这个,它似乎一直有效,直到我尝试设置调用的返回值:
private IDiscosClient CreateSubstituteClient(Type type)
{
IDiscosClient client = Substitute.For<IDiscosClient>();
MethodInfo getMethod = typeof(IDiscosClient).GetMethod(nameof(client.Get), new [] {typeof(string)}) ?? throw new MethodAccessException();
MethodInfo constructedGetMethod = getMethod.MakeGenericMethod(type);
Type constructedReturnType = typeof(DiscosResponse<>).MakeGenericType(type);
const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
CultureInfo culture = CultureInfo.InvariantCulture;
object returnValue = Activator.CreateInstance(constructedReturnType, flags, null, new [] {Activator.CreateInstance(type)}, culture); // Not using AutoFix as it will cause recursion issues
constructedGetMethod.Invoke(client, new object?[] {Arg.Any<string>()}).Returns(Task.FromResult(returnValue));
return client;
}
此时它会抛出此错误:
NSubstitute.Exceptions.CouldNotSetReturnDueToTypeMismatchException: 无法为 IDiscosClient.Get 返回类型为 Task
1 的值(预期类型为 Task
1)。
这很令人困惑,因为 returnValue 的类型是:
DISCOSweb_Sdk.Models.DiscosResponse`1[[DISCOSweb_Sdk.Models.ResponseModels.Reentries.Reentry, DISCOSweb-Sdk,版本=1.0.0.0,文化=中性, PublicKeyToken=null]],DISCOSweb-Sdk,版本=1.0.0.0, 文化=中立,PublicKeyToken=null
并且 constructedGetMethod.ReturnParameter
是:
System.Threading.Tasks.Task
1[DISCOSweb_Sdk.Models.DiscosResponse
1[DISCOSweb_Sdk.Models.ResponseModels.Reentries.Reentry]]
其中,一旦我将前者包装在 Task 中,AFIACT 就会匹配。从结果
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Task.FromResult(returnValue)
结果是Task
才能使运行时类型相同。
Task.FromResult(returnValue)
results in runtime type ofTask<object>
while your method expectsTask<DiscosResponse<T>?>
.NSubstitute
checks compatibility of returned type with(among others)IsAssignableFrom
so it throws exception. In this particular case you need to do sth like thisin order for runtime types to be the same.