使用 Moq 对 elasticsearch.net 的隐式运算符进行单元测试
Unit testing elasticsearch.net's implicit operator using Moq
我不确定 ElasticSearch's .net low level client 是否常用,因为 NEST 似乎是 "go to" 在 .net 中实现 Elasticsearch 客户端的方法。
无论如何,在许多其他方法中,主 IElasticLowLevelClient
接口公开了一个创建索引的方法:
ElasticsearchResponse<T> IndicesCreate<T>(
string index,
PostData<object> body,
Func<CreateIndexRequestParameters, CreateIndexRequestParameters> requestParameters = null
) where T : class;
如 in this doc 所述,您可以传递字符串而不是 PostData<object>
的实例,这要归功于 PostData
的隐式运算符之一:
public class PostData<T> : IPostData<T>, IPostData
{
// ... code omitted
public static implicit operator PostData<T>(string literalString);
}
我正在对 PersonIndexer
class 进行单元测试,其作用是使用正确的索引名称 "people" 和正确的 json 主体调用 IndicesCreate
-我不关心最后一个参数。为此,我正在使用 Moq 库模拟 IElasticLowLevelClient
。
下面是 PersonIndexer
如何调用低级客户端来创建索引:
var result = _elasticClient.IndicesCreate<object>("people", "{ properties: {etc}}", null);
下面是 PersonIndexerTest
class 验证的理想情况(json 解析简化):
_elasticClientMock.Verify(c => c.IndicesCreate<object>(
"people",
It.Is<string>(body =>
JObject.Parse(body).SelectToken("$.properties.name.type").ToString() == "string"
),
null
));
问题是 Moq 从未看到此调用,因为它期望 PostData<object>
匹配第二个参数类型,而不是字符串。
你会告诉我在我的子句中使用 "real" 基础类型:It.Is<PostData<object>>(...)
但这引发了另一个问题:PostData
class 没有公开暴露我用作隐式构造函数的字符串,因此它阻止我解析我的索引创建请求的主体。
有什么技巧可以用来验证传递的字符串吗?有什么方法可以设置模拟以重现隐式运算符的行为吗?我走错方向了吗...?
假设您没有测试 IElasticLowLevelClient
的实现,解决该问题的最简单方法是定义您自己的索引创建接口,该接口将接受索引名称和 string
正文。在您的代码中使用该接口并在测试中断言正确性。
或者,您可以使用一些反射来获取 It.Is<PostData<object>>(...)
语句中的私有 _literalString
字段。
更新:
在调查了 PostData
的实施之后,我认为我发现了更好的反思方式。您可以为方法调用设置回调,并将 PostData
写入流。然后你将能够将流转换为字符串并断言。
// Arrange
PostData<object> data = null;
mock
.Setup(m => m.IndicesCreate<object>(It.IsAny<string>(), It.IsAny<PostData<object>>(), null))
.Callback<string, PostData<object>>((s, d) => data = d);
// Act
// You test logic
// Assert
var stream = new MemoryStream();
data.Write(stream, fakeSettings);
var index = Enconding.UTF8.GetString(stream.ToArray());
index.Should().Be("{ properties: {etc}}");
我不确定 ElasticSearch's .net low level client 是否常用,因为 NEST 似乎是 "go to" 在 .net 中实现 Elasticsearch 客户端的方法。
无论如何,在许多其他方法中,主 IElasticLowLevelClient
接口公开了一个创建索引的方法:
ElasticsearchResponse<T> IndicesCreate<T>(
string index,
PostData<object> body,
Func<CreateIndexRequestParameters, CreateIndexRequestParameters> requestParameters = null
) where T : class;
如 in this doc 所述,您可以传递字符串而不是 PostData<object>
的实例,这要归功于 PostData
的隐式运算符之一:
public class PostData<T> : IPostData<T>, IPostData
{
// ... code omitted
public static implicit operator PostData<T>(string literalString);
}
我正在对 PersonIndexer
class 进行单元测试,其作用是使用正确的索引名称 "people" 和正确的 json 主体调用 IndicesCreate
-我不关心最后一个参数。为此,我正在使用 Moq 库模拟 IElasticLowLevelClient
。
下面是 PersonIndexer
如何调用低级客户端来创建索引:
var result = _elasticClient.IndicesCreate<object>("people", "{ properties: {etc}}", null);
下面是 PersonIndexerTest
class 验证的理想情况(json 解析简化):
_elasticClientMock.Verify(c => c.IndicesCreate<object>(
"people",
It.Is<string>(body =>
JObject.Parse(body).SelectToken("$.properties.name.type").ToString() == "string"
),
null
));
问题是 Moq 从未看到此调用,因为它期望 PostData<object>
匹配第二个参数类型,而不是字符串。
你会告诉我在我的子句中使用 "real" 基础类型:It.Is<PostData<object>>(...)
但这引发了另一个问题:PostData
class 没有公开暴露我用作隐式构造函数的字符串,因此它阻止我解析我的索引创建请求的主体。
有什么技巧可以用来验证传递的字符串吗?有什么方法可以设置模拟以重现隐式运算符的行为吗?我走错方向了吗...?
假设您没有测试 IElasticLowLevelClient
的实现,解决该问题的最简单方法是定义您自己的索引创建接口,该接口将接受索引名称和 string
正文。在您的代码中使用该接口并在测试中断言正确性。
或者,您可以使用一些反射来获取 It.Is<PostData<object>>(...)
语句中的私有 _literalString
字段。
更新:
在调查了 PostData
的实施之后,我认为我发现了更好的反思方式。您可以为方法调用设置回调,并将 PostData
写入流。然后你将能够将流转换为字符串并断言。
// Arrange
PostData<object> data = null;
mock
.Setup(m => m.IndicesCreate<object>(It.IsAny<string>(), It.IsAny<PostData<object>>(), null))
.Callback<string, PostData<object>>((s, d) => data = d);
// Act
// You test logic
// Assert
var stream = new MemoryStream();
data.Write(stream, fakeSettings);
var index = Enconding.UTF8.GetString(stream.ToArray());
index.Should().Be("{ properties: {etc}}");