Multiple Tests with MockRestServiceServer - spring-boot

My Tests run through when executing them separatly. When I execute the Test Class, then one of them fails:
java.lang.AssertionError: Further request(s) expected leaving 1 unsatisfied expectation(s).
0 request(s) executed.
Test-Class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles("test")
#Transactional
public class ProductListControllerIT {
#Autowired RestTemplate restTemplate;
#Autowired MockMvc mvc;
#Test
public void testGet_1() throws Exception {
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
mockServer.expect(ExpectedCount.once(),
requestTo(/* any url */))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(/* JSON-STRING */)
);
var model = mvc.perform(MockMvcRequestBuilders.get("/url")
.andReturn().getModelAndView().getModel();
mockServer.verify();
}
#Test
public void testGet_2() throws Exception {
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
mockServer.expect(ExpectedCount.once(),
requestTo(/* any url */))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(/* JSON-STRING */)
);
var model = mvc.perform(MockMvcRequestBuilders.get("/url")
.andReturn().getModelAndView().getModel();
mockServer.verify();
}
}
One test pass, the other fails with error message as described above.
Thanks for hints.

I apologize. I run in a cache pitfall. The first test activated the cache for the rest call, the second rest call in the second tets has not been executed.
I clear all caches now after tests:
#After
public void after() {
mockServer.verify();
cacheManager.getCacheNames().forEach(n -> cacheManager.getCache(n).clear());
}

Related

Unit tests for testing methods returning CompletableFuture always returns NullPointerException

I have a SpringBoot Component and few methods. It returns CompletableFuture and I want to cover those lines with Unit Tests. But it always ends up in NullPointer Exception.
#Component
Class AsyncClass {
// My implementation of RESTClient
#Autowired
RestClient httpClient;
#Async
public CompletableFuture<TestClass> getDetails() {
// Retrieves the headers
HttpHeaders headers = getHttpHeaders();
HttpEntity<String> request = new HttpEntity<>(headers);
InitiativeResponse initiativeResponse = httpClient.executeRequest(API_URL, HttpMethod.GET, request, Object.class).getBody();
return CompletableFuture.completedFuture(TestClass);
}
private HttpHeaders getHttpHeaders(String userId) {
HttpHeaders headers = new HttpHeaders();
headers.add(CONSUMER_ID, consumerId);
headers.add(USER_ID, userId);
return headers;
}
}
This works perfectly fine and I autowire this into my service class and get the results. The problem arises when I write unit test cases for this class.
Class TestAsyncClass {
#InjectMocks
AsyncClass asyncClass;
#BeforeEach
void setUp() {
openMocks(this);
}
#Test
void testGetDetails() {
asyncClass.getDetails();
}
}
The above unit test fails with NullPointer Exception. Can you please help me on what am I missing?
The stacktrace simple shows
ava.lang.NullPointerException
at com.example.services.helper.TestAsyncClass.testGetDetails(TestAsyncClass.java:11)
I am not very sure but do you have #RunWith(MockitoJUnitRunner.class) or MockitoAnnotations.initMocks(this) in your TestAsyncClass ? I have the gut feelings that the mocks are not initiated....

SpringBoot unit test controller mvc return always 404

