elasticsearch,Scala,Unit Testing,elasticsearch" /> elasticsearch,Scala,Unit Testing,elasticsearch" />

Scala 如何进行单元测试/模拟ElasticSearch

Scala 如何进行单元测试/模拟ElasticSearch,scala,unit-testing,elasticsearch,Scala,Unit Testing,elasticsearch,首先,我在应用程序中使用Scala和sbt 我正在使用elastic4s库的ElasticClient连接到我的ES实例。所以基本上我只需要能够在我的单元测试中测试这些。比如验证我的数据是否真的变成了ES之类的东西 模仿ElasticSearch是最好的方法还是有更有效的方法?我该怎么做呢 我发现您可以使用ElasticClient.local设置本地客户端,但我似乎找不到很多示例。我们希望使用此实现,因此如果您知道如何使用此功能,我很想听听您的看法,但如果有更好或更简单的方法来完成此功能,则会

首先,我在应用程序中使用Scala和sbt

我正在使用elastic4s库的ElasticClient连接到我的ES实例。所以基本上我只需要能够在我的单元测试中测试这些。比如验证我的数据是否真的变成了ES之类的东西

模仿ElasticSearch是最好的方法还是有更有效的方法?我该怎么做呢

我发现您可以使用ElasticClient.local设置本地客户端,但我似乎找不到很多示例。我们希望使用此实现,因此如果您知道如何使用此功能,我很想听听您的看法,但如果有更好或更简单的方法来完成此功能,则会起作用。

因为您的代码太多(或兼容),最好的方法是找到一种引导弹性搜索“可嵌入”的方法-只需在
@Before
方法中启动服务器,然后在
@After
中关闭/清除服务器即可


幸运的是,似乎有人已经有了完全相同的想法-

对于我们的ElasticSearch测试,我们在Jenkins build服务器上使用了一个始终运行的ElasticSearch实例,每个测试都使用该实例。对于本地测试,您必须启动本地ElasticSearch。我们使用rest接口,而不是JavaAPI

为了使单元测试可并行化,我们使用一个全局的、同步的名称池(用于索引名)。每个测试都可以配置一个索引定义json,如果可以在脏(=已填充)索引上运行。一个小的测试超类(scalatest)将从池中获取索引名。如果需要干净的索引,则会删除(可能)现有索引并创建新索引。如果测试接受脏索引,将检查索引定义是否与配置的索引定义相同。如果没有,也会重新创建索引


根据您的测试用例,这使您能够使用一些索引,这些索引将偶尔重新创建一次,并经常被测试重用,从而加快测试执行速度。

在我自己的代码中,我最近编写了一个用于测试的小型可嵌入弹性搜索。它将东西存储在磁盘上,使用后可以删除文件。我用它来运行我的各种elasticsearch单元测试

它构成了一个单节点弹性搜索集群。此节点支持完整的elasticsearch API

 /**
 * A simple embeddable Elasticsearch server. This is great for integration testing and also
 * stand alone tests.
 *
 * Starts up a single ElasticSearch node and client.
 */
public class EmbeddedElasticsearchServer
{
  public EmbeddedElasticsearchServer(String storagePath) {

    storagePath_ = storagePath;
    ImmutableSettings.Builder elasticsearchSettings = ImmutableSettings.settingsBuilder()
      .put("http.enabled", "false")
      .put("path.data", storagePath_);

    node_ = new NodeBuilder()
      .local(true)
      .settings(elasticsearchSettings.build())
      .node();

    client_ = node_.client();
  }



  public Client getClient() {
    return client_;
  }


  public void shutdown()
  {
    node_.close();
  }

  public void deleteStorage() throws IOException
  {
    File storage = new File(storagePath_);

    if(storage.exists())
    {
      FileUtils.deleteDirectory(storage);
    }

  }

  private Client client_;
  private Node node_;
  private String storagePath_;
}
要使用它,只需调用getClient,然后就可以很好地使用Elasticsearch Java API。

我使用junit+mock server(),这将在localhost:9200上打开一个Netty并侦听传入连接

您必须绑定以下规则:

  • “/”->给出ES版本
  • “/\u搜索”->发送此JSON
  • 等等

    @Rule
    public MockServerRule mockServerRule = new MockServerRule(9200, this);
    
    private MockServerClient mockClient;
    
    @Before
    public void setup() {
    
    mockClient.when(
            HttpRequest.request("/")
    ).respond(HttpResponse.response("{\"name\" : \"mock\", \"cluster_name\" : \"mock\", \"version\": { \"number\" : \"2.4\"}}").withHeader("Content-Type", "application/json"));
    
    mockClient.when(
            HttpRequest.request()
            .withPath("*/_count")
    ).respond(HttpResponse.response("{\"count\": 0}").withHeader("Content-Type", "application/json"));
    
    mockClient.when(
            HttpRequest.request()
                    .withPath("*/_search")
    ).respond(HttpResponse.response("{\"hits\": {\"total\" : 0}}").withHeader("Content-Type", "application/json"));
    
    mockClient.when(
            HttpRequest.request()
    ).respond(HttpResponse.response("{\"ok\":\"2.4\"}").withHeader("Content-Type", "application/json"));
    }
    
