Only first test in class failed when running multiple classes - java

I have a really strange problem when running multiple tests in intellij. My first test in class failed because apparently jwt was not valid, but then in the second test in the same class everything is working fine with the same jwt.
If I try to run every class separately everything works fine, also if I run mvn test from the terminal, all tests will pass.
This is my configuration for the test in intellij
I also tried to set Fork mode to class, then test passed, but every test run separately, so it takes a while, and also I can't use coverage in Fork mode.
edit:
This is my code for init and first test
#BeforeAll
void init() throws Exception {
token = login(new LoginRequest("user", "123"));
}
private String login(LoginRequest loginRequest) throws Exception {
ResultActions resultActions = mockMvc.perform(post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginRequest)))
.andExpect(status().isOk());
return resultActions.andReturn().getResponse().getHeader(JwtUtilities.HEADER);
}
#Test
#Order(1)
void findAllTeamsByCreator_ok() throws Exception {
mockMvc.perform(get(PATH)
.contentType(MediaType.APPLICATION_JSON)
.header(JwtUtilities.HEADER, token))
.andExpect(status().isOk())
.andExpect(content().contentType("application/hal+json"))
.andExpect(jsonPath("$._embedded.teamDtoList").isNotEmpty());
}
so, I'm not setting any state in my test, and if I create test like this
#Test
#Order(1)
void test() {}
and then everything else remains the same, my tests will work. It's like something is not loaded in first test.
And it's really strange to me why mvn test works fine, but in Intellij I have bug

This sounds like something is "state full" in your test cases, so something in the first test is initializing something that is needed in the remaining test cases.
As you are using JUnits, you might make sure all the initialization is done in one of the "Before"-type methods that the framework supports.
It would be helpful to see the code of your first test case to determine what has "state".

Related

Is it possible to use multiple MockMvc requests in a single test method?

I am studying an old MOOC web development course (as this is an old one, I don't get any credits from it but I do it anyway to learn something new so I don't think it is "cheating"). I have tried to google info about this but so far no luck.
I am using Spring framework and I have to write some tests using MockMvc. This has worked pretty well, but one test has to have one post request to create a new item in a database and one get request to make sure that the new item can be found from the database.
Post request seems to work just fine, I have tested it with another test method and at least it doesn't give an error when I run it with this new test method. I also don't get an error just by making the get request. But when I run the requests one after the other, I get an error:
Tests run: 3, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 19.304 s <<< FAILURE! - in airports.AircraftControllerTest
testRedirect(airports.AircraftControllerTest) Time elapsed: 1.678 s <<< ERROR!
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "aircraft.airport" (template: "aircrafts" - line 26, col 22)
at airports.AircraftControllerTest.testRedirect(AircraftControllerTest.java:87)
My test class is (I have omitted couple of tests that work as far as I know):
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class AircraftControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private AircraftRepository aircraftRepository;
public AircraftControllerTest() {
}
#After
public void tearDown() {
this.aircraftRepository.deleteAll();
}
#Test
public void testRedirect() throws Exception {
mockMvc.perform(post("/aircrafts").param("name", "XP-55"))
.andExpect(status().is3xxRedirection());
MvcResult result = mockMvc.perform(get("/aircrafts")).andReturn();
}
}
So, in the testRedirect() test method I make the post request and after that I make the get request but now I am not at all sure that MockMvc works that way. All examples I have seen make only one request.
As the last point, I have to add more functionality after the get request to find out if that XP-55 can be found from aircraftRepository but I should be able to that.
Any and all help will be useful. Thanks in advance.

How to unit test a SpringBoot controller secured by keycloak?

