Geyser14
Geyser14

Reputation: 1435

@ComponentScan not working in test with spring-boot-starter-test

I am attempting to test my @Service and @Repository classes in my project with spring-boot-starter-test and @Autowired is not working for the classes I'm testing.

Unit test:

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = HelloWorldConfiguration.class
//@SpringApplicationConfiguration(classes = HelloWorldRs.class)
//@ComponentScan(basePackages = {"com.me.sbworkshop", "com.me.sbworkshop.service"})
//@ConfigurationProperties("helloworld")
//@EnableAutoConfiguration
//@ActiveProfiles("test")
// THIS CLASS IS IN src/test/java/ AND BUILDS INTO target/test-classes
public class HelloWorldTest {

    @Autowired
    HelloWorldMessageService helloWorldMessageService;

    public static final String EXPECTED = "je pense donc je suis-TESTING123";

    @Test
    public void testGetMessage() {
        String result = helloWorldMessageService.getMessage();
        Assert.assertEquals(EXPECTED, result);
    }
}

Service:

@Service
@ConfigurationProperties("helloworld")
// THIS CLASS IS IN /src/main/java AND BUILDS INTO target/classes
public class HelloWorldMessageService {

    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String  message) {
        this.message=message;
    }

}

The commented class annotations on the unit test represent the various things I've tried to get this working. The test and the project packages are in the same package paths and the @ComponentScan works fine from my entry point (@RestController class with main method). The service @ComponentScan's and @Autowire's fine in my @RestController class in the src/main/java side, but does not in the test. I am required to add it again as a @Bean in my @Configuration class in order for @Autowired to work. The class is otherwise in scope just fine and I can reference and instantiate it just fine from the test. The problem appears to be that @ComponentScan does not appear to correctly traverse multiple entries in my test runner classpath, in this case /target/test-classes and /target/classes.

The IDE I am using is IntelliJ IDEA 13.

UPDATE - here are HelloWorldRs and its config:

@RestController
@EnableAutoConfiguration
@ComponentScan
public class HelloWorldRs {

    //    SPRING BOOT ENTRY POINT - main() method
    public static void main(String[] args) {
        SpringApplication.run(HelloWorldRs.class);
    }

    @Autowired
    HelloWorldMessageService helloWorldMessageService;

    @RequestMapping("/helloWorld")
    public String helloWorld() {
        return helloWorldMessageService.getMessage();
    }

}

...

@Configuration
public class HelloWorldConfiguration {

    @Bean
    public Map<String, String> map() {
        return new HashMap<>();
    }

    // This bean was manually added as a workaround to the @ComponentScan problem
    @Bean
    public HelloWorldMessageService helloWorldMessageService() {
        return new HelloWorldMessageService();
    }

    // This bean was manually added as a workaround to the @ComponentScan problem
    @Bean
    public HelloWorldRs helloWorldRs() {
        return new HelloWorldRs();
    }
}

Upvotes: 4

Views: 22784

Answers (3)

Heri
Heri

Reputation: 4598

SpringBoot 2.7.3, JUnit 5.8.2

If you want to have full control about the spring's configuration (and not rely on the hidden magic of auto configuration) I suggest to create an explicit configuration class:

@ComponentScan(basePackages = { "my.package.to.scan" })
public class MySpringTestConfig
{
    // just for spring configuration annotations
}

and reference it in your test class:

@ContextConfiguration(classes = { MySpringTestConfig.class })
@ExtendWith({ SpringExtension.class })
class MySpringTest
{
    ...
}

Upvotes: 0

Marwin
Marwin

Reputation: 2763

First, I'd recommend to use a newer @RunWith(SpringRunner.class) but that makes no difference, it is just shorter (and recommended).

Second, from the @EnableAutoConfiguration I see that you are using spring boot - which is certainly a good thing. There are some good reasons why not to use @ComponentScan directly. Can you try the following?

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes=YourApplication_or_other_Configuration.class)
public class HelloWorldTest {
... etc.

Upvotes: 3

Dave Syer
Dave Syer

Reputation: 58124

I don't know if this will turn out to be the solution, but don't use the default package (i.e. don't put *.java in "src/main/java" directly), and definitely don't use a @ComponentScan or @EnableAutoConfiguration in the default package. You will end up killing your application on startup as it tries to scan everything on the classpath (including all the Spring libraries).

Upvotes: 2

Related Questions