【问题标题】:Test for parameter passed to NEST (elasticsearch)测试传递给 NEST (elasticsearch) 的参数
【发布时间】:2019-02-19 00:22:54
【问题描述】:

我正在使用 NEST 进行弹性搜索查询。

    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&lt;[...]&gt;(x =&gt; true) 中,我想替换true 常量以对boolQuery 进行一些检查。但我不知道这是否可能或如何完成。还是有其他方法可以做到这一点?

【问题讨论】:

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


    【解决方案1】:

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

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

    可以做的是测试将发送到elasticsearch的JSON请求,但是你需要实际的ElasticClient:ElasticSearch 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 的列表,它们是不可测试的,因为它也是用 lambdas 生成的。虽然总比没有好,但它仍然不是测试 NEST 的干净方法。

    编辑

    @Russ Cam 在评论中提到了IQueryContainer 和QueryVisitor 我有什么:

    [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访问该字段的值。

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

    编辑2

    @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中,它得到Field和Value,然后在测试方法中断言。

    【讨论】:

    • 有一个QueryVisitor 可以访问一个QueryContainer 并检查它是否包含指定类型的查询。另一种方法是,如果您知道会出现哪些查询,则将其转换为 IQueryContainer 并读取相应的查询属性。
    • 非常好。感谢您将我指向QueryVisitor 和IQuerycontainer。更新了我的回复。
    • 您可以从 QueryVisitor 派生访问者并覆盖您的用例所需的任何方法
    • 你是我的英雄。我更新了我的回复。我现在对代码很满意。
    • @SimonLang 这很棒。谢谢你的详细回答。很有帮助!!!
    猜你喜欢
    • 2021-05-25
    • 2015-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    相关资源
    最近更新 更多