使用ElasticsearchIntegrationTest编写Elasticsearch的JUnit测试
版本信息
- Elasticsearch 1.7.4
根据本文介绍,ElasticsearchIntegrationTest从2.x版本更名为ESIntegTestCase。由此可能带来了重大变化,请注意。
首先
简要说明
这篇文章是Elasticsearch Advent Calendar 2015第三天的条目。
其实,Elasticsearch提供了一个名为ElasticsearchIntegrationTest的类,用于在Java中编写测试。不过,关于这方面的日语资料非常少甚至没有,所以我整理了一些我在调研过程中找到的信息。
使用这个工具,可以在运行测试的机器上快速启动一个简易的Elasticsearch集群,并且可以在测试用例中向该集群插入文档或进行搜索操作。这样就不需要专门启动一个Elasticsearch服务器来进行测试了!
弹性搜索公式文档(Elasticsearch 1.7)
在Elasticsearch的官方文档中提及的是以下这些文章。
-
- Java Testing Framework
-
- Using the elasticsearch test classes
- integration tests
考试所需的依赖关系
本文将使用Maven。为了尝试使用elasticsearch 1.7.4的ElasticsearchIntegrationTest,以下是依赖项的描述。
<dependencies>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-test-framework</artifactId>
<version>4.10.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.carrotsearch.randomizedtesting</groupId>
<artifactId>randomizedtesting-runner</artifactId>
<version>2.1.17</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>1.7.3</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>1.7.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
请注意,如果描述顺序改变,可能会导致无法正常运作。
作为版本选择的方法,首先要指定自己使用的elasticsearch版本。包括test-jar的地方也有2处。本次选择了1.7.3版本。
接下来,我们需要查找当前使用的Elasticsearch所依赖的Lucene版本,并确定lucene-test-framework的版本。我认为在Eclipse的Maven POM编辑器中检查依赖关系,或者查看Maven Repository中该版本的页面可能是一个不错的方法。这次我们可以使用4.10.4版本。
最後,我們需要確定 randomizedtesting-runner 的版本,但我不知道如何查詢。我曾考慮使用 lucene-test-framework 4.10.4 所依賴的 randomizedtesting-runner 2.1.6,但似乎沒有作用。試著指定 2.1 系列的最新版本 2.1.17,結果可以運行。也許最好是在 lucene-test-framework 中排除 randomizedtesting-runner。
测试代码
我们将测试一个类,该类在构造函数中接收一个Elasticsearch客户端,并在search方法中执行一个非常简单的搜索操作。
package jp.akimateras.elasticsearch.esintegtest.service;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.query.MatchQueryBuilder.Operator;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
public class SimpleSearch {
// インデックス・タイプ名定義.
public static final String INDEX_NAME = "simple-search";
public static final String TYPE_NAME = "documents";
// フィールド名定義.
public static final String TITLE_FIELD_NAME = "title";
public static final String TEXT_FIELD_NAME = "text";
// 検索結果の返却型.
public static class Document {
public final String title;
public final String text;
Document(String title, String text) {
this.title = title;
this.text = text;
}
}
// Elasticsearchクライアント.
private final Client client;
/**
* コンストラクタ.
*
* @param client 検索に使用するElasticsearchクライアント
*/
public SimpleSearch(Client client) {
this.client = client;
}
/**
* 検索メソッド
*
* @param keywords 検索キーワード. スペース区切りで複数のキーワードを指定した場合はAND検索とする.
* @return 検索結果.
*/
public Collection<Document> search(String keywords) throws IOException {
try {
// 検索クエリの組み立て.
MultiMatchQueryBuilder query = QueryBuilders
.multiMatchQuery(keywords, TITLE_FIELD_NAME, TEXT_FIELD_NAME)
.operator(Operator.AND);
// 検索の実行.
SearchResponse response = client.prepareSearch(INDEX_NAME)
.setTypes(TYPE_NAME)
.setQuery(query)
.execute()
.get();
// 検索結果を Collection<Document> に詰め替えてreturnする.
return Arrays.stream(response.getHits().hits())
.map(SearchHit::sourceAsMap)
.map(source -> new Document(
(String) source.get(TITLE_FIELD_NAME),
(String) source.get(TEXT_FIELD_NAME)
))
.collect(Collectors.toList());
} catch(Exception e) {
throw new IOException(e);
}
}
}
简单的测试
让我们粗略测试一下之前创建的SimpleSearch类。
要写ElasticsearchIntegrationTest测试的话,
-
- 让ElasticsearchIntegrationTest在测试类中继承。
- 在测试方法中调用client()会获取Elasticsearch客户端。
只要记住这两点就可以了。
package jp.akimateras.elasticsearch.esintegtest.service;
import static org.hamcrest.Matchers.is;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Before;
import org.junit.Test;
@ElasticsearchIntegrationTest.ClusterScope(numDataNodes = 1 /* テスト時に立ち上げるノード数 */)
public class SimpleSearchTest extends ElasticsearchIntegrationTest {
// テスト用ノードに設定したい内容はこのメソッドの返却値として表現する.
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.build();
}
// ここでインデックスを作成する.
@Before
public void initializeIndex() throws Exception {
// マッピング定義.
// 外部JSONファイルとかでマッピング定義をしているなら, そっちから読んだ方が良いと思う.
XContentBuilder mapping = XContentFactory.jsonBuilder()
.startObject()
.startObject(SimpleSearch.TYPE_NAME)
.startObject("properties")
.startObject(SimpleSearch.TITLE_FIELD_NAME)
.field("type", "string")
.field("source", true)
.endObject()
.startObject(SimpleSearch.TEXT_FIELD_NAME)
.field("type", "string")
.field("source", true)
.endObject()
.endObject()
.endObject()
.endObject();
// インデックス作成.
admin().indices() // admin()でアドミンクライアントが取れる!
.prepareCreate(SimpleSearch.INDEX_NAME)
.addMapping(SimpleSearch.TYPE_NAME, mapping)
.execute()
.actionGet();
// インデックス作成完了の待機.
ensureGreen();
}
// テスト本体
@Test
public void testSearch() throws Exception {
// ドキュメント投入.
// これも外部JSONファイルなどを用意して, Bulk APIで投入した方が良いと思う.
index(SimpleSearch.INDEX_NAME, SimpleSearch.TYPE_NAME, XContentFactory.jsonBuilder()
.startObject()
.field(SimpleSearch.TITLE_FIELD_NAME, "Alpaca")
.field(SimpleSearch.TEXT_FIELD_NAME, "An alpaca is a domesticated species of South American camelid.")
.endObject());
index(SimpleSearch.INDEX_NAME, SimpleSearch.TYPE_NAME, XContentFactory.jsonBuilder()
.startObject()
.field(SimpleSearch.TITLE_FIELD_NAME, "Llama")
.field(SimpleSearch.TEXT_FIELD_NAME, "A llama is a domesticated species of South American camelid.")
.endObject());
index(SimpleSearch.INDEX_NAME, SimpleSearch.TYPE_NAME, XContentFactory.jsonBuilder()
.startObject()
.field(SimpleSearch.TITLE_FIELD_NAME, "Vicugna")
.field(SimpleSearch.TEXT_FIELD_NAME, "A vicugna is a kind of herbivorous mammals that inhabit the Andes of South America.")
.endObject());
// ドキュメント投入完了の待機.
flushAndRefresh();
ensureGreen();
// テスト用クライアントに対してSimpleSearchインスタンスを生成.
SimpleSearch service = new SimpleSearch(client()); // client()でクライアントが取れる!
// テスト実施.
// せっかくなので, 依存のおまけで付いてきたhamcrestを使おう.
assertThat(service.search("alpaca").size(), is(1));
assertThat(service.search("llama").size(), is(1));
assertThat(service.search("vicugna").size(), is(1));
assertThat(service.search("lama").size(), is(0));
assertThat(service.search("domesticated").size(), is(2));
assertThat(service.search("alpaca domesticated").size(), is(1));
assertThat(service.search("llama domesticated").size(), is(1));
assertThat(service.search("vicugna domesticated").size(), is(0));
}
}
由于可以使用client()获取Elasticsearch客户端,因此基本上可以做任何事情。如果需要进行索引创建等管理员操作,请使用client().admin()或admin()进行获取。
在@Before标记下,我们创建了索引,但在@After标记下,我们不需要手动删除索引,它似乎会自动将每个测试方法重置为空状态。非常方便。
在平常看不到的方法ensureGreen()和flushAndRefresh()等出现了,这些是由ElasticsearchIntegrationTest提供的方便方法。例如,如果要自己编写ensureGreen()方法,
client().admin().cluster().prepareHealth()
.setWaitForGreenStatus()
.execute()
.actionGet();
听起来可能会有点长。当然,你也可以自己编写这种操作来进行测试。 有关便利方法的列表,请参考官方文档:integration tests (1.7)。
在这个阶段卡住不动的时候
我会写下我犯过的错误的日志以及相应的处理方法。
型 <类名> 的层次结构是不一致的
型 <クラス名> の階層は不整合です
The hierarchy of the type '<Class Name>' is inconsistent
当在Eclipse中的测试类中扩展`ElasticsearchIntegrationTest`时,如果看到了Elasticsearch测试框架的JAR包,而看不到`randomizedtesting-runner`的JAR包时,会出现上述错误。请确认依赖是否正确编写,并尝试刷新/清理项目等方法。
测试类需要启用断言。
java.lang.Exception: Test class requires enabled assertions, enable globally (-ea) or for Solr/Lucene subpackages only: jp.akimateras.elasticsearch.esintegtest.test.SampleTest
__randomizedtesting.SeedInfo.seed([E05E1B5BAB0E8E6]:0)
org.apache.lucene.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:38)
org.apache.lucene.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:48)
org.apache.lucene.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:65)
org.apache.lucene.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:55)
[...com.carrotsearch.randomizedtesting.*]
java.lang.Thread.run(Unknown Source)
如果使用ElasticsearchIntegrationTest,则测试执行时必须启用断言。请在JVM启动选项中添加-ea。
如果要从Eclipse中运行测试,只需打开[运行配置],然后打开特定配置选项的[参数]选项卡,在[VM参数]文本区域中写入-ea即可。
tests-framework.jar和lucene-core.jar发生了什么。
java.lang.AssertionError: fix your classpath to have tests-framework.jar before lucene-core.jar
__randomizedtesting.SeedInfo.seed([C7B320FCEA3DEB94]:0)
org.apache.lucene.util.TestRuleSetupAndRestoreClassEnv.before(TestRuleSetupAndRestoreClassEnv.java:211)
org.apache.lucene.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:45)
org.apache.lucene.util.TestRuleStoreClassName$1.evaluate(TestRuleStoreClassName.java:42)
[...com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:43)
org.apache.lucene.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:48)
org.apache.lucene.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:65)
org.apache.lucene.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:55)
[...com.carrotsearch.randomizedtesting.*]
java.lang.Thread.run(Unknown Source)
pom.xml中的描述顺序是错误的。是Java类路径的顺序出了问题吗…?
在依赖于randomizedtesting-runner之前写入对Elasticsearch的依赖会导致出现问题。我不太了解Java,所以不太清楚原因,但是以描述顺序无法正常运行有点让人不悦。
在使用randomizedtesting时出现了NoSuchMethodError错误。
java.lang.NoSuchMethodError: com.carrotsearch.randomizedtesting.RandomizedContext.runWithPrivateRandomness(Lcom/carrotsearch/randomizedtesting/Randomness;Ljava/util/concurrent/Callable;)Ljava/lang/Object;
__randomizedtesting.SeedInfo.seed([173369D720B9A36A:9F67560D8E45CE92]:0)
org.elasticsearch.test.ElasticsearchIntegrationTest.buildWithPrivateContext(ElasticsearchIntegrationTest.java:583)
org.elasticsearch.test.ElasticsearchIntegrationTest.buildAndPutCluster(ElasticsearchIntegrationTest.java:598)
org.elasticsearch.test.ElasticsearchIntegrationTest.beforeInternal(ElasticsearchIntegrationTest.java:283)
org.elasticsearch.test.ElasticsearchIntegrationTest.before(ElasticsearchIntegrationTest.java:1946)
[...sun.*, org.junit.*, java.lang.reflect.*, com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.TestRuleSetupTeardownChained$1.evaluate(TestRuleSetupTeardownChained.java:50)
org.apache.lucene.util.TestRuleFieldCacheSanity$1.evaluate(TestRuleFieldCacheSanity.java:51)
org.apache.lucene.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:46)
[...com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.TestRuleThreadAndTestName$1.evaluate(TestRuleThreadAndTestName.java:49)
org.apache.lucene.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:65)
org.apache.lucene.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:48)
[...com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:46)
org.apache.lucene.util.TestRuleStoreClassName$1.evaluate(TestRuleStoreClassName.java:42)
[...com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:43)
org.apache.lucene.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:48)
org.apache.lucene.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:65)
org.apache.lucene.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:55)
[...com.carrotsearch.randomizedtesting.*]
java.lang.Thread.run(Unknown Source)
如果在代码中忽略了对randomizedtesting-runner的依赖,或者使用了旧版本的randomizedtesting-runner,也可能会发生这个问题。当使用elasticsearch 1.7.3时,使用randomizedtesting 2.1.6会引发此错误,而使用randomizedtesting 2.1.17则可以正常运行。
使用Hamcrest时出现NoClassDefFoundError错误。
java.lang.NoClassDefFoundError: org/hamcrest/Matchers
__randomizedtesting.SeedInfo.seed([3F50D3975E9C4C58:B704EC4DF06021A0]:0)
org.elasticsearch.test.ElasticsearchIntegrationTest.afterInternal(ElasticsearchIntegrationTest.java:628)
org.elasticsearch.test.ElasticsearchIntegrationTest.after(ElasticsearchIntegrationTest.java:1954)
[...sun.*, org.junit.*, java.lang.reflect.*, com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.TestRuleSetupTeardownChained$1.evaluate(TestRuleSetupTeardownChained.java:50)
org.apache.lucene.util.TestRuleFieldCacheSanity$1.evaluate(TestRuleFieldCacheSanity.java:51)
org.apache.lucene.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:46)
[...com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.TestRuleThreadAndTestName$1.evaluate(TestRuleThreadAndTestName.java:49)
org.apache.lucene.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:65)
org.apache.lucene.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:48)
[...com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:46)
org.apache.lucene.util.TestRuleStoreClassName$1.evaluate(TestRuleStoreClassName.java:42)
[...com.carrotsearch.randomizedtesting.*]
org.apache.lucene.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:43)
org.apache.lucene.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:48)
org.apache.lucene.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:65)
org.apache.lucene.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:55)
[...com.carrotsearch.randomizedtesting.*]
java.lang.Thread.run(Unknown Source)
我认为文件中没有写,但似乎需要使用hamcrest。当使用elasticsearch 1.7.3时,hamcrest 1.3可以运行。
需要测试插件所需的功能。
简单的测试是否正常工作?实际上,从这里开始才是真正的事情。
如果想要处理日本语言的Elasticsearch,则需要安装诸如日本语分析器等插件。如果自己启动Elasticsearch服务器进行测试,则插件管理可能会变得麻烦,因此在这个阶段将感受到ElasticsearchIntegrationTest的优势。
在正在运行的Elasticsearch服务器中,如果要使用插件,则可能会执行类似于 $ bin/plugin –install <所需插件> 的命令。另一方面,在ElasticsearchIntegrationTest中使用插件时,
- 重写继承自ElasticsearchIntegrationTest类的nodeSettings方法,并在其中添加使用所需插件的依存关系描述。
只需要完成两个就可以了。
在依赖关系中添加所需使用的插件。
让我们这次尝试使用作为日本语分析器插件的elasticsearch-analysis-kuromoji,将以下依赖项添加到pom.xml中。
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch-analysis-kuromoji</artifactId>
<version>2.7.0</version>
</dependency>
与Elasticsearch和kuromoji插件的版本兼容性相当于在安装服务器时一样,可以通过elasticsearch-analysis-kuromoji的GitHub来确认。根据我们的了解,你可以使用对应于es-1.7版本的elasticsearch-analysis-kuromoji 2.7.0。
在使用插件时,记录覆盖nodeSettings的要点。
这件事有两种方式可以做。
将所有存在于类路径中的插件加载
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("plugins." + PluginsService.LOAD_PLUGIN_FROM_CLASSPATH, true)
.build();
}
如果给定一个由”plugins.”紧接着由org.elasticsearch.plugins.PluginsService.LOAD_PLUGIN_FROM_CLASSPATH指定的字符串常量连接而成的配置名,并设置为true,那么将会去读取存在于类路径上的所有插件。值得一提的是,在elasticsearch 1.7.3中,该配置名被展开为”plugins.load_classpath_plugins”。
指定要加载的插件
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("plugin.types", AnalysisKuromojiPlugin.class.getName())
.build();
}
如果将”plugin.types”内的插件类名提供给插件,将加载对应提供的插件名称。由于在测试时明确指定了需要使用的插件,个人更喜欢这种方式。
如果要加载多个插件,可以使用putArray方法来替代put方法,并按照以下方式编写。
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.putArray("plugin.types",
AnalysisKuromojiPlugin.class.getName(),
AnalysisICUPlugin.class.getName()) // elasticsearch-analysis-icu プラグイン
.build();
}
根据官方文件(integration tests (2.1)),从 Elasticsearch 2.1 开始,似乎已对指定 nodePlugins 插件进行了修改,使其需要覆盖专用方法。
使用日语的测试用例
让我们验证一下Kuromoji插件是否已经成功加载。
package jp.akimateras.elasticsearch.esintegtest.service;
import static org.hamcrest.Matchers.is;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.plugin.analysis.kuromoji.AnalysisKuromojiPlugin;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.junit.Before;
import org.junit.Test;
@ElasticsearchIntegrationTest.ClusterScope(numDataNodes = 1)
public class SimpleSearchTest extends ElasticsearchIntegrationTest {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("plugin.types", AnalysisKuromojiPlugin.class.getName())
.build();
}
@Before
public void initializeIndex() throws Exception {
// インデックス作成.
XContentBuilder mapping = XContentFactory.jsonBuilder()
.startObject()
.startObject(SimpleSearch.TYPE_NAME)
.startObject("properties")
.startObject(SimpleSearch.TITLE_FIELD_NAME)
.field("type", "string")
.field("source", true)
.field("analyzer", "kuromoji") // kuromojiがないとこれがエラーになる.
.endObject()
.startObject(SimpleSearch.TEXT_FIELD_NAME)
.field("type", "string")
.field("source", true)
.field("analyzer", "kuromoji")
.endObject()
.endObject()
.endObject()
.endObject();
admin().indices().prepareCreate(SimpleSearch.INDEX_NAME)
.addMapping(SimpleSearch.TYPE_NAME, mapping)
.execute()
.actionGet();
// インデックス作成完了の待機.
ensureGreen();
}
@Test
public void testSearch() throws Exception {
// ドキュメント投入. 日本語を使います.
index(SimpleSearch.INDEX_NAME, SimpleSearch.TYPE_NAME, XContentFactory.jsonBuilder()
.startObject()
.field(SimpleSearch.TITLE_FIELD_NAME, "アルパカ")
.field(SimpleSearch.TEXT_FIELD_NAME, "アルパカは, 南アメリカのラクダ科動物の家畜です.")
.endObject());
index(SimpleSearch.INDEX_NAME, SimpleSearch.TYPE_NAME, XContentFactory.jsonBuilder()
.startObject()
.field(SimpleSearch.TITLE_FIELD_NAME, "リャマ")
.field(SimpleSearch.TEXT_FIELD_NAME, "リャマは, 南アメリカのラクダ科動物の家畜です.")
.endObject());
index(SimpleSearch.INDEX_NAME, SimpleSearch.TYPE_NAME, XContentFactory.jsonBuilder()
.startObject()
.field(SimpleSearch.TITLE_FIELD_NAME, "ビクーニャ")
.field(SimpleSearch.TEXT_FIELD_NAME, "ビクーニャは, 南アメリカのアンデス山脈に生息する草食哺乳類です.")
.endObject());
// ドキュメント投入完了の待機.
flushAndRefresh();
ensureGreen();
// テスト用 client を使ってSimpleSearchインスタンスを生成.
SimpleSearch service = new SimpleSearch(client());
// テスト実施
assertThat(service.search("アルパカ").size(), is(1));
assertThat(service.search("リャマ").size(), is(1));
assertThat(service.search("ビクーニャ").size(), is(1));
assertThat(service.search("ラマ").size(), is(0));
assertThat(service.search("家畜").size(), is(2));
assertThat(service.search("アルパカ 家畜").size(), is(1));
assertThat(service.search("リャマ 家畜").size(), is(1));
assertThat(service.search("ビクーニャ 家畜").size(), is(0));
}
}
无论怎么样,这个测试不使用Kuromoji也可以通过默认的Standard分析器…非常出色啊。如果把输入文档的标题和文本中的”阿尔帕卡”都改成”阿尔帕卡阿尔帕卡”,Standard分析器会将其作为一个词来识别,所以搜索”阿尔帕卡”也不会有结果了。而如果使用Kuromoji,它会将其识别为两个”阿尔帕卡”,所以可以通过”阿尔帕卡”进行搜索。
需要对脚本所需功能进行测试。
我們將介紹如何測試需要使用Groovy等腳本的功能。
如果作为服务器启动,则Elasticsearch的脚本可以自动读取放置在例如/etc/elasticsearch/scripts这样的位置。为了使脚本在ElasticsearchIntegrationTest中可见,只需将脚本放置在特定目录中,并在nodeSettings方法中指定放置位置即可。
然而,在elasticsearch 1.7.3版本中,无法直接指定脚本目录。它会在由”path.conf”指定的设置目录下搜索名为”scripts”的固定目录。
因此,如果要让它读取名为”src/test/resources/scripts”的目录,可以将”path.conf”设置为”src/test/resources”。
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("plugin.types", AnalysisKuromojiPlugin.class.getName())
.put("path.conf", "src/test/resources")
.build();
}
据官方文档《目录布局 (2.1)》,从Elasticsearch 2.1版本开始,可以通过”path.script”设置项直接指定脚本目录进行改进。
最后
非常感谢发表这篇关于经历了诸多磨难,但最终测试框架终于成功运行的文章。如果有任何错误,请指正。
我尽管经历了很多困难,但因为这个,我非常高兴不再需要管理测试用的Elasticsearch服务器,而且我觉得引入它是值得的。
明天,是takakiku的MySQL慢查询日志可视化文章!