Spring boot field injection with autowire not work

2020-02-29 11:08发布

问题:

I want to inject DeMorgenArticleScraper in a test.

@RunWith(SpringJUnit4ClassRunner.class)
public class DeMorgenArticleScraperTest {

    @Autowired
    private DeMorgenArticleScraper deMorgenArticleScraper;

    ...
}

The DeMorgenArticleScraper component has some configuration going on for itself, but the IDE/compiler is not complaining about them.

@Component
public class DeMorgenArticleScraper extends NewsPaperArticleScraper {

    @Autowired
    public DeMorgenArticleScraper(
            @Qualifier("deMorgenSelectorContainer") SelectorContainer selector,
            GenericArticleScraper genericArticleScraper,
            @Qualifier("deMorgenCompany") Company company) {
        super(selector, genericArticleScraper, company);
    }

    ...
}

The constructor parameters that are annotated with @Qualifier, are defined in a Config.class With @Bean. The class itself has @Configuration. I figure the problem is not situated here.

The IDE warns me already, no bean found...autowired members must be defined in a bean. But as far as I know, it is defined in a bean with the @Component annotation. All other bean wiring seems ok as the Spring boot application can start (when I comment out the test class).

回答1:

@SpringBootTest is fairly heavyweight, and for all intents and purpose will load your entire application, https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications, it's fairly heavyweight and dramatically affects test time. Depending on what you are trying to test you may want to look into

  • Slice tests e.g. @JsonTest, @DataJpaTest, @WebMvcTest etc. , https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-testing-autoconfigured-tests. Benefit of these tests are not only will they not load everything, thus faster, but will try to hunt out the relevant configurations.
  • Plain old @ContextConfigurationand point to the relevant @Configuration's required to load beans needed for the test https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#contextconfiguration


回答2:

I replaced

@RunWith(SpringJUnit4ClassRunner.class)

with

@SpringBootTest
@RunWith(SpringRunner.class)

This appears to be working fine: I see Spring boot firing up and loading beans. I'll keep this question open for a short while for better suggestions.



回答3:

I have the same problem.

  1. There is service, which I want to inject for testing but I want to test only platformToSql(..) method without launching whole Spring Boot application.

    @Service
    public class ConverterToSqlImpl implements IConverterToSql {
    
        private final ISomeRepository someRepository;
        private final IUtilService utilService;
    
        public ConverterToSqlMainConstructorServiceImpl(
                IWorkerUtilService workerUtilService,
                ISomeRepository someRepository) {
            this.utilService = utilService;
            this.someRepository = someRepository;
        }
    
        @Override
        public String platformToSql(String platformName) {
            return "platform='" + platformName + "'";
        }
    
        @Override
        public String methodWhichUseRepositoryAndUtilBeans() {
            // some code which is not interested us
        }
    }
    
  2. I create test class. It is necessary to mock beans because they are not need for test method.

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = {ConverterToSqlImpl.class})
    //@ContextConfiguration(classes = {ConverterToSqlImpl.class})
    @MockBeans({@MockBean(IUtilService.class), @MockBean(ISomeRepository.class)})
    public class SqlConverterConstructorTest {
    
        @Autowired
        private IConverterToSqlMainConstructorService convertToSql;
    
        @Test
        public void platformTest() {
            String platformSql = convertToSql.platformToSql("PLATFORM_1");
            String expectedSql = "platform='PLATFORM_1'";
            Assert.assertEquals(platformSql, expectedSql);
        }
    }
    

@SpringBootTest(classess = {}) load to ApplicationContext only classess which are pointed out in classess section, not whole application with all beans will be lauched.

Also, as alternative to @SpringBootTest with classes={..}, you can uncomment line with @ContextConfiguration. It is plain old style.