Search code examples
javaspring-bootjunitjunit5maven-surefire-plugin

Unable to run JUnit5 tests with Maven


I'm running into an issue where I can't run JUnit5 tests using Maven. Running them in the IDE works just fine but using "mvn test" produces the following output:

 T E S T S
[INFO] -------------------------------------------------------
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Here are my test classes :

package com.example.spstream.controllers.events;

import com.example.spstream.entities.Event;
import com.example.spstream.repositories.EventRepository;
import com.example.spstream.repositories.UserRepository;
import com.example.spstream.util.Mapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.dao.DataAccessException;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import java.time.LocalDateTime;


import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
public class EventsCreationTest {
    private static final String MISSING_TITLE_ERROR_MESSAGE = "title is missing";
    private static final String MISSING_ACTIVITY_ERROR_MESSAGE = "activity is missing";
    private static final String MISSING_LOCALISATION_ERROR_MESSAGE = "localisation is missing";
    private static final String INVALID_ORGANISER_ID_ERROR_MESSAGE = "user %s does not exist";
    private static final String MISSING_ORGANISER_ID_ERROR_MESSAGE = "organiser id is missing";
    @Autowired
    private MockMvc mockMvc;
    @MockBean
    private UserRepository userRepository;
    @SpyBean
    private EventRepository eventRepository;

    private static final String DATE_IN_PAST_ERROR_MESSAGE = "date is in the past";

    @BeforeEach
    public void reset(){
        Mockito.reset(userRepository);
        Mockito.when(userRepository.existsById("123456")).thenReturn(true);
    }

    //prevents hardcoded events from failing tests due to date in the past
    public void setEventDateToTomorrow(Event event) {
        event.setDateTime(LocalDateTime.now().plusDays(1));
    }

    public void setEventDateToYesterday(Event event) {
        event.setDateTime(LocalDateTime.now().minusDays(1));
    }

    public void testCorrectEventCreationWithEvent(Event event) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.post("/events")
                        .content(Mapper.writeObjectToJson(event))
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.id").exists())
                .andExpect(jsonPath("$.id").isString());
    }

    public void testIncorrectEventCreationWithEvent(Event event, String errorMessagePattern) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.post("/events")
                        .content(Mapper.writeObjectToJson(event))
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isBadRequest())
                .andExpect(content().string(containsString(String.format(errorMessagePattern, event.getOrganiserId()))));
    }

    /**
     * correct data
     **/
    @Test
    public void testMinimalCorrectEvent() throws Exception {
        Event minimalEvent = Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/correct/minimal_event.json"), Event.class);
        setEventDateToTomorrow(minimalEvent);
        testCorrectEventCreationWithEvent(minimalEvent);
    }

    @Test
    public void testMaximalCorrectEvent() throws Exception {
        Event maximalEvent = Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/correct/maximal_event.json"), Event.class);
        setEventDateToTomorrow(maximalEvent);
        testCorrectEventCreationWithEvent(maximalEvent);
    }

    /**
     * missing data
     **/
    @Test
    public void testIncorrectEventTitleMissing() throws Exception {
        Event eventTitleMissing = Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/correct/minimal_event.json"), Event.class);
        setEventDateToTomorrow(eventTitleMissing);
        eventTitleMissing.setTitle(null);
        testIncorrectEventCreationWithEvent(eventTitleMissing, MISSING_TITLE_ERROR_MESSAGE);
    }

    @Test
    public void testIncorrectEventActivityMissing() throws Exception {
        Event eventActivityMissing =  Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/correct/minimal_event.json"), Event.class);
        eventActivityMissing.setActivity(null);
        setEventDateToTomorrow(eventActivityMissing);
        testIncorrectEventCreationWithEvent(eventActivityMissing, MISSING_ACTIVITY_ERROR_MESSAGE);
    }

    @Test
    public void testIncorrectEventLocalisationMissing() throws Exception {
        Event eventLocalisationMissing =  Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/correct/minimal_event.json"), Event.class);
        eventLocalisationMissing.setLocalisation(null);
        setEventDateToTomorrow(eventLocalisationMissing);
        testIncorrectEventCreationWithEvent(eventLocalisationMissing, MISSING_LOCALISATION_ERROR_MESSAGE);
    }

    @Test
    public void testIncorrectEventMissingUserId() throws Exception {
        Event eventOrganiserIdMissing = Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/incorrect/missing_user_id.json"), Event.class);
        setEventDateToTomorrow(eventOrganiserIdMissing);
        testIncorrectEventCreationWithEvent(eventOrganiserIdMissing, MISSING_ORGANISER_ID_ERROR_MESSAGE);
    }

    /**
     * invalid data
     **/
    @Test
    public void testIncorrectEventInvalidOrganiserId() throws Exception {
        Mockito.when(userRepository.existsById(Mockito.any())).thenReturn(false);
        Event eventInvalidOrganiserId = Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/correct/minimal_event.json"), Event.class);
        setEventDateToTomorrow(eventInvalidOrganiserId);
        testIncorrectEventCreationWithEvent(eventInvalidOrganiserId, INVALID_ORGANISER_ID_ERROR_MESSAGE);
    }

    @Test
    public void testIncorrectEventDateInThePast() throws Exception {
        Event eventInPast = Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/correct/minimal_event.json"), Event.class);
        setEventDateToYesterday(eventInPast);
        testIncorrectEventCreationWithEvent(eventInPast, DATE_IN_PAST_ERROR_MESSAGE);
    }

    /**
     * internal database issue
     **/
    @Test
    public void testCorrectEventServerError() throws Exception {

        Event eventInvalidOrganiserId = Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/create/correct/minimal_event.json"), Event.class);
        setEventDateToTomorrow(eventInvalidOrganiserId);
        Mockito.when(eventRepository.save(eventInvalidOrganiserId)).thenThrow(Mockito.mock(DataAccessException.class));
        mockMvc.perform(MockMvcRequestBuilders.post("/events")
                        .content(Mapper.writeObjectToJson(eventInvalidOrganiserId))
                        .contentType(MediaType.APPLICATION_JSON)
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().is5xxServerError());
        System.out.println("whatever");
    }
}
package com.example.spstream.controllers.events;

