使用 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}}");