Issue
I don't know why, but my code is giving me a nullPointerException error when I try to use test any of my methods from the controller using MVC. Here I put some parts of code:
My controller(first method only):
@RestController
@RequestMapping(path = "/companias")
public class ControllerAPI {
@Autowired
private final CompaniaServiceImpl companiaService;
public ControllerAPI(CompaniaServiceImpl companiaService) {
this.companiaService = companiaService;
}
@ResponseStatus(HttpStatus.OK)
@GetMapping("/{companiaId}/{field_name}")
public Map<String, List<Object>> getByField(
@PathVariable("companiaId") Long companiaId,
@PathVariable("field_name") String fieldName) {
return companiaService.getCompaniaByfield(companiaId, fieldName);
}
Here is my Service (the one I'm trying to mock):
@Service
@Slf4j
public class CompaniaServiceImpl implements CompaniaService {
private final CompaniaRepository companiaRepository;
private final DefaultGroupRepository defaultGroupRepository;
private final OfficeRepository officeRepository;
@Autowired
public CompaniaServiceImpl(CompaniaRepository companiaRepository, DefaultGroupRepository defaultGroupRepository, OfficeRepository officeRepository) {
this.companiaRepository = companiaRepository;
this.defaultGroupRepository = defaultGroupRepository;
this.officeRepository = officeRepository;
}
public Map<String, List<Object>> getCompaniaByfield(Long companiaId, String fieldName) {
if (companiaRepository.existsById(companiaId)) {
Map<String, Function<Compania, Object>> mapCompania = Map.of(
"name", Compania::getName,
"dominio", Compania::getDominio,
"altas", Compania::getAltas,
"bajas", Compania::getBajas
);
Compania c = companiaRepository.findCompaniaById(companiaId);
Function<Compania, Object> retriever = mapCompania.get(fieldName);
return Collections.singletonMap("success", List.of(retriever.apply(c)));
}
else throw new IllegalStateException(
"Compania con id (" + companiaId + ") no existe"
);
}
Here is my controller Test:
@ExtendWith(SpringExtension.class)
@ContextConfiguration
class ControllerAPITest {
String token = "xxx";
@InjectMocks
private ControllerAPI controllerAPI;
private CompaniaServiceImpl companiaService = Mockito.mock(CompaniaServiceImpl.class);
private MockMvc mockMvc;
@Before
public void setup() throws Exception{
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controllerAPI).build();
}
@Test
void getByField() throws Exception {
Map<String, List<Object>> response = Collections.singletonMap("success", List.of("nombre1"));
Mockito.when(companiaService.getCompaniaByfield(1L,"name")).thenReturn(response);
this.mockMvc.perform(
get("/companias/{companiaId}/{field_name}", 1L, "name")
.header("authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.success[0]").isNotEmpty())
.andExpect(jsonPath("$.success[0]").value("nombre1"));
}
And the NullPointerException error that is giving me:
java.lang.NullPointerException
at database.configuration.controller.ControllerAPITest.getByField(ControllerAPITest.java:69)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
It gives me this error in the line that says this.mockMvc.perform( I don't know why I'm not being able to mock my service. If you could help me, I'd really appreciate it
Solution
The main issue in your test is that you are mixing JUnit 4 annotations and JUnit5 in a single test. The @Before
is from JUnit4 so the setup is never being run, hence no dependencies or mocks will be created. Hence a NullPointerException
as nothing is injected.
In your code you should use the CompaniaService
interface instead of the CompaniaServiceImpl
. You have defined an interface which you should use to your advantage.
Change your controller.
@RestController
@RequestMapping(path = "/companias")
public class ControllerAPI {
private final CompaniaService companiaService;
public ControllerAPI(CompaniaService companiaService) {
this.companiaService = companiaService;
}
You don't need the @Autowired
on the field as injection is done through the constructor!.
Now for your test you can do a couple of things.
Replace
@Before
with@BeforeEach
which is the JUnit5 annotation to mark the method. You don't need the@ExtendsWith
and@ContextConfiguration
.Instead of the
@ExtendsWith(SpringExtension.class)
use theMockitoExtension
, use@BeforeEach
but not to initialize the mocks.Use
@WebMvcTest
and@MockBean
and let Spring Boot handle the injection and mock creation for you.
Use proper annotations
class ControllerAPITest {
String token = "xxx";
@InjectMocks
private ControllerAPI controllerAPI;
@Mock
private CompaniaService companiaService;
private MockMvc mockMvc;
@BeforeEach
public void setup() throws Exception{
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controllerAPI).build();
}
@Test
void getByField() throws Exception { ... }
Use MockitoExtension
@ExtendsWith(MockitoExtension.class)
class ControllerAPITest {
String token = "xxx";
@InjectMocks
private ControllerAPI controllerAPI;
@Mock
private CompaniaService companiaService;
private MockMvc mockMvc;
@BeforeEach
public void setup() throws Exception{
this.mockMvc = MockMvcBuilders.standaloneSetup(controllerAPI).build();
}
@Test
void getByField() throws Exception { ... }
Use @WebMvcTest
@WebMvcTest(ControllerAPI.class)
class ControllerAPITest {
String token = "xxx";
@MockBean
private CompaniaService companiaService;
@Autowired
private MockMvc mockMvc;
@Test
void getByField() throws Exception { ... }
Answered By - M. Deinum