I know that there are already similar questions, here and here, regarding this problem but every solution proposed failed to help me. There is also mention to this library in most of those answers but (with all due respect) I would like to avoid depending on an external library just to be able to test a simple controller.
So, I have a very simple api that is accessed using a bearer token generated by keycloak and I would like to test the controller. Something along these lines:
#Test
#DisplayName("Should be ok")
#WithMockUser
void whenCalled_shouldBeOk() throws Exception {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
mockMvc.perform(
post("/api/url/something")
.content("{}")
.contentType(APPLICATION_JSON)
.with(authentication(authentication))
).andExpect(status().isOk());
}
The problem is that I will always get a null pointer exception thrown by the KeycloakDeploymentBuilder for it's missing the adapter config. In our SecurityConfig we extend the KeycloakWebSecurityConfigurerAdapter and do all the required configurations for the app to work but I am failing to mock/by-pass this process in the test. Normally I find my way around this authentication problems in the tests (when keycloak isn't used) with #WithMockUser annotation but not this time.
Isn't there way to mock the adapter or the filter process in order to by-pass this issue?
I have tried everything that was answered in the other questions (except the library) with no luck. If you have any clue that could be of help or at least point me in the correct direction (since this can be due to a lack of knowledge on spring security from my part) that would very much appreciated.
2023 update
The deprecated Keycloak adapters for Spring (where KeycloakAuthenticationToken is defined) are not compatible with spring-boot 3. Alternatives in the accepted answer to "Use Keycloak Spring Adapter with Spring Boot 3"
Original answer
As I already wrote in my answer to the first question you linked, #WithMockUser populates the security context with a UsernamePaswordAuthenticationToken when your code / conf probably expect a KeycloakAuthenticationToken.
If you read carefully the same answer, you'll find an alternative to using my lib to do this: manually populate the security-context with a KeycloakAuthenticationToken instance or mock in each test.
Minimal sample with Mockito I added to my repo:
#Test
public void test() {
final var principal = mock(Principal.class);
when(principal.getName()).thenReturn("user");
final var account = mock(OidcKeycloakAccount.class);
when(account.getRoles()).thenReturn(Set.of("offline_access", "uma_authorization"));
when(account.getPrincipal()).thenReturn(principal);
final var authentication = mock(KeycloakAuthenticationToken.class);
when(authentication.getAccount()).thenReturn(account);
// post(...).with(authentication(authentication))
// limits to testing secured #Controller with MockMvc
// I prefer to set security context directly instead:
SecurityContextHolder.getContext().setAuthentication(authentication);
//TODO: invoque mockmvc to test #Controller or test any other type of #Component as usual
}
Maybe, after you mesure how much this clutters your tests (there are very few claims set here) you'll reconsider using my lib (or copying from it as it's opensource).
With my annotation, above sample becomes:
#Test
#WithMockKeycloakAuth
public void test() throws Exception {
//TODO: invoque mockmvc to test #Controller or test any other type of #Component as usual
}
Regarding spring test config with Keycloak involved, you could dig a bit into the tests of spring-addons-keycloak module. You'd find a complete app using KeycloakAuthenticationToken with unit tests (and working test conf).
Last (and maybe off-topic) you could read the repo main README and consider using a more generic OIDC implementation than Keycloak's one. I provide one, along with test annotation, and wrote tutorials on how to extend it to your app specific needs.

Is MockMvc eligible for WebFlux controllers testing?

I have a simple WebFlux application (that uses controllers, not router functions). The only non-standard part is that it uses Server-Sent-Events.
An interesting part of the controller is
#GetMapping(path = "/persons", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<Object>> persons() {
return service.persons()
.map(this::personToSse)
.onErrorResume(e -> Mono.just(throwableToSse(e)));
}
private ServerSentEvent<Object> personToSse(Person person) {
return ServerSentEvent.builder().data(person).build();
}
Service:
public interface Service {
Flux<Person> persons();
}
I have two tests:
#SpringBootTest(classes = Config.class)
#AutoConfigureMockMvc
class PersonsControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private Service service;
#Test
void streamsPersons() throws Exception {
when(service.persons())
.thenReturn(Flux.just(new Person("John", "Smith"), new Person("Jane", "Doe")));
String responseText = mockMvc.perform(get("/persons").accept(MediaType.TEXT_EVENT_STREAM))
.andExpect(status().is2xxSuccessful())
.andExpect(content().string(not(isEmptyString())))
.andReturn()
.getResponse()
.getContentAsString();
assertThatJohnAndJaneAreReturned(responseText);
}
#Test
void handlesExceptionDuringStreaming() throws Exception {
when(service.persons())
.thenReturn(Flux.error(new RuntimeException("Oops!")));
String responseText = mockMvc.perform(get("/persons").accept(MediaType.TEXT_EVENT_STREAM))
.andExpect(status().is2xxSuccessful())
.andReturn()
.getResponse()
.getContentAsString();
assertThat(responseText, is("event:internal-error\ndata:Oops!\n\n"));
}
First test checks that for the 'sunny day scenario' we get two persons that we expect. Second test checks what happens when an exception occurs.
The tests work perfectly when I run them one by one. But when I run them both, sometimes they pass, sometimes one of them fail, sometimes both fail. Failure reasons are different:
Sometimes Jackson complains during JSON parsing that an EOF was reached ('No content to map due to end-of-input', although in the log I can see a valid full JSON)
Sometimes first test fails and second passes as if in both cases an error was returned, even though I can see in the logs that for the first test normal response was generated, not the erroneous one
Sometimes second test fails and first passes as if in both cases valid JSONs where returned
So it looks like there is some concurrency problem. But my test code is simple enough, it does not use any concurrency-related concepts.
The following test fails 100% of times on my machine (it just runs these 2 tests repeatedly 1000 times):
#Test
void manyTimes() throws Exception {
for (int i = 0; i < 1000; i++) {
streamsPersons();
handlesExceptionDuringStreaming();
}
}
The questions follow:
Can MockMvc be used to test reactive controllers at all?
If it can, do I do anything incorrectly?
Here is the full project source code: https://github.com/rpuch/sse-webflux-tests
The manyTests() method is commented out and has to be re-enabled to be used.
1. Can MockMvc be used to test reactive controllers at all?
The answer is no, MockMvc is a blocking mockClient that will call your method once and return. It does not have the ability to read items consecutively as they get emitted The client you need to use is the Spring WebClient.
You can read more here how to go about testing infinite streams using the Spring WebTestClient.
2. If it can, do I do anything incorrectly?
See the answer to question one.

