在ESIntegTestCase中,轻松进行Elasticsearch的集成测试

Elasticsearch为我们提供了一个名为ESIntegTestCase的类,用于方便地进行集成测试。每次执行测试时,该类会自动启动和关闭一个专用的Elasticsearch实例。由于无需安装Elasticsearch,只需向项目添加依赖即可使用,这使得编写对Elasticsearch进行实际访问的集成测试变得非常简单。

在Elasticsearch的1.x版本中,原先的类名为ElasticsearchIntegrationTest。在2.x版本中,这个名称发生了变化,并且似乎还增加了一些功能。

    参考: ElasticsearchIntegrationTestを使ってElasticsearchのJUnitテストを書く

这个班级非常方便!但是文档不够完善,而且它的表现也有一些小缺陷。我将简要总结我使用时遇到的问题作为重点。

    参考: 公式ドキュメント: Integration tests

引入

  <dependencies>
    ...
    <dependency>
      <groupId>org.elasticsearch</groupId>
      <artifactId>elasticsearch</artifactId>
      <version>2.3.4</version>
    </dependency>
    <dependency>
      <groupId>org.elasticsearch</groupId>
      <artifactId>elasticsearch</artifactId>
      <version>2.3.4</version>
      <type>test-jar</type>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.lucene</groupId>
      <artifactId>lucene-test-framework</artifactId>
      <version>5.5.0</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>1.3</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna</artifactId>
      <version>4.1.0</version>
      <scope>test</scope>
    </dependency>
    ...
  </dependencies>

除了org.elasticsearch:elasticsearch:jar之外,还需要将org.elasticsearch:elasticsearch:test-jar添加到依赖中。
此外,还需要单独添加以下依赖。

    • lucene-test-framework (公式ドキュメントに記載あり)

 

    • hamcrest-library

 

    jna

请查看Elasticsearch的父级pom.xml以确定每个库的版本。上述示例中的版本是Elasticsearch 2.3.4的版本。

使用方法

只需创建一个继承了ESIntegTestCase的测试类。

// ESIntegTestCaseクラスを継承したテストクラスを作成
public class SampleTest extends ESIntegTestCase {

  @Test
  public void test() throws Exception {

    // client()メソッドでクライアントを取得 -> インデックスを作成
    client().admin().indices().prepareCreate("pizza").get();

    for (String[] p : PIZZA_ITALIANA) {
      // 文章をインデックス
      client().prepareIndex("pizza", "recipe").setSource("name", p[0], "topping", p[1]).get();
    }

    // クライアントを使わず直接インデックスを操作するメソッドも用意されている
    flushAndRefresh("pizza");

    // インデックスされたレシピからキノコ(funghi)を含むピザを検索
    SearchResponse res = client().prepareSearch("pizza").setQuery(queryStringQuery("funghi")).get();

    // ヒット数を確認
    assertEquals(5, res.getHits().totalHits());
  }

  private static final String[][] PIZZA_ITALIANA = {
    {"Margherita", "pomodoro, fiordilatte, basilico, olio"},
    {"Marinara", "pomodoro, aglio, origano, olio"},
    {"Napoletana", "pomodoro, acciughe salate, origano, capperi, olio"},
    {"Capricciosa ", "pomodoro, mozzarella, funghi, carciofini, prosciutto cotto o crudo, olive nere"},
    {"Quattro formaggi", "pomodoro, mozzarella, altri formaggi a discrezione, basilico"},
    {"Funghi", "pomodoro, mozzarella, funghi champignon, prezzemolo, olio"},
    {"Prosciutto e funghi", "pomodoro, mozzarella, funghi champignon, prosciutto cotto o crudo"},
    {"Diavola", "pomodoro, mozzarella, salame piccante, peperoncino, olive nere"},
    {"Boscaiola", "mozzarella, funghi champignon, salsiccia"},
    {"Fiori di zucca", "mozzarella, fiori di zucca, acciughe salate, olio"},
    {"Frutti di mare", "frutti di mare, aglio, prezzemolo"},
    {"Ortolana", "pomodoro, mozzarella, peperoni, melanzane, zucchine, altre verdure grigliate a scelta"},
    {"Tirolese", "pomodoro, mozzarella, gorgonzola, speck"},
    {"Bismarck", "pomodoro, mozzarella, prosciutto cotto, uovo fritto"},
    {"Mare e monti", "pomodoro, mozzarella, funghi, gamberetti, cozze, calamari, aglio, prezzemolo"},
    {"Carrettiera", "mozzarella, friarielli, salsiccia, scaglie di grana"},
    {"Mimosa", "mozzarella, panna, prosciutto cotto, mais"},
    {"Primavera", "pomodori pachino, mozzarella, rucola, prosciutto crudo, scaglie di grana"},
    {"Caprese", "pomodori pachino, bufala, basilico, olio"}
  };
}

请注意:在某些环境中,如果发现测试类路径中存在”found jar hell”错误,测试可能会停止。在这种情况下,请首先查阅下一章节中的故障排除指南。

在测试用例中,可以使用继承的ESIntegTestCase类的client()方法获取Elasticsearch客户端。

另外,还提供了一些方便的方法来直接操作Elasticsearch,例如createIndex(…)和flushAndRefresh(…),而无需通过客户端。

    参考: Generic helper methods

如果需要编写测试,方便的方法是使用实用方法进行准备工作,例如创建索引,并将通过client()方法获取的客户端作为依赖项注入到用于验证客户端的组件中,然后执行测试。

默认情况下,在运行测试类之前,Elasticsearch集群会自动启动,并在运行完成后停止(使用@BeforeClass和@AfterClass)。
如果要在每个测试用例中启动和停止集群,可以在测试类上添加以下注解(当然,测试的执行时间会更长)。

@ESIntegTestCase.ClusterScope(scope= ESIntegTestCase.Scope.TEST)
public class SampleTest extends ESIntegTestCase {

故障排除

在使用ESIntegTestCase进行测试时,可能会出现无法访问本地文件或无法使用反射库(如Mockito和Jackson-databind)的情况。另外,测试的执行可能会因为出现”found jar hell in test classpath”错误而停止。

由于ESIntegTestCase最初是为了开发Elasticsearch本身和插件而设计的,所以在执行测试之前强制要求引入安全管理器并进行严格的依赖冲突检查。

    参考: BootstrapForTesting.java (Github)

这个预处理操作是在ESIntegTestCase类加载时执行的,因此无法通过测试类的代码来进行自定义。相反,您可以通过设置以下系统属性来禁用这些预处理操作。

tests.security.manager: セキュリティマネージャの有効化・無効化

tests.jarhell.check: 依存性衝突のチェックの有効化・無効化

请注意,在版本5.0的代码中,我们无法找到`tests.jarhell.check`,这是令人担忧的地方。

要設定這些屬性,可以通過java命令設置命令行選項。

java -Dtests.security.manager=false -Dtests.jarhell.check=false ...

需要在Maven等构建工具的配置中添加在执行测试时所需的系统属性。

  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.19.1</version>
        <configuration>
          <systemPropertyVariables>
             <!-- セキュリティマネージャと依存性衝突のチェックを無効化 -->
            <tests.security.manager>false</tests.security.manager>
            <tests.jarhell.check>false</tests.jarhell.check>
          </systemPropertyVariables>
        </configuration>
      </plugin>
      ...
    </plugins>
  </build>
bannerAds