这不是保存任何数据,而是一个非常简单的模拟。我用它来测试一个使用elasticsearch hadoop插件的Java Spark应用程序


当然,这是用于单元测试。

要回答没有物理网络设施的问题:

使用
SearchRequest
SearchResponse

import com.sksamuel.elastic4s.{RequestSuccess,Executor,Functor,Handler,JacksonSupport}
导入com.sksamuel.elastic4s.requests.searches.{SearchHit,SearchHits,SearchRequest,SearchResponse,Total}
def toSearchResponse(点击:数组[SearchHit]):SearchResponse={
val searchHits=searchHits(总数(hits.size,“”),10,hits)
//成功,失败,全部
val shards=碎片(1,1,1)
SearchResponse(10L,//take
false,//isTimedOut
false,//isTerminatedEarly
Map.empty,//建议
碎片,//\u碎片
无,//滚动ID
Map.empty,//\u聚合asmap
searchHits)//hits
}
def jsonToHit():SearchHit={
val json=JacksonSupport.mapper.readTree(
"""{
“_id”:“b141597ad13f9a3af3879c5ea64761f7”,
“\u索引”:“开发人员本地采集人员”,
“_分数”:4.9972124,
“\u类型”:“\u单据”,
“_source”:{“some”:“elasticsearch response”}
}"""
)
JacksonSupport.mapper.treeToValue[SearchHit](json)
}
val mockElasticClient=mock[ElasticClient]
val searchResponse=toSearchResponse(数组(jsonToHit))
val futureResponse=未来{
RequestSuccess[SearchResponse](200,有些(“”),Map.empty,SearchResponse)
}
(mockElasticClient.execute(\ux:SearchRequest)(
_:遗嘱执行人[未来],
_:函子[未来],
_:Handler[SearchRequest,SearchResponse],
_:清单[SearchResponse])
).预期(*,*,*,*,*,*)。返回(未来响应)
这可以概括为这样的概念

def to[U](hits: Array[SearchHit]): U = {
  val searchHits = SearchHits(Total(hits.size,""), 10, hits)
  // successful, failed, total
  val shards = Shards(1, 1, 1)
  U( ... ) 
}

val mockElasticClient = mock[ElasticClient]
val searchResponse = toSearchResponse(Array(jsonToHit))
val futureResponse = Future {
    RequestSuccess[U](200, Some(""), Map.empty, searchResponse)
}

(mockElasticClient.execute(_: T)(
    _: Executor[Future],
    _: Functor[Future],
    _: Handler[T,U],
    _: Manifest[U])
).expects(*, *, *, *, *).returning(futureResponse)
其中,
T
是传递到execute的请求对象,
U
是嵌套的返回对象

其他一些样本T:

  • 索引请求
  • 搜索请求
其他一些示例U:

  • 指数响应
  • 搜索响应

让我保持纯粹,但验证我的数据是否真正进入ES不是单元测试,而是集成测试,因为您正在检查一个组件(您的代码)与另一个组件(ES)的性能。这些是集成测试。此链接指向集成测试。单元测试有参考资料吗?Thanks@Bob如果您需要嵌入式数据库,我不确定测试是否仍然是单元。
SearchResponse searchResponse = mock(SearchResponse.class);
SearchResponse scrollResponse = mock(SearchResponse.class);
when(analysisRestClient.search(any(), Mockito.any(RequestOptions.class))).thenReturn(searchResponse);
SearchHits searchHits = mock(SearchHits.class);
SearchHit searchHit = mock(SearchHit.class);
when(searchResponse.getHits()).thenReturn(searchHits);
when(searchHits.iterator()).thenReturn(Iterators.singletonIterator(searchHit));
EventDto eventDto = new EventDto();
when(searchHit.getSourceAsString()).thenReturn(JSON.toJSONString(eventDto));
when(searchHits.getTotalHits()).thenReturn(1L);
when(analysisRestClient.scroll(any(), Mockito.any(RequestOptions.class))).thenReturn(scrollResponse);
when(searchResponse.getScrollId()).thenReturn("ddd");