What are the pros & cons of 2 types of spring testing

In my current project we are working on microservices(web app).
In unit tests we try to cover 85-90% of our code. I have noticed 2 approaches of testing using spring:
Inject a controller and invoke its methods directly
Form a proper request where you can specify cookies, headers... and then make a call
Moreover, we won't be able to test authentication with the 1 approach.
Which of the next spring testing ways should be used? And what are the (dis-)advantages of each type?
#RestController
class MyController {
#PostMapping(path="/path")
public String handle(#RequestBody MyRequest request) {
//service invoked
return "some value";
}
}
JUnit approach #1
#LotsOfAnnotations
class ControllerTest1 {
#Autowired
private MyController myController;
#Test
public String verboseNameTest() {
// Mock 3rd party calls
....
// Form request
MyRequest request = new MyRequest();
// Invoke testing method
myController.handle(request);
// Assert
}
}
JUnit approach #2
#LotsOfAnnotations
class ControllerTest2 {
#Autowired
private TestRestTemplate testTemplate;
private MockRestServiceServer server;
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webAppContext;
#Before
public void setup() {
server = MockRestServiceServer.createServer(testTemplate.getRestTemplate());
mockMvc = MockMvcBuilders.webAppContextSetup(this.webAppContext).build();
}
#Test
public String verboseNameTest() {
// Mock 3rd party calls
....
// Form request
String jsonStringRequest = "{}";
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/path")
.contentType(MediaType.APPLICATION_JSON)
.content(jsonStringRequest);
// Make a call
MvcResult result = this.mockMvc
.perform(requestBuilder)
.andExpect(status().isOk())
.andReturn();
// Assert
}
}
As far as I can see you need to understand when to mock the service and when to make a direct call.
In your first approach you are directly calling the web service so let's say for example you have a huge code base and multiple developers are working at a time so you would get constant updates in the classes and if you want to test your piece of code(which is why you use JUnit) then you making direct call can result in error/ failed test case because someone might have changed something.
In second approach or mocking basically removes that possibility by mocking the other service which your code would be needing so that will make you code test easier.
But there are people who questions what's the use of mocking when you are not even testing the whole thing which is true up to certain point but the main reason for mocking is to test your piece of code only irrespective of other services that code is dependent on. Also if you talk about making actual service call that should be part of integration testing or end to end testing.

writing junit test for specific scenario

I am trying to write junit for my java web application which uses older command line design pattern (no framework is used for this older application).
I have a scenario in my application.
When the application is deployed in a server, first the server will find the web.xml and load and run the TestDataServlet(servlet class configured in web.xml which extends http servlet) before everything gets deployed in the server.
This TestDataServlet in turn calls the TestRunnable class which is a thread which loads all the properties file (contans informaton about endpoint everything which is used in java classes of my application code) and intialize it before hitting the application in browser.
I am trying to write junit for this application by using one time step up which loads all the properties file before running my junit, so that I can test it easily.
Since my application is not using a framework, I was not able to do it as spring junit does it.
Since there anyway to do it? Could I able to run the TestDataServlet before running my junit class?
Any help is appreciated.
Modify your TestDataServlet to be able to process a request "isTestRunnableCompleted". Have the return be true or false.
In your JUnit test, implement the setup() method. Call the TestDataServlet to start the runnable. Then, in a while() loop inside the setup() method, every second, call the TestDataServlet to check "isTestRunnableCompleted". If it is "false", sleep for a second, and then allow the loop to make the call again. You may want to implement some sort of a timeout in the loop also just in case things go wrong.
Good luck.
public class TestDataServlet
{
public void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException
{
// Look for some indicator in the request that the caller wants a "status"
// of the test data being set up
if ( request.getParameter( "isTestDataRunnableCompleted" ) != null )
{
boolean status = testDataRunnableThread.isAlive();
PrintWriter writer = response.getWriter();
writer.println( Boolean.toString( status ) );
writer.close();
}
else
{
// If we get here, then start the test data runnable thread
}
}
}

Categories