I'm using the Dropwizard framework and I'm trying to mock certain objects inside a web resource (the web resource tries to communicate with something external and I want to mock that).
Currently, I'm running the Dropwizard application in my tests through:
#PowerMockIgnore({"org.apache.http.conn.ssl.*", "javax.net.ssl.*", "javax.crypto.*", "javax.management.*", "javax.net.*"})
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ExternalCommunicator.class})
public class MockTest {
#ClassRule
public static final DropwizardAppRule<ApplicationWebserviceConfiguration> RULE = new DropwizardAppRule<>(ApplicationWebserviceApplication.class, ResourceHelpers.resourceFilePath("config.yml"));
#Before
public void doStuff() {
final ExternalCommunicator externalCommunicator = mock(ExternalCommunicator.class);
whenNew(ExternalCommunicator.class).withAnyArguments().thenReturn(externalCommunicator);
}
My resource looks something like:
#GET
#Path("/{id}")
#Timed
#UnitOfWork
#ApiOperation(value = "Talk to things", notes = "Talk to things.", response = String.class)
public String talk(#ApiParam(value = "id", required = true) #PathParam("id") Long id) {
ExternalCommunicator communicator = new ExternalCommunicator(id);
String result = communicator.get();
someDAO.create(result);
return result;
}
I'm using powermock and mockito but can't seem to correctly mock ExternalCommunicator
I've also tried
#Rule
public PowerMockRule rule = new PowerMockRule();
instead of #RunWith(PowerMockRunner.class) annotation but that doesn't seem to work either
Additionally, I do not want to mock the DAO.
Related
I implemented resilience4j in my project using the Spring Boot2 starter (https://resilience4j.readme.io/docs/getting-started-3).
I annotated a method with #CircuitBreaker that uses http client for calling an external service and the circuit breaker is working fine - including its fallback.
I'd like to add unit tests for it but when I run a test trying to simulate the fallback, nothing happens - the exception is thrown but is not handled by the circuit breaker mechanism.
I've found some examples using its metrics but it is not useful in my case.
Any thoughts?
Here is a snippet of my client:
#CircuitBreaker(name = "MY_CICUIT_BREAKER", fallbackMethod = "fallback")
public ResponseEntity<String> search(String value) {
ResponseEntity<String> responseEntity = restTemplate.exchange(
searchURL,
HttpMethod.GET,
new HttpEntity(new HttpHeaders()),
String.class,
value);
}
public ResponseEntity<String> fallback(String value, ResourceAccessException ex) {
return "fallback executed";
}
As andres and pvpkiran mentioned/explained, I had to add a integration test.
You can achieve that basically adding #SpringBootTest annotation to your test class, it will bootstrap a container with spring context on it.
I also autowired CircuitBreakerRegistry in order to reset the circuit breaker before each test so I could guarantee a clean test. For mocking/spying/verifying I used Mockito from spring boot test starter (spring-boot-starter-test).
Here is how I managed to test the fallbacks methods:
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = Application.class)
public class RestClientIntegrationTest {
private final String SEARCH_VALUE = "1234567890";
#MockBean( name = "myRealRestTemplateName")
private RestTemplate restTemplate;
#SpyBean
private MyRestClient client;
#Autowired
private CircuitBreakerRegistry circuitBreakerRegistry;
#BeforeEach
public void setUp() {
circuitBreakerRegistry.circuitBreaker("MY_CIRCUIT_BREAKER_NAME").reset();
}
#Test
public void should_search_and_fallback_when_ResourceAccessException_is_thrown() {
// prepare
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class), eq(SEARCH_VALUE)))
.thenThrow(ResourceAccessException.class);
String expectedResult = "expected result when fallback is called";
// action
String actualResult = client.search(SEARCH_VALUE);
// assertion
verify(client).fallback(eq(SEARCH_VALUE), any(ResourceAccessException.class));
assertThat(actualResult, is(expectedResult));
}
}
I hope there is no compilation error since I had to remove some non-relevant stuff.
You shouldn't test #CircuitBreaker in a unit test as it involves more than one class. Rather use an integration test.
I have tests with allure2 #Tmslink("1234") annotaion on each #Test. So, i need to get #TmsLink value and use it in my tests. I've got annotation value in extension, but how can i provide it to test?
public class TmsLink implements BeforeTestExecutionCallback {
private String tmsLink;
#Override
public void beforeTestExecution(ExtensionContext context) {
findAnnotation(context.getElement(), TmsLink.class).ifPresent(link -> this.tmsLink = link.value());
}
public String getTmsLink() {
return tmsLink;
}
}
#ExtendWith(TmsLink .class)
public abstract class Tests {
}
With Junit4 it's just:
#Rule
public TmsLink extension = new TmsLink();
extension.getTmsLink();
JUnit Jupiter also supports registering extensions programmatically by annotating fields in test classes with #RegisterExtension:
class WebServerDemo {
#RegisterExtension
static WebServerExtension server = WebServerExtension.builder()
.enableSecurity(false)
.build();
#Test
void getProductList() {
WebClient webClient = new WebClient();
String serverUrl = server.getServerUrl();
// Use WebClient to connect to web server using serverUrl and verify response
assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus());
}
}
For details please consult the User-Guide at https://junit.org/junit5/docs/current/user-guide/#extensions-registration-programmatic
Or let your extension also implement ParameterResolver and inject TmsLink as an argument to your test method parameter. Find details and an example linked at https://junit.org/junit5/docs/current/user-guide/#extensions-parameter-resolution
I want to test my controller class and its methods.
My Controller method looks like this:
#RequestMapping(value = "/updateUserStory/{usid}", method = RequestMethod.GET)
public String updateUserStory(#PathVariable("trrid") Integer trrID, #PathVariable("usid") Integer userstoryID, Model model ){
UserStory userStory = this.userStoryService.getUserStoryById(userstoryID);
model.addAttribute("userstory", userStory);
model.addAttribute("trrID", trrID);
return "updateUserStory";
}
My test method looks like this:
public void updateUserStory() throws Exception {
Model model = mockModel();
UserStory userStory = new UserStory();
userStory.setId(1);
EasyMock.expect(userStoryService.getUserStoryById(1)).andReturn(userStory);
EasyMock.replay(userStoryService);
String test = controller.updateUserStory(1, 1, model );
EasyMock.verify(userStoryService);
Assert.assertEquals("updateUserStory", test);
}
I added #Mock above for the userStoryService
#Mock
private UserStoryServiceImpl userStoryService;
and #TestSubject for the UserStoryController (In the test simply called controller).
#TestSubject
UserStoryController controller = new UserStoryController();
When running the test I keep getting A NullPointerException at the EasyMock.expect line. I don't know how this is failing. I am mocking the right method.
I see two possible reasons.
1. You haven't used any runner or rule.
To inject mocks, EasyMock needs a JUnit rule
#Rule
public EasyMockRule mocks = new EasyMockRule(this);
or a runner
#RunWith(EasyMockRunner.class)
public class MyTest {
2. The field type is UserStoryService
Not UserStoryServiceImpl. So you should mock UserStoryService instead.
I have developed a rest service using Jersey. Now I want to write some integration tests for this web service but since not every class being used from the web service is already implemented I need to mock some of them. For example I have the following class:
public class A {
private String getWeather() {
return null;
}
}
My web service looks like this :
#Path("/myresource")
public class MyResource {
#GET
#Produces("text/plain")
public String getIt() {
A a = new A();
return a.getWeather();
}
}
The problem is that the getWeather function is not ready so I need to mock the return value for this function. But for the integration test where I issue a rest call I don't know how to do that.
Are there any ideas?
To make your design decoupled from A you should pass it in as a parameter to MyResource. Then you can easily mock it by hand or with mockito. With constructor injection it would look like this:
#Path("/myresource")
public class MyResource {
private A a;
public MyResource(A a) {
this.a = a;
}
#GET
#Produces("text/plain")
public String getIt() {
return a.getWeather();
}
}
and you can test it with
#Test
public void shouldGetIt() {
A a = mock(A.class);
when(a.getWeather()).thenReturn("sunny!");
MyResource r = new MyResource(a);
assertThat(r.getIt(), is("sunny!));
}
This makes your design decoupled. MyResource no longer depends on A directly but on anything that looks lik an A. Another benefit is that mockito doesn't mess with you class files. It is your code that is tested - not something that has been generated on the fly.
Many consider injection by constructor to be a bit old school. I am old so I like it.... With spring (a framework I don't recommend that you pick up) you can autowire variables like so:
#Autowire
private A a;
and you don't need the constructor at all. Spring will find a the only implementation of A and insert it in this variable. I prefer explicit programming so I would chose constructor injection any day.
You may be able to achieve this using Power Mockito (https://code.google.com/p/powermock/wiki/MockitoUsage)
#RunWith(PowerMockRunner.class)
#PrepareForTest({ MyResource.class })
public class MyResourceTest {
#Test
public void testGetIt()() {
MyResource mr = new MyResource();
//Setup mock
A mockA = PowerMockito.mock(A.class);
String mockReturn = "Some String";
//Stub new A() with your mock
PowerMockito.whenNew(A.class).withAnyArguments().thenReturn(mockA);
PowerMockito.doReturn(mockReturn).when(mockA).getWeather();
String ret = mr.getIt();
//asserts go here
}
}
Note that you can mock a local variable creation using PowerMockito's whenNew - this should take care of your concern for A a = new A() code in getIt() method.
With Spring MVC, you can specify that a particular URL will handled by a particular method, and you can specify that particular parameters will map to particular arguments, like so:
#Controller
public class ImageController {
#RequestMapping("/getImage")
public String getImage( #RequestParam("imageId") int imageId, Map<String,Object> model ) {
model.put("image",ImageService.getImage(imageId));
}
}
This is all well and good, but now I want to test that an http request with an imageId parameter will invoke this method correctly. In other words, I want a test that will break if I remove or change any of the annotations. Is there a way to do this?
It is easy to test that getImage works correctly. I could just create an ImageController and invoke getImage with appropriate arguments. However, this is only one half of the test. The other half of the test must be whether getImage() will be invoked by the Spring framework when an appropriate HTTP request comes in. I feel like I also need a test for this part, especially as my #RequestMapping annotations become more complex and invoke complex parameter conditions.
Could you show me a test that will break if I remove line 4, #RequestMapping("getImage")?
You could use AnnotationMethodHandlerAdapter and its handle method programmatically. This will resolve the method for the given request and execute it. Unfortunately this is a little indirect. Actually there is a private class called ServletHandlerMethodResolver in AMHA that is responsible for just resolving the method for a given request. I just filed a request for improvement on that topic, as I really would like to see this possible, too.
In the meantime you could use e.g. EasyMock to create a mock of your controller class, expect the given method to be invoked and hand that mock to handle.
Controller:
#Controller
public class MyController {
#RequestMapping("/users")
public void foo(HttpServletResponse response) {
// your controller code
}
}
Test:
public class RequestMappingTest {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MyController controller;
private AnnotationMethodHandlerAdapter adapter;
#Before
public void setUp() {
controller = EasyMock.createNiceMock(MyController.class);
adapter = new AnnotationMethodHandlerAdapter();
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
#Test
public void testname() throws Exception {
request.setRequestURI("/users");
controller.foo(response);
EasyMock.expectLastCall().once();
EasyMock.replay(controller);
adapter.handle(request, response, controller);
EasyMock.verify(controller);
}
}
Regards,
Ollie
Ollie's solution covers testing the specific example of an annotation but what about the wider question of how to test all the other various Spring MVC annotations. My approach (that can be easily extended to other annotations) would be
import static org.springframework.test.web.ModelAndViewAssert.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({/* include live config here
e.g. "file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml" */})
public class MyControllerIntegrationTest {
#Inject
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
private MyController controller;
#Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
// I could get the controller from the context here
controller = new MyController();
}
#Test
public void testFoo() throws Exception {
request.setRequestURI("/users");
final ModelAndView mav = handlerAdapter.handle(request, response,
controller);
assertViewName(mav, null);
assertAndReturnModelAttributeOfType(mav, "image", Image.class);
}
}
I've also written a blog entry about integration testing Spring MVC annotations.