Moq ReturnsAsync returns Null when tuplelt;object,boolgt; instead of provided value(当元组lt;对象、bool代替提供的值时,Moq ReturnsAsync返回Null)
问题描述
这是我到目前为止的第一个问题,如果描述得不是很好,请原谅,但我会尽我最大的努力。我正在使用Moq在单元测试中模拟POST API调用的服务层,并使用_service.Create(...)返回一个元组值:
Task<(Model.Receipt Receipt, bool IsIdempotent)>
为此,我创建了一个元组结果并作为ReturnsAsync传递,如下所示:
var input = JsonConvert.DeserializeObject<Model.Receipt>(_jsonReceiptString);
var output = (Receipt: input, IsIdempotent: true);
_service.Setup(x => x.CreateAsync(input)).ReturnsAsync(output);
直到现在一切正常,但在运行时,在Post调用中调用服务后,返回值是<null,false>
!!这听起来像是返回一个缺省值,而不是预期的元组。因为我在这个模拟之后有日志记录数据,所以这会导致单元测试失败。
你知道我是不是遗漏了什么吗?
推荐答案
此设置
_service.Setup(x => x.CreateAsync(input)).ReturnsAsync(output);
..表示如果调用CreateAsync
且参数为input
,则返回output
。仅当参数实际上是与input
相同的对象、相同的类实例时,这才有效。
有时会起作用,但通常不起作用。在这种情况下,input
是从字符串反序列化的。如果您测试的代码还反序列化了一个字符串,那么您最终将得到Receipt
的两个实例。它们可能相同,但它们不是相同的实例,因此Setup
不会以您希望的方式工作。
您可能希望设置模拟,以便在调用CreateAsync
和input
具有某些属性值时,模拟返回output
。
我不知道Receipt
是什么样子。为了便于演示,我们假设它如下所示
internal class Receipt
{
public int Id { get; set; }
public string Name { get; set; }
}
如果传递给CreateAsync
的参数与input
具有相同的Id
和Name
,则您希望Setup
返回output
。在这种情况下,您可以执行以下操作:
_service.Setup(x =>
x.CreateAsync(
It.Is<Models.Receipt>(receipt =>
receipt.Id == input.Id
&& receipt.Name == input.Name)))
.ReturnsAsync(output);
这表示Setup
正在查找Receipt
参数,当它获得该参数时,它将执行此函数,如果该参数的Id
和Name
属性与input
的Id
和Name
:
true
receipt =>
receipt.Id == input.Id
&& receipt.Name == input.Name
如果您计划编写大量这样的测试,并且不想反复编写该函数,该怎么办?您也可以像这样创建IEqualityComparer
:
public class ReceiptEqualityComparer : IEqualityComparer<Receipt>
{
public bool Equals(Receipt x, Receipt y)
{
return x.Id == y.Id && x.Name == y.Name;
}
public int GetHashCode(Receipt obj)
{
return HashCode.Combine(obj.Id, obj.Name);
}
}
除非您在生产代码中需要这个类,否则我会在测试项目中定义这个类。此类包含比较Receipt
的两个实例并确定它们是否相等的逻辑。
现在您的Setup
可能如下所示:
_service.Setup(x =>
x.CreateAsync(
It.Is<Models.Receipt>(input, new ReceiptEqualityComparer())))
.ReturnsAsync(output);
现在Setup
将采用传递给CreateAsync
的参数,并使用ReceiptEqualityComparer
确定该参数是否等于input
。如果它们的Id
和Name
相同,则它们是相等的。
最后,如果
Receipt
实现IEquatable<Receipt>
,则问题中发布的原始代码将工作。这意味着该类有自己的内置逻辑,用于比较属性以查看两个实例是否相等。我使用ReSharper为我自动生成它。如下所示:
public class Receipt : IEquatable<Receipt>
{
public int Id { get; set; }
public string Name { get; set; }
public bool Equals(Receipt? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Id == other.Id && Name == other.Name;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Receipt) obj);
}
public override int GetHashCode()
{
return HashCode.Combine(Id, Name);
}
}
这可能是有道理的。但是,如果您的测试只需要这样做,那么我更喜欢前面两种方法中的任何一种,而不是修改生产类以使测试正常工作。
这篇关于当元组<;对象、bool&>代替提供的值时,Moq ReturnsAsync返回Null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:当元组<;对象、bool&>代替提供的值时,Moq ReturnsAsync返回Null
基础教程推荐
- rabbitmq 的 REST API 2022-01-01
- c# Math.Sqrt 实现 2022-01-01
- 如何激活MC67中的红灯 2022-01-01
- MS Visual Studio .NET 的替代品 2022-01-01
- SSE 浮点算术是否可重现? 2022-01-01
- 如何在 IDE 中获取 Xamarin Studio C# 输出? 2022-01-01
- 将 Office 安装到 Windows 容器 (servercore:ltsc2019) 失败,错误代码为 17002 2022-01-01
- 为什么Flurl.Http DownloadFileAsync/Http客户端GetAsync需要 2022-09-30
- 将 XML 转换为通用列表 2022-01-01
- 有没有办法忽略 2GB 文件上传的 maxRequestLength 限制? 2022-01-01