0

I have a very basic Spring Boot application.
It consists of a Controller a Service and a Repository.
I want to test the Controller which basically just calls the Service, which in turn calls the Repository.
I'm trying to mock the Service and inject it into the Controller for testing, but it fails at this row:

when(service.findEntry(1L)).thenReturn(model);

The error is NullPointerException and when I go through the stack trace it's because of a HashSet.forEach() call.
How can I fix this? In the row above the method service.findEntry(1L) is actually called and throws a NullPointerException The Error:

java.lang.NullPointerException
    at com.efl.journal.controller.JournalEntryControllerITests.testGetJournalEntryById(JournalEntryControllerITests.java:46)
    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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:44)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:74)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:80)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

My Test Class:

package com.efl.journal.controller;

import com.efl.journal.model.JournalEntryModel;
import com.efl.journal.service.JournalEntryService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.boot.test.mock.mockito.MockBean;

import javax.ws.rs.core.Response;


import java.util.HashSet;

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

@RunWith(MockitoJUnitRunner.class)
public class JournalEntryControllerITests {

    @MockBean
    JournalEntryService service;

    @InjectMocks
    JournalEntryController controller;

    @Test
    public void testGetJournalEntryById(){
        JournalEntryModel model = new JournalEntryModel();
        model.setEntryId(1L);
        model.setSubject("test 1");
        model.setBody("This is a test");
        model.setUserId(1L);
        model.setTags(new HashSet<>());

        when(service.findEntry(1L)).thenReturn(model);
        Response result = controller.getJournalEntry(1L);
        JournalEntryModel resultModel = (JournalEntryModel) result.getEntity();

        assertEquals(model.getEntryId(), resultModel.getEntryId());
    }
}

My Controller:

package com.efl.journal.controller;

import com.efl.journal.model.JournalEntryModel;
import com.efl.journal.service.JournalEntryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.ws.rs.core.Response;
import java.util.List;

@RestController
@RequestMapping("/")
@Slf4j
public class JournalEntryController {

    JournalEntryService entryService;

    @Autowired
    public JournalEntryController(JournalEntryService entryService){
        this.entryService = entryService;
    }


    @GetMapping("entry/{entryId}")
    public Response getJournalEntry(@PathVariable Long entryId){
        JournalEntryModel journalEntry =  entryService.findEntry(entryId);
        if( journalEntry != null){
            return Response.status(Response.Status.OK)
                    .entity(journalEntry)
                    .build();
        }

        return Response.status(Response.Status.NO_CONTENT)
                    .build();
    }

    @GetMapping("entry")
    public Response getAllJournalEntires(){
        List<JournalEntryModel> entryList = entryService.findAllEntryObjects();

        if(entryList.isEmpty()){
            return Response.status(Response.Status.NO_CONTENT).build();
        }
        return Response.status(Response.Status.OK).entity(entryList).build();
    }

    @PostMapping("entry")
    public Response postJournalEntry(@RequestBody JournalEntryModel entry) {
        entry = entryService.saveJournalEntry(entry);
        return Response.status(Response.Status.OK)
                .entity(entry)
                .build();
    }

    @DeleteMapping("entry/{entryId}")
    public Response deleteJournalEntry(@PathVariable Long entryId, @RequestBody JournalEntryModel entry ){
        entryService.deleteJournalEntry(entry);
        return Response.status(Response.Status.NO_CONTENT)
                .build();
    }
}

My Service class:

package com.efl.journal.service;

import com.efl.journal.entity.JournalEntry;
import com.efl.journal.model.JournalEntryModel;
import com.efl.journal.repository.JournalEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

@Service
public class JournalEntryService {

    private JournalEntryRepository journalEntryRepo;

    @Autowired
    public JournalEntryService(JournalEntryRepository journalEntryRepo){
        this.journalEntryRepo = journalEntryRepo;
    }

    public JournalEntryModel findEntry(Long id) {
        Optional<JournalEntry> optional = journalEntryRepo.findById(id);
        if(optional.isPresent()){
            return optional.get().toModel();
        }

        return null;

    }

    public List<JournalEntryModel> findAllEntryObjects(){
        Iterable<JournalEntry> iterable = journalEntryRepo.findAll();
        Iterator iterator = iterable.iterator();

        List<JournalEntryModel> list = new ArrayList<>();

        iterator.forEachRemaining(e -> {
               list.add(((JournalEntry) e).toModel());
        });

        return list;
    }

    public JournalEntryModel saveJournalEntry(JournalEntryModel entry){
        return journalEntryRepo.save(entry.toEntity()).toModel();
    }

    public void deleteJournalEntry(JournalEntryModel entry){
        journalEntryRepo.delete(entry.toEntity());
    }

    public List<JournalEntryModel> findEntriesbyUserId(Long userId){
        return null;
    }

}

My JournalEntity class:

package com.efl.journal.entity;

import com.efl.journal.model.JournalEntryModel;
import com.efl.journal.model.TagModel;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;

import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "entry")
@Getter
@Setter
public class JournalEntry implements Serializable {

    @GeneratedValue
    @Id
    private Long entryId;
    private Long userId;
    private String subject;
    private String body;
    @CreationTimestamp
    @ColumnDefault("CURRENT_TIMESTAMP")
    private LocalDateTime localDateTime;
    @ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
    @JoinTable(
            name = "journal_entry_tags",
            joinColumns = {@JoinColumn(name="entry_id")},
            inverseJoinColumns = {@JoinColumn(name= "tag_id")}
    )
    private Set<Tag> tags = new HashSet<>();

    public JournalEntry(){
    }

    public JournalEntryModel toModel(){
        JournalEntryModel entry = new JournalEntryModel();
        entry.setEntryId(entryId);
        entry.setUserId(userId);
        entry.setSubject(subject);
        entry.setBody(body);
        entry.setLocalDateTime(localDateTime);
        Set<TagModel> tagModels = new HashSet<>();
        tags.forEach(t->{
            tagModels.add(t.toModel());
        });
        entry.setTags(tagModels);

        return entry;
    }
}

I think the error occurs with the call of JournalEntity.toModel() method call. But I don't understand why this method is actually called when it's part of the code in the method I'm trying to mock.

1
  • I was using the wrong annotation, should have been @Mock not @MockBean , see see the accepted answer.
    – mal
    Commented Dec 21, 2019 at 14:24

1 Answer 1

1

Your test is a unit test, run with the Mockito runner, which doesn't care at all about the Spring MockBean annotation. It should be annotated with Mockito's @Mock method, not with @MockBean.

Not the answer you're looking for? Browse other questions tagged or ask your own question.