找到你要的答案

Q:How to mock feign.Client.Default with Mockito

Q:如何模拟feign.client.default Mockito

I'm writing a Dropwizard application and using Feign for building client calls to outside services. I have custom encoders and decoders I'm registering with the feign.Builder like so:

    this.feignBuilder = Feign.builder()
            .contract(new JAXRSContract()) // we want JAX-RS annotations
            .encoder(new JacksonEncoder()) // same as what dropwizard is using
            .decoder(new CustomDecoder())
            .errorDecoder(new CustomErrorDecoder())
            .requestInterceptor(new AuthKeyInterceptor(config.getInterceptor()));

I'm writing unit tests for the feign client calls so I can watch how the feign machinery deals with my encoder/decoder overrides and bubbles on exceptions. I'm not interested in writing integration tests with a fake server right now (this is the most common type of test i see people writing for this situation).

This should be straight forward. I want to mock the point at which feign makes the request and have it return my fake response. That means I should mock the call to feign.Client.Default.execute so it returns my fake response when it makes the request a this call site. An example of what that mock looks like:

String responseMessage = "{\"error\":\"bad\",\"desc\":\"blah\"}";
feign.Response feignResponse = FeignFakeResponseHelper.createFakeResponse(404,"Bad Request",responseMessage);
Client.Default mockFeignClient = mock(Client.Default.class);
try {
     when(mockFeignClient.execute(any(feign.Request.class),any(Request.Options.class))).thenReturn(feignResponse);
} catch (IOException e) {
     assertThat(true).isFalse(); // fail nicely
}

No luck. The Cleint.Default class isn't mocked when I reach the call site for the request in the code. What am I doing wrong?

我写了一dropwizard应用和使用外国建筑客户端调用外部服务。我已经习惯的编码器和解码器我注册的建筑师一样假装:

    this.feignBuilder = Feign.builder()
            .contract(new JAXRSContract()) // we want JAX-RS annotations
            .encoder(new JacksonEncoder()) // same as what dropwizard is using
            .decoder(new CustomDecoder())
            .errorDecoder(new CustomErrorDecoder())
            .requestInterceptor(new AuthKeyInterceptor(config.getInterceptor()));

我的伪装客户端调用编写单元测试让我看如何处理我假装机械编码器/解码器的覆盖和泡沫的例外。我现在不想用一个假服务器编写集成测试(这是我看到这种情况下人们最常见的测试类型)。

这应该是直线前进。我想嘲笑这一点假装使请求并返回响应我的假。这就意味着我要模拟电话feign.client.default.execute所以它返回我的假响应使请求调用网站的时候。一个例子,什么样的嘲笑看起来像:

String responseMessage = "{\"error\":\"bad\",\"desc\":\"blah\"}";
feign.Response feignResponse = FeignFakeResponseHelper.createFakeResponse(404,"Bad Request",responseMessage);
Client.Default mockFeignClient = mock(Client.Default.class);
try {
     when(mockFeignClient.execute(any(feign.Request.class),any(Request.Options.class))).thenReturn(feignResponse);
} catch (IOException e) {
     assertThat(true).isFalse(); // fail nicely
}

没有运气。的cleint。默认的类不是嘲笑当我到代码中的请求调用站点。我做错什么了?

answer1: 回答1:

It turns out Mockito is not powerful enough to do the thing I assumed it could do. The correct solution is to use PowerMockito to mock the constructor so Client.Default returns the mocked instance when it is instantiated in the class that holds that reference.

After a lot of compilation-error pain I got PowerMockito to compile and it seemed like it was going to work. Alas it failed to return my mock and the calls were still going through. I've tried PowerMockito in the past and never got around to using it because of the extra problems it caused. So I'm still of the opinion that it's not super easy to just plug and play.

It's a shame that trying to do something like this is so hard.

原来Mockito是没有强大到足以做这件事我认为可以做的。正确的方法是用PowerMockito嘲笑构造函数以客户端。默认返回嘲笑实例时认为,参考类的实例化。

很多编译错误的痛苦后我得到了PowerMockito编译它好像要去工作。唉,它没有返回我的模拟和电话仍在通过。我想PowerMockito在过去从来没有去使用它,因为它造成的额外的问题。所以我仍然认为它不是超级简单的即插即用。

这是一个耻辱,试图做这样的事情是如此艰难。

answer2: 回答2:

As mentioned before, Mockito is not powerful enough. I solved this with a manual mock.

It's easier than it sounds:

MyService.Java

public class MyService{
    //My service stuff      

    private MyFeignClient myFeignClient;

    @Inject //this will work only with constructor injection
    public MyService(MyFeignClient myFeignClient){
        this.MyFeignClient = myFeignClient
    }


    public void myMethod(){
        myFeignClient.remoteMethod(); // We want to mock this method
    }
}

MyFeignClient.Java

@FeignClient("target-service")
public interface MyFeignClient{

    @RequestMapping(value = "/test" method = RequestMethod.GET)
    public void remotemethod();
}

If you want to test the code above while mocking the feignclient, do this:

MyFeignClientMock.java

@Component
public class MyFeignClientMock implements MyFeignClient {

    public void remoteMethod(){
         System.out.println("Mocked remoteMethod() succesfuly");
    }
}

MyServiceTest.java

@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {

    private MyService myService;

    @Inject
    private MyFeignClientMock myFeignClientMock;

    @Before
    public void setUp(){
       this.myService = new MyService(myFeignClientMock); //inject the mock
    }

    //Do tests normally here...
}

As mentioned before, Mockito is not powerful enough. I solved this with a manual mock.

它比声音更容易:

那么,java

public class MyService{
    //My service stuff      

    private MyFeignClient myFeignClient;

    @Inject //this will work only with constructor injection
    public MyService(MyFeignClient myFeignClient){
        this.MyFeignClient = myFeignClient
    }


    public void myMethod(){
        myFeignClient.remoteMethod(); // We want to mock this method
    }
}

myfeignclient java。

@FeignClient("target-service")
public interface MyFeignClient{

    @RequestMapping(value = "/test" method = RequestMethod.GET)
    public void remotemethod();
}

如果你想测试上面的代码而嘲笑feignclient,这样做:

myfeignclientmock.java

@Component
public class MyFeignClientMock implements MyFeignClient {

    public void remoteMethod(){
         System.out.println("Mocked remoteMethod() succesfuly");
    }
}

myservicetest.java

@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {

    private MyService myService;

    @Inject
    private MyFeignClientMock myFeignClientMock;

    @Before
    public void setUp(){
       this.myService = new MyService(myFeignClientMock); //inject the mock
    }

    //Do tests normally here...
}
java  jersey  mockito  dropwizard  netflix-feign