I'm trying to run a simple unit test for my controllers but for all requests I try MockMvc returns me a 404 error.
Here is a sample of controller:
#RestController
#RequestMapping("/airports")
public class AirportController {
private final AirportRepository repository;
...
#GetMapping(value = "/no-page", produces = "application/json; charset=utf-8")
public List<Airport> noPage() {
try {
return repository.findAllByActive(true);
} catch (Exception e) {
throw new ResponseStatusException(
HttpStatus.INTERNAL_SERVER_ERROR, "Failed to retrieve from DB!", e);
}
}
}
And a test:
#ActiveProfiles("test")
#SpringBootTest
#AutoConfigureMockMvc
public class AirportControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testAirportController() throws Exception {
this.mockMvc.perform(get("/api/airports/no-page"))
.andDo(print())
.andExpect(status().isOk());
}
}
(The request is on /api because it's my servlet context)
The status is never 200 (what it should be), but always 404 not found.
I also try to create a test configuration like this and import it to my test:
#Configuration
#EnableWebMvc
#ComponentScan("com.project")
public class TestConfiguration implements WebMvcConfigurer {
}
But it changes nothing.
What is wrong with my test ?
Your AirportController is mapped to /airports. Therefore your test should also use that path as follows:
this.mockMvc.perform(get("/airports/no-page"))
.andDo(print())
.andExpect(status().isOk());
Please note that MvcMock test runs independent of the configured servlet context path.

TooManyActualInvocations exception when creating #Nested tests in #WebMvcTest

I am trying to group my tests in a #WebMvcTest using #Nested, but unfortunately, a few of my tests stated failing with TooManyActualInvocations exception.
Here is minimal example I arrived at:
The test:
#WebMvcTest(value = AController.class)
public class AControllerMvcTest {
#MockBean
BService bService;
#Autowired
MockMvc mockMvc;
#Nested
class NestedTests1 {
#Test
void testOne() throws Exception {
System.out.println("Test One started");
Mockito.when(bService.getDummyString()).thenReturn("dummyResp1");
mockMvc.perform(MockMvcRequestBuilders.post("/underTest"))
.andExpect(MockMvcResultMatchers.status().isOk());
Mockito.verify(bService).getDummyString();
System.out.println("Test One finished");
}
}
#Nested
class NestedTests2 {
#Test
void testTwo() throws Exception {
System.out.println("Test Two started");
System.out.println(bService.hashCode());
Mockito.when(bService.getDummyString()).thenReturn("dummyResp2");
mockMvc.perform(MockMvcRequestBuilders.post("/underTest"))
.andExpect(MockMvcResultMatchers.status().isOk());
Mockito.verify(bService).getDummyString();
System.out.println("Test Two finished");
}
}
}
AController:
#Controller
class AController {
final BService bService;
#Autowired
AController(BService bService) {
this.bService = bService;
}
#PostMapping("/underTest")
String methodUnderTest() {
return bService.getDummyString();
}
}
BService:
#Service
class BService {
String getDummyString() {
return "ABC";
}
}
The exception I receive:
org.mockito.exceptions.verification.TooManyActualInvocations:
mypackage.controller.BService#0 bean.getDummyString();
Wanted 1 time:
-> at mypackage.controller.AControllerMvcTest$NestedTests1.testOne(AControllerMvcTest.java:45)
But was 2 times:
-> at mypackage.controller.AController.methodUnderTest(AController.java:19)
-> at mypackage.controller.AController.methodUnderTest(AController.java:19)
The same test passes if there are no nested tests.
In both cases (nested tests and no nested tests)
the tests are executed sequentially (println to the console)
bService is the same instance in both tests (checked with debugger)
My Questions
why adding #Nested caused my test to fail
how to fix it?
The Spring testing support does not work with nested tests unless you repeat all spring-related annotations in all nested test classes. I recommend to forgo nesting in this case.
It is an open bug in Spring Boot: #MockBean fields are not reset for JUnit 5 #Nested tests #12470.
It is blocked by another issue in Spring Framework: Discover test configuration on enclosing class for nested test class [SPR-15366] #19930
The latter is scheduled for 5.3 M2, so hopefully this won't be an issue soon.

Async RestController endpoints :: Async result for handler was not set during the specified timeToWait=0

We have endpoints of the form:
#GetMapping("/myurl")
public Callable<String> getARandomString(#RequestParam int a) {
The application is configured with just one thread (that's why we use Callables to handle this). However, in order to create the tests for this, we do the setup as follows:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApplication.class, MyController.class})
#ActiveProfiles("test")
public class MyControllerTest {
#MockBean
private MyService myService;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.build();
when(myServices.isOk()).thenReturn(true);
}
#Test
public void given_when_then() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/myurl")
.param("a", 1))
.andExpect(request().asyncStarted())
.andReturn();
mvcResult.getAsyncResult();
mockMvc
.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk());
}
1 test at the time works perfect. On the contrary, when having more than 1 test an exception as the following is thrown:
java.lang.IllegalStateException: Async result for handler [public java.util.concurrent.Callable<String> com.antmordel.MyController. getARandomString(int)] was not set during the specified timeToWait=0
I reckon that it is something about the timeouts. Have anybody had this issue?
I had the exactly same problem. 01 test = OK, multiple tests in 01 run = FAIL.
Fixed by telling the TestRunner to consider that the current test makes the context "dirty".
Add
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) to your test class
See this question Reload Spring application context after every test

Call original method on a #Spy then throw an exception

I have a #Transactional method that I must test when the transaction fails after it is called, for example :
#Service
public class MyService {
#Transactional
public void myMethod() {
// [...] some code I must run in my test, and throw an exception after it has been called but before the transaction is commited in order for the transaction to be rolled back
}
}
Here is the test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class)
public class MyServiceTest {
#SpyBean
private MyService myService;
#Test
public void testMyMethod() {
doAnswer(/* some code which would call the real method, then throw an exception in order to cancel the transaction */)
.when(myService).myMethod();
// [...] other code that test other services when that service failed in the transaction but after its real method has been correctly executed
}
}
Could you tell me what code to put in the /* ... */ part in my test?
You can simply use the invocation.callRealMethod() then throw some exception inside the doAnswer:
#Test
public void testMyMethod() {
doAnswer(invocation -> {
invocation.callRealMethod();
throw new IllegalStateException();
})
.when(myService).myMethod();
// [...] other code that test other services when that service failed in the transaction but after its real method has been correctly executed
}

Resources