[英]How do I Unit test/mock ElasticSearch
首先,我在我的應用程序中使用 Scala 和 sbt。
我正在使用 elastic4s 庫的 ElasticClient 連接到我的 ES 實例。 所以基本上我只需要能夠在我的單元測試中測試這些。 比如驗證我的數據是否真的進入了 ES 之類的東西。
嘲笑 ElasticSearch 是最好的方法還是有更有效的方法? 我將如何處理其中任何一個?
我發現您可以使用 ElasticClient.local 設置本地客戶端,但我似乎找不到很多示例。 我們想使用這個實現,所以如果你知道如何使用它,我想聽聽它,但如果有更好或更簡單的方法來完成這個工作。
由於彈性的搜索是用java和你的代碼太(或兼容的)最好的辦法是找出一種方法來啟動彈性搜索“嵌入” -剛開始他們的服務器在你的@Before
方法和其關閉/在清除它@After
。
幸運的是,似乎有人已經有了完全相同的想法 - https://orrsella.com/2014/10/28/embedded-elasticsearch-server-for-scala-integration-tests/
在我自己的代碼中,我最近編寫了一個小的 Embeddable 彈性搜索用於測試。 它將東西存儲在磁盤上,然后可以在使用后刪除文件。 我用它來運行我的各種 elasticsearch 單元測試。
它構成了一個單節點的 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 就好了。
對於我們的 ElasticSearch 測試,我們在每個測試使用的 Jenkins 構建服務器上使用一個始終在線的 ElasticSearch 實例。 對於本地測試,您必須啟動本地 ElasticSearch。 我們使用rest接口,而不是java api。
為了使單元測試可並行化,我們使用全局同步名稱池(用於索引名稱)。 每個測試都可以配置一個索引定義 json,如果可以在臟的(= 已經填充的)索引上運行。 一個小的測試超類(scalatest)將從池中獲取一個索引名稱。 如果需要一個干凈的索引,則刪除(可能)現有索引並創建新索引。 如果測試接受臟索引,則檢查索引定義是否與配置的相同。 如果沒有,索引也會重新創建。
根據您的測試用例,這使您可以使用一些索引,這些索引將不時重新創建,並經常被測試重用,從而加快測試執行速度。
我使用 junit + 模擬服務器( http://www.mock-server.com/ ),這會在 localhost:9200 上打開一個 Netty 並偵聽傳入連接。
您必須綁定規則,例如:
等等..
@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}
import com.sksamuel.elastic4s.requests.searches.{SearchHit, SearchHits, SearchRequest, SearchResponse, Total}
def toSearchResponse(hits: Array[SearchHit]): SearchResponse = {
val searchHits = SearchHits(Total(hits.size,""), 10, hits)
// successful, failed, total
val shards = Shards(1, 1, 1)
SearchResponse(10L, //took
false, // isTimedOut
false, // isTerminatedEarly
Map.empty, // suggest
shards, // _shards
None, // scrollId
Map.empty, // _aggregationsAsMap
searchHits) // hits
}
def jsonToHit(): SearchHit = {
val json = JacksonSupport.mapper.readTree(
"""{
"_id": "b141597ad13f9a3af3879c5ea64761f7",
"_index": "dev_local_collectr_person",
"_score": 4.9972124,
"_type": "_doc",
"_source": {"some":"elasticsearch response"}
}"""
)
JacksonSupport.mapper.treeToValue[SearchHit](json)
}
val mockElasticClient = mock[ElasticClient]
val searchResponse = toSearchResponse(Array(jsonToHit))
val futureResponse = Future {
RequestSuccess[SearchResponse](200, Some(""), Map.empty, searchResponse)
}
(mockElasticClient.execute(_: SearchRequest)(
_: Executor[Future],
_: Functor[Future],
_: Handler[SearchRequest,SearchResponse],
_: Manifest[SearchResponse])
).expects(*, *, *, *, *).returning(futureResponse)
這可以概括為這樣的概念
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:
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");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.