-1

I have created a spring boot web application with Rest based apis. My controller has a service which is autowired and injected when the application is started normally when run via main application. However if i run my integration test for that controller. I get the following exception.

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type 'com.xyz.test.service.LolService' available: expected
 at least 1 bean which qualifies as autowire candidate. Dependency annotations: 
{@org.springframework.beans.factory.annotation.Autowired(required=true)}

This is my controller class.

package com.xyz.test.controller;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.xyz.test.model.Lol;
import com.xyz.test.service.LolProcessingService;
import com.xyz.test.service.LolService;


@RestController
@RequestMapping("/api")
@Slf4j
public class CalorieController {

    @Autowired
    private LoleService lolService;

    @Autowired
    private LolProcessingService lolProcessingService;


    @PostMapping("/lol")
    @PreAuthorize("hasRole('USER')")
    public FoodIntake createLol( @Valid @RequestBody Lol lol) throws Exception {
        return lolProcessingService.processNewLol(lol);
    }
}

This is my service class

package com.xyz.test.service.impl;
   @Service
    public class LolServiceImpl implements LolService{

        @Autowired
        private LolRepository lolRepository;

    //some methods
    }

And this is my Integration test class.

package com.xyz.test.rest;

import org.json.JSONException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;

import com.xyz.test.controller.CalorieController;


@RunWith(SpringRunner.class)
@SpringBootTest(classes = CalorieController.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableAutoConfiguration
@ComponentScan
public class LolControllerTest {


    @LocalServerPort
    private int port;

    TestRestTemplate restTemplate = new TestRestTemplate();

    HttpHeaders headers = new HttpHeaders();


    @Test
    public void testabc() throws JSONException {
        headers.add("Authorization","Basic cGFtcGxlOnBhbXBsZTEyMw==");
        HttpEntity<String> entity = new HttpEntity<String>(null, headers);

        ResponseEntity<String> response = restTemplate.exchange(
                createURLWithPort("/api/lol/pample"),
                HttpMethod.GET, entity, String.class);

        String expected = "[]";

        JSONAssert.assertEquals(expected, response.getBody(), false);
    }

    private String createURLWithPort(String uri) {
        return "http://localhost:" + port + uri;
    }


}

And finally this is main application

package com.xyz.test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableJpaAuditing
public class SpringbootSecurityMysqlApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootSecurityMysqlApplication.class, args);
    }

    //bean for making http external calls
    @Bean 
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

I dont understand why the test class doesnt start properly with proper injection of beans.. I need help in this. Thanks in advance.

stallion
  • 1,901
  • 9
  • 33
  • 52

1 Answers1

3

Don't specify classes parameter for the @SpringBootTest annotation.

Using it in following way

 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

will cause Spring Boot to look for configuration class in current package, then parent package etc.. eventually finding your SpringbootSecurityMysqlApplication. This will test full application including all layers.

In case you want to test the controller in isolation with mocked service use

@WebMvcTest(CalorieController.class)

and provide mock bean for the service in your test

@MockBean
private LoleService lolService;
František Hartman
  • 14,436
  • 2
  • 40
  • 60