Spring Boot Controller Test - Mockito when thenReturn list not working

Viewed 2414

I'm mocking my MemberServiceImpl class. In particular the following getMembers() method which returns List<Golfer>:

@Service
public class MemberServiceImpl implements MemberService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    MemberRepository repository;

    @Override
    public List<Golfer> getMembers() {
        List<Golfer> members = repository.findAll();
        return members;
    }

In my MemberControllerTest class I create a list of golfers and the use the when thenReturn(members) before sending my GET request - which always returns an empty list. Any ideas what I am doing wrong:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MemberControllerTest {

    @Mock
    MemberService memberService;

    @InjectMocks
    MemberController memberController = new MemberController();

    @Autowired
    private TestRestTemplate restTemplate;

    //bind RANDOM_PORT
    @LocalServerPort
    private int port;
    private String name = "David";
    private Golfer member = new Golfer("David");
    private HttpHeaders headers = new HttpHeaders();
    private String baseUrl = "http://localhost:";

    @Test
    void testGetAllMembers() throws Exception{
        restTemplate = new TestRestTemplate();
        HttpHeaders headers = new HttpHeaders();

        Golfer member1 = new Golfer("Harry");
        Golfer member2 = new Golfer("Maureen");
        List<Golfer> members = new ArrayList<Golfer>();
        members.add(member1);
        members.add(member2);

        when(memberService.getMembers()).thenReturn(members);

        ResponseEntity<List<Golfer>> response = restTemplate.exchange("http://localhost:"+port, HttpMethod.GET, null, new ParameterizedTypeReference<List<Golfer>>() {});

        assertEquals(members,response.getBody());
    }   
}
2 Answers

Your mock isn't picked up by the application (test) context. So even though you mock your service and inject it into your controller instance inside your test, that instance of the controller is not the instance that will be used by the application context when you fire the request against your (test)application.

Instead of using @Mock and @InjectMocks, create a TestConfiguration class and use @MockBean instead for the ServiceImpl. Remove the controller from the test all together, as you dont have to interact with it. So something like:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 
@Import(MemberControllerTestConfiguration.class)
class MemberControllerTest {

  @Autowired
  MemberService memberService;

  @Autowired
  private TestRestTemplate restTemplate;

//rest of your test

}


@TestConfiguration
class MemberControllerTestConfiguration{

  @MockBean
  MemberService memberService;
}

I usually tend to to stick that Testconfiguration at the bottom of the same test file to keep them together as this is only something that will be used by this test. You may have to annotate the mocked bean with an @Primary if it starts complaining about too many beans

You are mocking the controller object, but expect it to handle the request. Let Spring create the Controller like in normal application startup, no need to define it by yourself.

But you need to change the definition of the MemberServiceto be a @MockBean that means that when Spring needs a service to inject it into the controller it creates, it will use this mock bean:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MemberControllerTest {

    @MockBean
    MemberService memberService;

    @Autowired
    private TestRestTemplate restTemplate;

    //bind RANDOM_PORT
    @LocalServerPort
    private int port;
    private String name = "David";
    private Golfer member = new Golfer("David");
    private HttpHeaders headers = new HttpHeaders();
    private String baseUrl = "http://localhost:";

    @Test
    void testGetAllMembers() throws Exception{
        restTemplate = new TestRestTemplate();
        HttpHeaders headers = new HttpHeaders();

        Golfer member1 = new Golfer("Harry");
        Golfer member2 = new Golfer("Maureen");
        List<Golfer> members = new ArrayList<Golfer>();
        members.add(member1);
        members.add(member2);

        when(memberService.getMembers()).thenReturn(members);

        ResponseEntity<List<Golfer>> response = restTemplate.exchange("http://localhost:"+port, HttpMethod.GET, null, new ParameterizedTypeReference<List<Golfer>>() {});

        assertEquals(members,response.getBody());
    }
}
Related