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

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

Related

Spring MockMVC Test weird behaviour. single vs "all" execution

I am currently experiencing a weird issue regarding Spring's MockMvc in combination with Spring Security in JUnit Tests.
When I run a whole class of tests, everything works fine and the tests are passing.
But when I run All Tests in the Project one test is always failing in that certain class and it doesn't matter if I remove the test method failing, then another one fails in the same class.
#SpringBootTest
#ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
class AuthenticationApiTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
private static ObjectMapper objectMapper;
#BeforeAll
static void beforeAll() {
objectMapper = new ObjectMapper();
}
#BeforeEach
void setUp(RestDocumentationContextProvider documentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
.apply(documentationConfiguration(documentation)
.operationPreprocessors()
.withRequestDefaults(prettyPrint(),removeHeaders("Content-Length","Host","Pragma","X-XSS-Protection","Expires","X-Frame-Options","X-Content-Type-Options","Cache-Control"))
.withResponseDefaults(prettyPrint(),removeHeaders("Content-Length","Host","Pragma","X-XSS-Protection","Expires","X-Frame-Options","X-Content-Type-Options","Cache-Control")))
.apply(springSecurity())
.build();
}
The test which fails:
#Test
public void testSignUpFail() throws Exception {
SignUpBody signUpBody = new SignUpBody();
signUpBody.setPassword("demo1234");
signUpBody.setFirstname("first");
signUpBody.setLastname("lastn");
this.mockMvc.perform(post("/auth/user/signup")
.header(HttpHeaders.CONTENT_TYPE,"application/json")
.content(objectMapper.writeValueAsString(signUpBody))
).andExpect(status().is(400))
.andDo(document("user-signup-fail"));
this.mockMvc.perform(post("/auth/vendor/signup")
.header(HttpHeaders.CONTENT_TYPE,"application/json")
.content(objectMapper.writeValueAsString(signUpBody))
).andExpect(status().is(400))
.andDo(document("vendor-signup-fail"));
}
Any suggestions on how to solve this weird issue?

CrudRepository test cases without inserting data in DB

I have one repository class which which implements CrudRepository. Then in service class I have auto wired this repositary. Then in controller class I have autowired this service.
I want to write test cases of controller Class. I am using below configuration.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class XYZControllerTest {
MockMvc mockMvc;
#Mock
private XYZController xyzController;
#Autowired
private TestRestTemplate template;
#Autowired
XYZRepository xyzRepository;
#Before
public void setup() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(xyzController).build();
}
#Test
public void testPanelShouldBeRegistered() throws Exception {
HttpEntity<Object> xyz = getHttpEntity("{\"name\": \"test 1\", \"email\": \"test10000000000001#gmail.com\","
+ " \"registrationNumber\": \"41DCT\",\"registrationDate\":\"2018-08-08T12:12:12\" }");
ResponseEntity<XYZ> response = template.postForEntity("/api/xyz", xyz, XYZ.class);
}
}
My problem is that when I run test case, data is going to insert in DB which is used for application. Can I test it without inserting data in DB.
Conceptually when we are testing services we mock repositories instead of injection.
You need to mock your repository and setup behavior for returning data.
An example :
#MockBean
XYZRepository xyzRepository;
#Test
public void test() {
// other mocks
//
when(xyzRepository.findAll()).thenReturn(Arrays.asList(new XYZ()));
// service calls
// assertions
}

How to debug a Spring Boot application via SpringBootTest

I'm new to Spring Boot and I really like it especially when it comes to eliminate the boilerplate code.
I have created a test class to test my NBRController:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = NewBusinessRevitalizationApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource(properties = {"management.port=0"})
public class NBRControllerTest extends TestCase {
#LocalServerPort
private int port;
#Value("${local.management.port}")
private int mgt;
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void getApplicationByAgencyIdAndStatusTest() {
String uri = "http://localhost:" + this.port + "/nbr-services/applications/{status}?agencyIds=123456,56765,678576";
Map<String, String> vars = new HashMap<String, String>();
vars.put("status", "SAVED");
ResponseEntity<String> response = testRestTemplate.getForEntity(uri, String.class, vars);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
If I run it in debug mode I can only debug the Test class and not my NBRController class:
#RestController
#RequestMapping("/nbr-services")
public class NBRController {
#Autowired
private NBRServices nbrServices;
private static Logger logger = LoggerFactory.getLogger(NBRController.class);
#RequestMapping(value = "/configuration/environment/{environment}", method = RequestMethod.GET)
#ResponseBody
public String getConfiguration(#PathVariable("environment") String environment) throws RemoteException {
logger.debug("environment={}", environment);
String result = nbrServices.getConfiguration(environment);
return result;
}
}
I have tried to setup the Tomcat debug port but not luck.
The only way I can debug my NBRController is run it in debug mode and call my RestAPI from the browser, but I want to use my unit test. Thanks in advance!
I had this happening when I accidentally had 2 controller methods with the same path mapping.
Other alternatives for debugging:
Only mock the server using mockMVC
It is possible to debug a system by not using a split webEnvironment, but to use spring MockMVC to make direct method calls to controllers instead of http calls.
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.MOCK // this is the default
)
#AutoConfigureMockMvc
class MyTest {
#Autowired
private MockMvc mockMvc;
#Test public void myTest() {
mockMvc.perform("/mypath");
// ...
}
}
This will not actually make http calls between the jUnit class and the controller, so this http processing part would not be tested.
Start the server separately and attach a remote debugger
In the IDE, start the application can in debugging mode
When application it is up and running, start JUnit tests which contain any http client, e.g. RestAssured.
This will spawn 2 JVMs, but the IDE is connected to both, so all breakpoints work.
I am using Intellij 2020.3 and I could able to debug my controller.
Stop all the running instances from intellij.
2.Simply put debug pointers in right controller method and run your test case in debug mode.
Unless you are hitting wrong endpoint, it should work.
Also you can try to evaluate your testRestTemplate call in test case in debug mode, in case it is failing in network itself.
You're possibly running on a different port to the one you think you are.
The SpringBootTest annotation is what controls the test port e.g.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
In the Uri you are appending http://local host +port , which is not necessary , testRestTemplate does it for you. Remove that and try you may hit the debug point
Here is example to write Junit for Rest layer for Spring boot 2 + JUnit 5
#ExtendWith(MockitoExtension.class)
public class RestTest {
#InjectMocks
private Rest rest;
private MockMvc mockMvc;
#BeforeEach
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(rest).build();
}
#Test
public void getTest() throws Exception {
String url = "/test";
ResultActions resultActions = mockMvc.perform(get(url));
resultActions.andExpect(status().isOk());
}
}
#RestController
#RequestMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
public class Rest {
#GetMapping
public #ResponseBody String get() {
return "success";
}
}
Personally, i use maven-spring-boot plugin and when i debug, its from a Maven run config. Maybe that is what is wrong with what you are doing? The maven-spring-boot plugin, during test phase, will start spring boot server before the test runs.
If you want to do it with a command line app , then you have to manually load the Spring context and execute the main class from a couple lines of code, before your test runs. I don't remember off-hand how to do it.

