Search code examples
junit5spring-boot-test

JUnit5 SpringBootTest NullPointerException when testing Service


I am following a tutorial for unit testing a Service, and I am attempting to use JUnit5 instead of JUnit4 (which is what the tutorial uses). The test works fine when I follow the tutorial exactly with JUnit4, but I get a NullPointerException with JUnit5.

The repository:

public interface CourseRepo extends CrudRepository<Course, Long> {}

The service:

@Service
public class CourseService {

    private final CourseRepo courseRepo;

    public CourseService(CourseRepo courseRepo) {
        this.courseRepo = courseRepo;
    }

    public Course save(Course course) {
        Course newCourse = courseRepo.save(course);
        return newCourse;
    }

}

The test:

package com.elovell.blackboard.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import com.elovell.blackboard.domain.Course;
import com.elovell.blackboard.repository.CourseRepo;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

@ExtendWith(MockitoExtension.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class CourseServiceTest {

    @Mock
    public CourseRepo courseRepo;

    @InjectMocks
    public CourseService courseService;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testSaveCourse() {

        Course mockCourse = new Course();
        mockCourse.setPk1(1L);
        mockCourse.setCourseId("ENGL101");

        when(courseRepo.save(any(Course.class))).thenReturn(mockCourse);
        // save(null) should still return mockCourse
        Course newCourse = courseService.save(null);
        assertEquals("ENGL101", newCourse.getCourseId());
    }
}

Here is the first few lines of the Exception:

java.lang.NullPointerException
    at com.elovell.blackboard.service.CourseServiceTest.testSaveCourse(CourseServiceTest.java:44)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

The test and dependencies portion of my build file:

test {
    useJUnitPlatform {
        includeEngines 'junit-jupiter'
    }
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    runtimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.1")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.1.0")
    testCompile 'org.mockito:mockito-junit-jupiter:2.23.4'
}

Solution

  • This looks like you trying to mix an integration test with unit tests.

    When you use the @SpringBootTest annotation then SpringExtension for Junit5 starts an application context before running your test, and to make mocks in tests you need to use @MockBean annotation instead of @Mock & @InjectMocks. This is a different scope of tests.

    As I see, you need just a unit test and you can remove the SpringBootTest annotation.

    By the way, I think you got NPE because of you any(Course.class) in when(courseRepo.... Try to use isNull() or any() (without specification concrete class).