Mock FeignClient response - java

It's possible to mock response FeignClient via MockRestServiceServer(restTemplate)?
This example dosn't work:
Application.class
#SpringBootApplication
#EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
TicketService.class
#FeignClient("ws")
public interface TicketService {
#RequestMapping(value = "/tickets/")
List<Ticket> findAllTickets();
}
TestConfig.class
#Profile("test")
#Configuration
public class TestConfig {
#Bean
#Primary
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
MyTest.class
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Application.class}, properties = {"ws.ribbon.listOfServers:example.com"})
public class MyTest {
#Autowired
RestTemplate restTemplate;
#Autowired
DispatcherService dispatcherService; // service where the execution of the method TicketService.findAllTickets();
private MockRestServiceServer mockServer;
#Before
public void setUp() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void ticket() {
mockServer.expect(requestTo("http://example.com/tickets/"))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(new ClassPathResource("tickets.json"), MediaType.APPLICATION_JSON));
dispatcherService.run();
}
}
But going a request to the real server example.com.

At the moment I know 2 good approaches:
Use wiremock library (for Spring Boot i use spring-cloud-contract-wiremock)
Mockito (i use #MockBean)

Related

Spring Feign configuration: how to test all #Bean methods are called

I want to configure Spring feign with a configuration class, and I want to make sure that all the #Bean methods are called when Spring configures the feign client for me.
How to test it?
For example, I have:
#FeignClient(
name = "PreAuthSendRequest",
url = "${xxx.services.preauth.send.url}",
configuration = AppFeignConfig.class)
public interface RequestService {
#PostMapping("")
#Headers("Content-Type: application/json")
PreAuthResponse execute(#RequestBody PreAuthRequest preAuthRequest);
}
And AppFeignConfig.java:
#Configuration
#RequiredArgsConstructor
public class AppFeignConfig{
private final HttpClient httpClient;
private final Jackson2ObjectMapperBuilder contextObjectMapperBuilder;
#Bean
public ApacheHttpClient client() {
return new ApacheHttpClient(httpClient);
}
#Bean
public Decoder feignDecoder() {
return new JacksonDecoder((ObjectMapper)contextObjectMapperBuilder.build());
}
#Bean
public Encoder feignEncoder() {
return new JacksonEncoder((ObjectMapper)contextObjectMapperBuilder.build());
}
#Bean
public Retryer retryer() {
return Retryer.NEVER_RETRY;
}
#Bean
public ErrorDecoder errorDecoder() {
return new ServiceResponseErrorDecoder();
}
}
So, how to verify that all #Bean methods are called? I know #MockBean, but what I want to check is config.feignDecoder(), etc., are indeed called.
When I am trying to context.getBean(RequestService.class); and call execute() method, it seems to send a real request and without wiremock, it fails, obviously.
For now I have this:
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
class RequestServiceTest {
#Autowired
private ApplicationContext applicationContext;
#MockBean
private ApacheHttpClient client;
#MockBean
private Decoder feignDecoder;
#MockBean
private Encoder feignEncoder;
#MockBean
private Retryer retryer;
#MockBean
private ErrorDecoder errorDecoder;
#Test
void shouldRetrieveBeansFromApplicationContextToConstructConfigurationInstance() {
AppFeignConfig config = applicationContext.getBean(AppFeignConfig.class);
Assertions.assertEquals(config.feignEncoder(), feignEncoder);
Assertions.assertEquals(config.feignDecoder(), feignDecoder);
Assertions.assertEquals(config.errorDecoder(), errorDecoder);
Assertions.assertEquals(config.client(), client);
Assertions.assertEquals(config.retryer(), retryer);
}
}
I don't know if it is how it should be. If any idea, please comment.

Spring Testing - WebTestClient Could not autowire. No beans of 'WebTestClient' type found

I want to use WebTestClient in my tests. works like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
public class ControllerTest {
#Autowired
private WebTestClient webTestClient;
#Test
public void webtestClient () {
assertNotNull(webTestClient);
}
}
But now I want to inject WebTestClient into a helper class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
public class ControllerTest {
#Autowired
private Helper helper;
#Test
public void webtestClient () {
helper.check();
}
}
#Component
public class Helper {
#Autowired
private WebTestClient webTestClient;
public void check() {
assertNotNull(webTestClient);
}
}
Works, too. But Intellij is showing an error:
Could not autowire. No beans of 'WebTestClient' type found. more...
(Strg+F1)
New info: The test runs fine in Intellij, but not when running with maven.
Here is a test project with the problem:
https://github.com/kicktipp/demo
How can I use WebTestClient on my Helper class?
For what it's worth - I was able to fix this by simply specifying the AutoConfigureWebTestClient annotation explicitly:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
public class MyTestClass {
}
You have to build webTestClient before use it in tests
Use below, it will work
#Autowired
ApplicationContext context;
#Autowired
WebTestClient webTestClient;
#Before
public void setup() throws Exception {
this.webTestClient = WebTestClient.bindToApplicationContext(this.context).build();
}

Controller Mapping is not working in a spring multi-module project

I am creating a multi-module project in a spring boot application and have an internal library which is created for all the common services,repository etc.
Then I have added this internal library as a dependency to the parent project .
#EnableDiscoveryClient
#SpringBootApplication
#ComponentScan({"com.testlab.internal"})
public class ProfileServiceApplication {
#LoadBalanced
#Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ProfileServiceApplication.class , args);
}
}
Now after this change all the controller mapping stopped working, i.e
spring is not able find the handle method for any mapping .
But after adding a test controller in the ProfileServiceApplication class it worked seamlessly .
#EnableDiscoveryClient
#SpringBootApplication
#ComponentScan({"com.testlab.internal"})
public class ProfileServiceApplication {
#LoadBalanced
#Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ProfileServiceApplication.class , args);
}
#GetMapping("/")
public String home() {
return "hello";
}
}
This is working.
And my controller looks like this.
#Slf4j
#RestController
#RequestMapping
public class PringConversationController {
#GetMapping("/test")
public String home() {
return "hello";
}
}
Thanks for any help in advance

