We created a common class where a RestTemplate is passed as a parameter. This rest template is used to facilitate:
postForEntity
exchange
However, since it is not Autowired in the common class, I am not able to create unit tests that mocks the RestTemplate. Is there a work around for this?
Setup:
Spring boot Project A - initiates a rest integration and utilises the common class. This Project A instantiates the #Autowired RestTemplate rest template and pass it as a parameter to the common class method.
Spring boot Common Class - conducts the rest integration but uses the rest template passed by Project A. This common class I am unable to conduct the unit test since I cannot mock the Rest Template.
This is a java spring boot project.
Addendum:
**COMMON CLASS
public class RestService {
public static void invoke(RestTemplate restTemplate, RequestDetails requestDetails) {
switch (requestDetails.getHttpMethod()) {
case POST:
HttpEntity<?> postEntity = new HttpEntity<>(request, httpHeaders);
restResponse = restTemplate.postForEntity(requestDetails.getUrl(), postEntity, String.class);
break;
case GET:
HttpEntity<?> getEntity = new HttpEntity<>(httpHeaders);
restResponse = restTemplate.exchange(requestDetails.getUrl(),
HttpMethod.GET, getEntity, String.class);
break;
default:
break;
}
}
}
** INVOKING CLASS
public class InvokingClass {
#Autowired
private RestTemplate restTemplate;
public void invoke() {
//RequestDetails construct here ...
RestService.invoke(restTemplate,requestDetails)
}
}
I'm not sure what you are trying to achieve. If you want to test your RestService you should be able to do something like this:
#Test
void test() {
RestTemplate templateMock = mock(RestTemplate.class);
RestService.invoke(templateMock, new RequestDetails());
verify(templateMock).postForEntity(any(URI.class), any(Object.class), any(Class.class));
}
If this is not what you're looking for please provide more details on what you're trying to test. Thanks.
Related
I am using Spring Boot 1.5.x (Spring 4.2.x), and I created a RestClientSdk spring component class as shown here:
#Component
public class RestClientSdkImpl implements RestClientSdk {
private RestTemplate restTemplate;
#Autowired
public RestClientSdkImpl(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
...
//other methods kept out for brevity
}
I have also defined a DefaultRestTemplateCustomizer spring component as shown here:
#Component
public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer {
private LogClientHttpRequestInterceptor logClientHttpRequestInterceptor;
#Autowired
public DefaultRestTemplateCustomizer(LogClientHttpRequestInterceptor logClientHttpRequestInterceptor) {
this.logClientHttpRequestInterceptor = logClientHttpRequestInterceptor;
}
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(logClientHttpRequestInterceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
}
}
With that, I've defined a test class as shown below that uses the #RestClientTest annotation as shown below.
#RunWith(SpringRunner.class)
#RestClientTest(RestClientSdk.class)
#ActiveProfiles("test")
/*
* The RestClientTest only includes the most minimal configuration to include a rest template builder,
* so we include the rest sdk auto config within the scope of the test
*/
#ImportAutoConfiguration(RestSdkAutoConfiguration.class)
public class RestClientApplicationBehaviourTest{
#Autowired
private RestClientSdk restClientSdk;
#Autowired
private MockRestServiceServer mockRestServiceServer;
/**
* A simple Http Get that retrieves a JSON document from a rest server and
* produces a plain old java object as a response.
*/
#Test
public void testPlainHttpGet() throws IOException{
//ARRANGE
RestClientDto<?> simpleGetRequest = simpleHttpGet();
mockRestServiceServer.expect(once(), requestTo("http://localhost:8080/account/1234567890"))
.andRespond(withSuccess(IOUtils.getInputAsString("/stubs/account.json"),MediaType.APPLICATION_JSON));
//ACT
Account account = restClientSdk.send(simpleGetRequest, Account.class);
//ASSERT
mockRestServiceServer.verify();
Assert.assertNotNull(account);
Assert.assertNotNull(account.getAccountId());
Assert.assertNotNull(account.getFirstName());
Assert.assertNotNull(account.getLastName());
}
...
//not including other methods for brevity
}
PROBLEM
Because the MockRestServiceServer builder overrides the BufferingClientHttpRequestFactory in my rest template with a MockClientHttpRequestFactory, I am getting a null response from my body. This is because the logging interceptor is reading the input stream coming from the response and as such the stream no longer has content to read. The BufferingClientHttpRequestFactory would prevent that from happening. Now, I know that as of Spring 5.0.5, there is an extra option in the MockRestServiceServer builder called bufferContent, but I don't have the option of moving to Spring 5.x (Spring Boot 2.x), so I was wondering if there is a way to get this configured using Spring Boot 1.5.x / Spring 4.2.x.
I thank you in advance!
Juan
In order to workaround the issue, I needed to define a test configuration, that would allow me to override the client request factory. Please see the code below. It is a bit hacky, but I suppose the real solution here would be to upgrade to Spring 5.x / Spring Boot 2.x.
#Configuration
#Profile("test")
public class MockRestServiceServerConfiguration {
/**
* Wrap the Mock Rest client factory in the buffered one.
* #param restClientSdk The rest client SDK containing the rest template to use.
* #return The mock rest service server to use.
*/
#Bean
public MockRestServiceServer mockRestServiceServer(RestClientSdkImpl restClientSdkImpl) {
RestTemplate restTemplate = restClientSdkImpl.getRestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
//need to do this because getRequestFactory returns InterceptingHttpRequestFactory wraping the mock rest service server request factory
List<ClientHttpRequestInterceptor> templateInterceptors = restTemplate.getInterceptors();
restTemplate.setInterceptors(null);
//now we wrap the delegate, which should be the mock rest service server request factory
BufferingClientHttpRequestFactory bufferingFact = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
restTemplate.setRequestFactory(bufferingFact);
restTemplate.setInterceptors(templateInterceptors);
return server;
}
}
This is my controller class
#Controller
public class myClass extends BaseController {
#RequestMapping(value = "/myList", method = RequestMethod.POST, consumes = "application/json")
public #ResponseBody MyBaseVO getMyList(#RequestBody MyBaseVO requestObj) throws Exception {
requestObj.setEntity(getConfig(LanguageConstant.MY_MODULE_ID));
MyBaseVO response = (MyBaseVO) this.execute(requestObj,LanguageConstant.MY_MODULE_ID);
return response;
}
}
I wanted to write a mock test case but stuck at this point, also i'm not sure i am doing correct or not. Can anyone help me in this.
public class myClassTest{
myClass spy = Mockito.spy(new myClass ());
Mockito.when(spy.getMyList(itemRequest)).thenReturn(itemReq);
}
I am not sure how to use url myList in testcase and request and response object. I have created a json file of my request and response. Since it is using parent class therefore I am using spy for my testcase.
Since you are using Spring, you should have a look at Spring's platform Mock Test implementation for Spring MVC.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-mvc-test-framework
I have a SB service that is being used to send email. I wanted to use that in my existing application , how can I do that? I am thinking to create a controller that handles incoming HttpRequest and HttpResponse. But still no idea on how my existing application will invoke it. I need some high level overview too on how exactly SB application will run independently with other application.
P.S.- there is no UI interface for the email service so i wont be mapping url like we do in controllers generally.
Here is my sample email service:
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
I created a controller like this earlier to test this out:
#RestController
public class CourseController {
#Autowired
private EmailService emailService;
#RequestMapping(value = "/sendEmail", method = RequestMethod.POST)
public void sendEmail() {
emailService.sendMessage("abc#gmail.com","{client#gmail.com}", "testSubject",new Context);
}
Context has some business data.
I have a jsp that I am using and posting my form through which it is mapping. It all works fine.
But now I want to integrate this with my existing application (its on struts 1)so there wont be any uri to map. There must be some kind of HttpRequest need to be created from the invoking application and my controller should be handling it. How can I achieve this?
You have already this service implemented? Then you need a RestController class that mapps the uri of your choice. In this class you need to inject the service class that realizes your email sending method. Is this class annotated with #Service? Quite difficult to explain without seeing your code. Here an example for a REST-Interface:
#RestController
#RequestMapping("/api/v1/email")
public class RestClass {
private EmailService emailService;
#Autowired
public RestClass(EmailService emailService){
this.emailService = emailService;
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> sendEmail(#RequestBody EmailDTO emailDTO){
String emailAdress = emailDTO.getEmail();
this.emailService.sendEmail(emailAdress);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
So in this case the emailService would be the class that has the method that sends your email. This class should be annotated with #Service. Hope that helps.
Here your existing class:
#Service
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
And in case the injection doesn't work you have to annotate your application class with #ComponentScan({"com.foo.dal.","com.foo.notification."}) replace this packages simply with the package of your service and resource class.
I am not sure about the problem. If I am right that you need to call a rest service from your application. In this case it is lot easier and convenient to use Spring's RestTemplate link
You can get some overview here
So I'm writing this web app with Spring Boot using Spring Data with JPA and Spring MVC and I would like to make mock controller tests. I figured out how to test the get method, but in my controllers post method a new JPA entity is being either persisted or updated with my service. Here is what my controller looks like:
#Controller
#RequestMapping("/registerMember")
public class RegisterMemberController {
#Autowired
private MemberService memberService;
#GetMapping
public String index(RegisterMemberBean registerMemberBean) {
return "registerMember";
}
#PostMapping
public String handleSubmit(#Valid RegisterMemberBean registerMemberBean, BindingResult bindingResult, Model model) {
Member member = registerMemberBean.getMember();
boolean isRepeatPasswordCorrect = !isRepeatPasswordIncorrect(member.getPassword(), registerMemberBean.getComparePassword());
if(isAnyErrors(isRepeatPasswordCorrect, !bindingResult.hasErrors())) {
if(!isRepeatPasswordCorrect) {
model.addAttribute("isRepeatPasswordIncorrect", true).
addAttribute("isRepeatPasswordIncorrectMsg", "Passwords don't match");
}
return "registerMember";
}
boolean errUsername = !memberService.isNoOtherEntityWithUserName(0, member.getUserName());
boolean errEmail = !memberService.isNoOtherEntityWithEmail(0, member.getEmail());
if(errUsername || errEmail) {
if(errUsername) {
model.addAttribute("isExistingUserName", true).addAttribute("isExistingUserNameMsg", "Already a user with that username");
} if(errEmail) {
model.addAttribute("isExistingEmail", true).addAttribute("isExistingEmailMsg", "Already a user with that email");
}
return "registerMember";
}
getMainService().save(member);
return redirectTo("index", new RedirectEntity("member", member.getId()));
}
}
Now in my mock controller test i want to make make sure that my post method does the following:
Reload the page if the BindingResults has any errors
My service persists the member JPA entity in db (if no errors)
Method redirects me to the index page
This is what my (poor) test class looks like so far:
#RunWith(SpringRunner.class)
#TestPropertySource(locations="classpath:application_test.properties")
#WebAppConfiguration
public class RegisterMemberControllerTest {
private MockMvc mockMvc;
#MockBean
private MemberService memberService;
#MockBean
private RegisterMemberController controller;
#Before
public void init() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(new StandaloneMvcTestViewResolver()).build();
controller.setMainService(memberService);
}
#Test
public void testIndex() throws Exception {
mockMvc.perform(get("/registerMember"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("registerMember");
}
#Test
public void testHandleSubmit() throws Exception {
RegisterMemberBean registerMemberBean = new RegisterMemberBean();
registerMemberBean.setMember(TestFixture.getValidMemberWithoutReferences());
Member member = TestFixture.getValidMember();
mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect(status().isOk());
when(mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect((ResultMatcher) memberService.save(member)).andExpect(forwardedUrl("redirect:/index/member=" + member.getId() + ".html")));
}
}
to my understanding spring boot uses Mockito. I have some experience with EasyMock but I would like to use the spring defaults as much as possible. Can someone show how to achieve this?
I think there is a little bit of confusion on what should and shouldn't be mocked.
If I read your question correctly, you are actually trying to Unit Test your RegisterMemberController. Therefore, you most likely should NOT make a mock of that class, but actually test that class.
I believe that you would be creating fakes/dummies/stubs/mocks/spies of your MemberService, RegisterMemberBean, and BindingResult classes.
It would be these classes that would be created by your unit test and handed to your controller during the test that will force the testing of the logic that you are interested in proving/disproving.
FYI, when verifying that the MemberService class was called, that is where you would use a mock. The rest of the classes could either be dummies or stubs.
Side Note: I would recommend removing the Model parameter from your handleSubmit() method since it doesn't seem to be used anywhere.
In my integration test, I tried to use resttemplate to send a Get request to a dummy server created by MockMvcBuilders. However I got an error:
I/O error on GET request for "http://localhost:8080/test":Connection refused:
(In the function testAccess(), url is "http://localhost:8080/test"). My code is as below:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#IntegrationTest("server.port=8080")
public class MyTest {
private MockMvc mockMvc = null;
#Autowired
private WebApplicationContext context;
#Value("${server.port}")
private int port;
#Autowired
private MyController myController;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.build();
}
#Test
public void testAccess() throws Exception{
RestTemplate restTemplate=new RestTemplate();
String url="http://localhost:8080/test";
try{
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
}
catch(ResourceAccessException ex){
System.out.println(ex.getMessage());
}
}
#Controller
public static class MyController {
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody String access() {
return "OK";
}
}
}
The way I've done it is this:
First, you create a mock server from the actual RestTemplate you are using in your application
#Before
public void setup() throws Exception {
mockServer = MockRestServiceServer.createServer(myService.restTemplate);
}
Then you define how that request is going to work:
mockServer.expect(requestTo("http://localhost:8080/myrestapi"))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("{ success: true }", MediaType.APPLICATION_JSON));
And last you call the method in your application that will trigger a call to that url with that RestTemplate:
#Test
public void testThis() throws Exception {
myService.somethingThatCallsMyRestApi(parameters);
}
That will make your tests work as if there was a server up and running to process requests.
Using this in your example makes no sense, cause you would be testing that you build your test correctly and nothing else from the actual application.
The problem with this is that you cannot test dynamic responses. I mean, in my case the method I'm calling generates different data every time you call it and then sends it to the mockServer and then validates that the response matches in some very specific way. I haven't found a solution yet, but if the data you are going to send and receive is previously known, which would be in most cases, you'll have no problem using this.
Why are you defining a controller in your Test class and then trying to test it ? It doesn't feel logical to try to test something that is defined within the test it self.
Rather you would want to test a controller defined somewhere outside your tests, an actual controller that is used within your application.
Let's say MyController is defined as an actual controller then you could use the mockMvc object you created to test it.
mockMvc.perform(get('/test'))
.andExpect(status().isOk())