简体   繁体   English

如何模拟 Elasticsearch Java 客户端?

[英]How to mock an Elasticsearch Java Client?

Do you know how to propertly mock the Elasticsearch Java Client?您知道如何适当地模拟 Elasticsearch Java 客户端吗? Currently to mock the following request in Java:目前在 Java 中模拟以下请求:

SearchResponse response = client.prepareSearch(index)
                .setTypes(type)
                .setFrom(0).setSize(MAX_SIZE)
                .execute()
                .actionGet();
SearchHit[] hits = response.getHits().getHits();

I have to mock:我必须嘲笑:

  • client.prepareSearch client.prepareSearch
  • SearchRequestBuilder:搜索请求构建器:
    • builder.execute builder.execute
    • builder.setSize builder.setSize
    • builder.setFrom builder.setFrom
    • builder.setTypes builder.setTypes
  • SearchResponse:搜索响应:
    • action.actionGet action.actionGet
  • SearchResponse:搜索响应:
    • response.getHits response.getHits
    • searchHits.getHits searchHits.getHits

So my test looks like:所以我的测试看起来像:

SearchHit[] hits = ..........;

SearchHits searchHits = mock(SearchHits.class);
when(searchHits.getHits()).thenReturn(hits);

SearchResponse response = mock(SearchResponse.class);
when(response.getHits()).thenReturn(searchHits);

ListenableActionFuture<SearchResponse> action = mock(ListenableActionFuture.class);
when(action.actionGet()).thenReturn(response);

SearchRequestBuilder builder = mock(SearchRequestBuilder.class);
when(builder.setTypes(anyString())).thenReturn(builder);
when(builder.setFrom(anyInt())).thenReturn(builder);
when(builder.setSize(anyInt())).thenReturn(builder);
when(builder.execute()).thenReturn(action);

when(client.prepareSearch(index)).thenReturn(builder);

Ugly... So I would like to known if there is a more "elegant way" to mock this code.丑...所以我想知道是否有更“优雅的方式”来模拟这段代码。

Thanks谢谢

I've come across a similar problem when mocking builders, so I thought I'd experiment to see if there is a nicer way.我在模拟构建器时遇到了类似的问题,所以我想我会尝试看看是否有更好的方法。

As Mr Spoon said, it's probably nicer if you can avoid doing do this in the first place as it is not your code and could be assumed to "just work", but I thought I'd give it a go anyway.正如 Spoon 先生所说,如果您一开始就可以避免这样做可能会更好,因为它不是您的代码,并且可以被假定为“正常工作”,但我想无论如何我都会试一试。

I've come up with a (maybe crude) way of doing it by using "default answers" in Mockito.通过在 Mockito 中使用“默认答案”,我想出了一种(可能是粗略的)方法。 I'm still deciding if I like it or not.我还在决定我是否喜欢它。

Here's my builder...这是我的建设者...

public class MyBuilder {

    private StringBuilder my;

    public MyBuilder() {
        my = new StringBuilder();
    }

    public MyBuilder name(String name) {
        my.append("[name=").append(name).append("]");
        return this;
    }

    public MyBuilder age(String age) {
        my.append("[age=").append(age).append("]");
        return this;
    }

    public String create() {
        return my.toString();
    }
}

(Pretty basic right?) (很基本的吧?)

I got my test to look something like this...我的测试看起来像这样......

// Create a "BuilderMocker" (any better name suggestions welcome!) 
BuilderMocker<MyBuilder> mocker = BuilderMocker.forClass(MyBuilder.class);
// Get the actual mock builder
MyBuilder builder = mocker.build();

// expect this chain of method calls...
mocker.expect().name("[NAME]").age("[AGE]");

// expect this end-of-chain method call...
Mockito.when(builder.create()).thenReturn("[ARGH!]");

Now if I do the following...现在,如果我执行以下操作...

System.out.println(builder.name("[NAME]").age("[AGE]").create());

...I expect "[ARGH!]" to be output. ...我希望输出“[ARGH!]”。

If I altered the last line...如果我更改了最后一行...

System.out.println(builder.name("[NOT THIS NAME]").age("[AGE]").create());

...then I expect it to break with a NullPointerException. ...然后我希望它会因 NullPointerException 而中断。

Here is the actual "BuilderMocker"...这是实际的“BuilderMocker”...

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;

import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class BuilderMocker<T> {

    private Class<T> clazz;
    private T recorder;
    private T mock;

    // Create a BuilderMocker for the class
    public static <T> BuilderMocker<T> forClass(Class<T> clazz) {
        return new BuilderMocker<T>(clazz);
    }

    private BuilderMocker(Class<T> clazz) {
        this.clazz = clazz;
        this.mock = mock(clazz);
        createRecorder();
    }

    // Sets up the "recorder"
    private void createRecorder() {
        recorder = mock(clazz, withSettings().defaultAnswer(new Answer<Object>() {

            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                // If it is a chained method...
                if (invocation.getMethod().getReturnType().equals(clazz)) {
                    // Set expectation on the "real" mock...
                    when(invocation.getMethod().invoke(mock, invocation.getArguments())).thenReturn(mock);
                    return recorder;
                }
                return null;
            }

        }));
    }

    // Use this to "record" the expected method chain
    public T expect() {
        return recorder;
    }

    // Use this to get the "real" mock...
    public T build() {
        return mock;
    }
}

Not sure if there is a "built-in" way of doing this in Mockito, but this seems to work.不确定在 Mockito 中是否有“内置”的方式来做到这一点,但这似乎有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM