测试传递给NEST的参数(elasticsearch)

时间:2019-02-14 14:15:49

标签: c# .net unit-testing elasticsearch nest

我正在使用NEST进行Elasticsearch查询。

    public IReadOnlyCollection<IHit<Recommendation>> GetRecommendations(
        RecommenderQueryFields shoulds,
        RecommenderQueryFields musts, 
        RecommenderQueryFields mustNots)
    {
        var boolQuery = new BoolQuery();
        boolQuery.Should = GetQueryContainers(shoulds);
        boolQuery.Must = GetQueryContainers(musts);
        boolQuery.MustNot = GetQueryContainers(mustNots);

        var response = _elasticClient.Search<Recommendation>(s => s
            .AllTypes().From(0).Size(10)
            .Query(outerQuery => boolQuery));

        return response.Hits;
    }

我要测试的GetQueryContainers方法中有逻辑。 有什么方法可以检查boolQuery对象中传递给ElasticClient的内容吗?

我已经使用NUnit和NSubstitute尝试了以下内容。

    [Test]
    public void Test1()
    {
        // Arrange
        var searchResponse = Substitute.For<ISearchResponse<Recommendation>>();
        searchResponse.Hits.Returns(new List<IHit<Recommendation>>());
        var elasticClient = Substitute.For<IElasticClient>();

        var sut = new Recommender(elasticClient);

        // Act
        sut.GetRecommendations(null, null, null);

        // Assert
        elasticClient
            .Received(1)
            .Search(Arg.Is<Func<SearchDescriptor<Recommendation>, ISearchRequest>>(x => true));
    }

Arg.Is<[...]>(x => true)中,我想用true常量代替对boolQuery的某些检查。但是我不知道是否可能或如何做到。还是有其他方法可以做到这一点?

1 个答案:

答案 0 :(得分:0)

TL; DR 使用派生的QueryVisitor。请参见下面的Edit2。

发现问题已经回答。它与NEST不相关,但与测试lambda表达式有关。 不可能:C# Moq Unit Testing with Lambda Expression or Func delegate

可以做的是测试将发送到elasticsearch的JSON请求,但是随后您需要实际的ElasticClientElasticSearch NEST 5.6.1 Query for unit test

可以做的是将逻辑放入自己的方法/类中。但是然后您只是为了测试而编写代码,我不喜欢这样做。喜欢:

public BoolQuery GetBoolQuery(RecommenderQueryFields shoulds, RecommenderQueryFields musts,
    RecommenderQueryFields mustNots)
{
    var boolQuery = new BoolQuery();
    boolQuery.Should = GetQueryContainers(shoulds);
    boolQuery.Must = GetQueryContainers(musts);
    boolQuery.MustNot = GetQueryContainers(mustNots);
    return boolQuery;
}

您要公开一个public方法,该方法不打算用于测试。 但是您可以像这样在boolQuery上声明:

[Test]
public void GetRecommendations_CallsElasticSearch()
{
    // Arrange
    var elasticClient = Substitute.For<IElasticClient>();
    var sut = new Recommender(elasticClient);

    // Act
    var boolQuery = sut.GetBoolQuery(new RecommenderQueryFields{BlackListedFor = new List<string>{"asdf"}}, null, null);

    // Assert
    Assert.AreEqual(1, boolQuery.Should.Count());
}

boolQuery.Should中的QueryContainer列表是不可测试的,因为它也是用lambda生成的。尽管总比没有好,但这仍然不是测试NEST的干净方法。

编辑

@Russ Cam在评论中提到了IQueryContainerQueryVisitor 我所拥有的:

[Test]
public void test()
{
    // Arrange
    var fieldValue = "asdf";
    var elasticClient = Substitute.For<IElasticClient>();
    var sut = new Recommender(elasticClient);

    // Act
    var boolQuery = sut.GetBoolQuery(new RecommenderQueryFields { BlackListedFor = new List<string> { fieldValue } }, null, null);

    // Assert
    IQueryContainer qc = boolQuery.Should.First(); // Cast to IQueryContainer
    Assert.AreEqual(fieldValue, qc.Match.Query); // Assert value

    // Get "field name"
    var queryVisitor = new QueryVisitor();
    var prettyVisitor = new DslPrettyPrintVisitor(new ConnectionSettings(new InMemoryConnection()));
    qc.Accept(queryVisitor);
    qc.Accept(prettyVisitor);
    Assert.AreEqual(0, queryVisitor.Depth);
    Assert.AreEqual(VisitorScope.Query, queryVisitor.Scope);
    Assert.AreEqual("query: match (field: blacklistedfor.keyword)\r\n", prettyVisitor.PrettyPrint);
}

可以通过IQueryContainer访问该字段的值。

我尝试了QueryVisitorDslPrettyPrintVisitor。第一个没有提供任何有用的信息。它的深度为0,是一个查询?我已经知道了对于第二个,我可以声明一些附加信息,例如字段名称(blacklistedfor)和后缀(关键字)。 断言字符串表示并不完美,但总比没有好。

Edit2

@Russ Cam给了我一个我非常满意的解决方案。它使用派生的QueryVisitor

public class MatchQueryVisitor : QueryVisitor
{
    public string Field { get; private set; }
    public string Value { get; private set; }

    public override void Visit(IMatchQuery query)
    {
        var inferrer = new Inferrer(new ConnectionSettings(new InMemoryConnection()));
        Field = inferrer.Field(query.Field);
        Value = query.Query;
    }
}

[Test]
public void test()
{
    // Arrange
    var fieldValue = "asdf";
    var elasticClient = Substitute.For<IElasticClient>();
    var sut = new Recommender(elasticClient);

    // Act
    var boolQuery = sut.GetBoolQuery(new RecommenderQueryFields { BlackListedFor = new List<string> { fieldValue } }, null,
        null);

    // Assert
    IQueryContainer qc = boolQuery.Should.First();
    var queryVisitor = new MatchQueryVisitor();
    qc.Accept(queryVisitor);
    Assert.AreEqual(fieldValue, queryVisitor.Value);
    Assert.AreEqual("blacklistedfor.keyword", queryVisitor.Field);
}

因此,在MatchQueryVisitor中,它得到FieldValue,然后在测试方法中对其进行断言。