从elasticsearch1.6.2版本开始,项目组就开始进行基于elasticsearch的应用开发,然而对于这些应用的单元测试则相对落后——在搭建的es测试集群上跑测试用例。其实,es本身提供了易于使用地基础的集成测试能力。在开始es功能的集成测试前,建议阅读es测试官方文档
项目使用的版本是2.1.1,下面遇到的问题都是针对此版本的。
1.elasticsearch中的jarHell问题
解决方案是在测试源码目录src/test/java中添加org.elasticsearch.bootstrap.JarHell
类,源码如下:
package org.elasticsearch.bootstrap;
import java.net.URL;
public class JarHell {
private JarHell() {}
public static void checkJarHell() throws Exception {}
public static void checkJarHell(URL urls[]) throws Exception {}
public static void checkVersionFormat(String targetVersion) {}
public static void checkJavaVersion(String resource, String targetVersion) {}
public static URL[] parseClassPath() {return new URL[]{};}
}
参考目录:解决es2.1.1中jarHell问题
2.和EasyMock集成问题
进行单元测试时,经常使用mock对象,项目中主要使用的是EasyMock和PowerMock。
如果测试类是继承自ESIntegTestCase进行继承测试时,要注意,如果使用@Before或者@After标识,则参考LuceneTestCase类中关于此的描述
* For instance-level setup, use {@link Before} and {@link After} annotated
* methods. If you override either {@link #setUp()} or {@link #tearDown()} in
* your subclass, make sure you call super.setUp()
and
* super.tearDown()
. This is detected and enforced.
3.设置elasticsearch集群实例的shard和replica数
对于shard个数和备份个数的设置,要重载方法。
@Override
protected int numberOfShards() {
return 1;
}
@Override
protected int numberOfReplicas() {
return 0;
}
参考number of shards
4.给出elasticsearch集成测试的小例子
public class Alias {
private AliasType aliasType;
private MGIndex current;
public Alias(AliasType aliasType) {
this.aliasType = aliasType;
current = MGIndexFactory.get(aliasType.getPrefix());
}
//对于每个shard设置界限值
public boolean isNeedAdd(Client client) throws ClusterUnhealthException {
// Check shard doc
String index = current.getIndexName();
IndicesStatsResponse stats = client.admin().indices().prepareStats(index).execute().actionGet();
if (stats.getFailedShards() > 0) {
throw new ClusterUnhealthException("Get shards stats failed");
}
int docThreshold = current.getDocThreshold();
for (ShardStats st : stats.getShards()) {
String info = String.format("%s contains %d files which's threshold is %d", index,
st.getStats().getDocs().getCount(), docThreshold);
MGLogger.info(info);
if (st.getStats().getDocs().getCount() > docThreshold)
return true;
}
return false;
}
}
其测试函数如下:
public class AliasTest extends ESIntegTestCase{
Alias alias = null;
@Before
public void setUp() throws Exception {
super.setUp();
alias = new Alias(AliasType.ATTACHMENT);
}
@After
public void tearDown() throws Exception {
super.tearDown();
}
@Override
protected int numberOfShards() {
return 1;
}
@Override
protected int numberOfReplicas() {
return 0;
}
@Test
public void testIsNeedAdd() throws Exception {
indexRandom(true, false,
client().prepareIndex("test", "type1", "1").setSource("field1", "王宁"),
client().prepareIndex("test", "type1", "2").setSource("field1", "个人电话"),
client().prepareIndex("test", "type1", "3").setSource("field1", "12")
);
refresh();
MGIndex mgIndexMock = EasyMock.createMock(MGIndex.class);
EasyMock.expect(mgIndexMock.getIndexName()).andReturn("test").times(2);
EasyMock.expect(mgIndexMock.getDocThreshold()).andReturn(5).times(2);
EasyMock.replay(mgIndexMock);
alias.setCurrent(mgIndexMock);
assertFalse(alias.isNeedAdd(client()));
indexRandom(true, false,
client().prepareIndex("test", "type1", "4").setSource("field1", "王宁"),
client().prepareIndex("test", "type1", "5").setSource("field1", "个人电话"),
client().prepareIndex("test", "type1", "6").setSource("field1", "12")
);
assertTrue(alias.isNeedAdd(client()));
EasyMock.verify(mgIndexMock);
}
}
测试用例AliasTest中,使用EasyMock来mock current对象,通过继承ESIntegTestCase来进行es功能的集成测试——构筑一个只有一个shard无备份的集群,shard中文档的个数大于5时,使得isNeedAdd的返回值为空。
5.PowerMock和ESIntegTestCase配合产生的问题
由于ESIntegTestCase的祖先LuceneTestCase使用@RunWith(RandomizedRunner.class)和PowerMock在mock static函数时,使用的@RunWith(PowerMockRunner.class)冲突,使用这种方法无法将es的集成测试和PowerMock mock static 函数的能力结合在一起,尝试了Bootstrap using a Junit Rule和Delegate to another JUnit Runner仍没解决ESIntegTestCase和PowerMock融合的问题,记录留待解决。