Spring Boot Integration Test with WireMock and Eureka fails with "No instances available"

When writing an integration test for a Spring Boot application (Service A) that uses a RestTemplate (and Ribbon under the hood) and Eureka to resolve a Service B dependency, I get a "No instances available" exception when calling the Service A.
I try to mock the Service B away via WireMock, but I don't even get to the WireMock server. It seems like the RestTemplate tries to fetch the Service instance from Eureka, which doesn't run in my test. It is disabled via properties.
Service A calls Service B.
Service discovery is done via RestTemplate, Ribbon and Eureka.
Does anyone have a working example that includes Spring, Eureka and WireMock?
I faced the same problem yesterday and for the sake of completeness here is my solution:
This is my "live" configuration under src/main/java/.../config:
//the regular configuration not active with test profile
#Configuration
#Profile("!test")
public class WebConfig {
#LoadBalanced
#Bean
RestTemplate restTemplate() {
//you can use your regular rest template here.
//This one adds a X-TRACE-ID header from the MDC to the call.
return TraceableRestTemplate.create();
}
}
I added this configuration to the test folder src/main/test/java/.../config:
//the test configuration
#Configuration
#Profile("test")
public class WebConfig {
#Bean
RestTemplate restTemplate() {
return TraceableRestTemplate.create();
}
}
In the test case, I activated profile test:
//...
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ServerCallTest {
#Autowired
private IBusiness biz;
#Autowired
private RestTemplate restTemplate;
private ClientHttpRequestFactory originalClientHttpRequestFactory;
#Before
public void setUp() {
originalClientHttpRequestFactory = restTemplate.getRequestFactory();
}
#After
public void tearDown() {
restTemplate.setRequestFactory(originalClientHttpRequestFactory);
}
#Test
public void fetchAllEntries() throws BookListException {
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
mockServer
.andExpect(method(HttpMethod.GET))
.andExpect(header("Accept", "application/json"))
.andExpect(requestTo(endsWith("/list/entries/")))
.andRespond(withSuccess("your-payload-here", MediaType.APPLICATION_JSON));
MyData data = biz.getData();
//do your asserts
}
}
This is what I done in my project:
Somewhere in your project configuration:
#LoadBalanced
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
return restTemplate;
}
#Bean
public SomeRestClass someRestClass () {
SomeRestClass someRestClass = new SomeRestClass (environment.getProperty("someservice.uri"), restTemplate(new RestTemplateBuilder()));
return parameterRest;
}
And SomeRestClass:
public class SomeRestClass {
private final RestTemplate restTemplate;
private final String someServiceUri;
public LocationRest(String someServiceUri, RestTemplate restTemplate) {
this.someServiceUri= someServiceUri;
this.restTemplate = restTemplate;
}
public String getSomeServiceUri() {
return locationUri;
}
public SomeObject getSomeObjectViaRest() {
//making rest service call
}
}
And Test class for SomeRestClass:
#RunWith(SpringRunner.class)
#RestClientTest(SomeRestClass.class)
public class SomeRestClassTest {
#Autowired
private SomeRestClass someRestClass;
#Autowired
private MockRestServiceServer server;
#Test
public void getSomeObjectViaRestTest() throws JsonProcessingException {
SomeResponseObject resObject = new SomeResponseObject();
ObjectMapper objectMapper = new ObjectMapper();
String responseString = objectMapper.writeValueAsString(resObject);
server.expect(requestTo(locationRest.getSomeServiceUri() + "/some-end-point?someParam=someParam")).andExpect(method(HttpMethod.POST))
.andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON_UTF8).body(responseString));
someRestClass.getSomeObjectViaRest();
}
}
Note: I diasbled eureka client because otherwise you have to mock a eureka server. So I added eureka.client.enabled=false in test application.properties
Hopefully this can help someone. I was getting the same error with Ribbon, but without Eureka.
What helped me was
1) Upgrading to the latest version of WireMock (2.21) in my case
2) Adding a wiremock rule stub for url "/" to answer Ribbon's ping
If you are using Eureka just bypass in test/application.properties using eureka.client.enabled=false

Test security with spring boot

I confused with configuration for unit test:
This's my test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class MyTest {
private MockMvc mockMvc;
#Autowired
private MyController myController;
#Before
public void setUp() {
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
#Test
public void test() {
}
#Configuration
static class Config {
#Bean
MyController myController() {
return new MyController();
}
}
}
When I run it, I get:
java.lang.IllegalStateException: springSecurityFilterChain cannot be
null. Ensure a Bean with the name springSecurityFilterChain
implementing Filter is present or inject the Filter to be used.
How to configure it properly?

Categories