import com.example.spstream.entities.Event;
import com.example.spstream.repositories.EventRepository;
import com.example.spstream.util.Mapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

import org.springframework.dao.DataAccessException;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;


import java.util.List;
import java.util.Optional;

import static com.example.spstream.util.Mapper.readJsonFromFile;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
public class EventsAccessTest {

    @MockBean
    EventRepository mockEventRepository;

    @Autowired
    MockMvc mockMvc;

    @BeforeEach
    public void reset(){
        Mockito.reset(mockEventRepository);
    }

    @Test
    public void testFindAll() throws Exception{
        List<Event> events = Mapper.readObjectListFromJson(readJsonFromFile("controllers/events/access/all_events.json"), Event.class);
        Mockito.when(mockEventRepository.findAll()).thenReturn(events);
        mockMvc.perform(MockMvcRequestBuilders.get("/events")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(content().json(Mapper.readJsonFromFile("controllers/events/access/all_events.json")));
    }

    @Test
    public void testFindEventWhichExists() throws Exception{
        Mockito.when(mockEventRepository.findById("123456")).thenReturn(Optional.of(Mapper.readObjectFromJson(Mapper.readJsonFromFile("controllers/events/access/final_event.json"),Event.class)));
        mockMvc.perform(MockMvcRequestBuilders.get("/events/123456")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(content().json(Mapper.readJsonFromFile("controllers/events/access/final_event.json")));
    }

     @Test
    public void testFindEventWhichDoesntExist() throws Exception {
         Mockito.when(mockEventRepository.findById("7891011")).thenReturn(Optional.empty());
        mockMvc.perform(MockMvcRequestBuilders.get("/events/7891011")
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isNotFound())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON));
    }

    @Test
    public void testFindEventDatabaseError() throws Exception {
        Mockito.when(mockEventRepository.findById("123456")).thenThrow(Mockito.mock(DataAccessException.class));
        mockMvc.perform(MockMvcRequestBuilders.get("/events/123456")
                        .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().is5xxServerError());

    }

}

The pom :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spstream</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spstream</name>
    <description>spstream</description>
    <properties>
        <java.version>17</java.version>
        <testcontainers.version>1.16.2</testcontainers.version>

    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
            <exclusion>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
            </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>postgresql</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.github.tomakehurst</groupId>
            <artifactId>wiremock-jre8</artifactId>
            <version>2.32.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.testcontainers</groupId>
                <artifactId>testcontainers-bom</artifactId>
                <version>${testcontainers.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
                <dependencies>
                    <dependency>
                        <groupId>org.apache.maven.surefire</groupId>
                        <artifactId>surefire-junit47</artifactId>
                        <version>3.0.0-M5</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

I have done some research and I figured it might have something to do mith mixing up JUnit4 and JUnit5 features which leads to maven surefire plugin not running tests. However I can't find where those leftover JUnit4 features might be. I'd appreciate any help.


Solution

  • As pointed out by other comments and answers I had residual JUnit4 dependencies due to test containers. I was able to fix the issue by explicitly setting JUnit5 as a dependency for maven surefire plugin like so :

    <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>3.0.0-M5</version>
                    <dependencies>
                        <dependency>
                            <groupId>org.junit.jupiter</groupId>
                            <artifactId>junit-jupiter</artifactId>
                            <version>5.8.2</version>
                        </dependency>
                    </dependencies>
                </plugin>