Junit set system property for test not working

I have an issue that I would have thought I could resolve by now...
I'm writing a few simple tests to hit a couple services...
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class EndpointTests {
private static Logger log = Logger.getLogger(ApplicationController.class.getName());
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Mock
private ApplicationController applicationController = new ApplicationController();
static {
System.setProperty("audit.enabled", "false");
}
#BeforeClass
public static void setupProperties() {
System.setProperty("audit.enabled", "false");
}
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void contextLoads() {}
#Test
public void testGetApplications() throws Exception {
mockMvc.perform(get("/applications/")).andExpect(status().isOk())
.andDo(print())
.andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.[0].name",is("XYZ")));
}
Long story short, I need this property disabled when running tests. I've tried setting the property in a static initializer and in #BeforeClass as I've seen on other posts but when it goes into the actual method, it's still its default 'enabled' value and the tests fail. I'm not using XML configuration so would prefer a code/annotation solution.
Any suggestions on another way I can fix this? Thanks.
UPDATE
Seems like every time my integration test runs:
#Test
public void testGetApplications() throws Exception {
mockMvc.perform(get("/applications/")).andExpect(status().isOk())
.andDo(print())
.andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.[0].name",is("paasport-services")));
}
It executes my #Configuration classes on the call to mockMvc.perform...
#Configuration
public class AppConfiguration
...
...
So setting the property value in my test class does no good.
Is there any way to get in between and set this one property for my tests? I don't want to really create a separate test application context as it's just one property and everything has been working well for me up to this point.
Thanks.
I'm sure there's a much more elegant solution but I simply set a new system property in #BeforeClass on my test class.
My audit is handled by an aspect and I simply check that property I set only in my test class. If it's set to true, the advice doesn't execute.

Initialising MockMvc through Mockito

I'm having some trouble trying to get MockMvc to run with Mockito. This is what I have so far:
public class mockito {
MockMvc mockMvc;
#InjectMocks
EntryController controller;
#Mock
DAO data_access_object;
SmokeEvent evt = new SmokeEvent(18);
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = standaloneSetup(controller).setMessageConverters(new MappingJackson2HttpMessageConverter()).build();
//Exception thrown at above line (line 44) when the build method is called
}
#Test
public void viewRendersCorrectly() throws Exception
{
/**
* This test method is supposed to check to see if the controller renders
* correctly
*/
when(data_access_object.getEvent(any(Integer.class))).thenReturn(evt);
}
}
The following is the stack trace it throws in Testing mode. I should also state that I've been working through the Spring REST tutorial over at spring.io
java.lang.ExceptionInInitializerError: null
at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:1499)
at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1322)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:721)
at javax.servlet.GenericServlet.<clinit>(GenericServlet.java:95)
at org.springframework.test.web.servlet.MockMvcBuilderSupport.createMockMvc(MockMvcBuilderSupport.java:50)
at org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder.build(DefaultMockMvcBuilder.java:193)
at test.mockito.setup(mockito.java:44)
Any help is appreciated.
Thanks
Since you are running a standalone test, you need to create an instance of EntryController for standaloneSetup() method. As it is, you're passing standaloneSetup a null reference.
The other option is to run a semi-integrated test with MockMvcBuilders.webAppContextSetup and an appropriate WebApplicationContext.
for example:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes= {yourconfigurationClasses.class})
public class ... {
#Inject
protected WebApplicationContext webApplicationContext;
#Before
public void beforeSetUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

Resources