Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot: @ConfigurationProperties not satisfied on test

I'm getting this exception on my test excution:

UnsatisfiedDependencyException: Error creating bean with name 'net.gencat.transversal.espaidoc.mongo.GridFSTest': Unsatisfied dependency expressed through field 'resourceProperties'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.gencat.transversal.espaidoc.ResourcesConfigProperties' available: expected at least 1 bean which qualifies as autowire candidate.

So, I think message is so clear enough: ResourcesConfigProperties is not satisfied.

My test:

RunWith(SpringRunner.class)
@SpringBootTest()
public class GridFSTest {

    @Autowired
    private GridFsTemplate gridFsTemplate;

    @Autowired
    private ResourcesConfigProperties resourceProperties;

    public URL getHugeResource() {
        try {
            return Paths
                .get(this.resourceProperties.getHuge())
                .toUri()
                .toURL();
        } catch (MalformedURLException e) {
            return null;
        }
    }

    @Test
    public void storeHugeFile() throws IOException {
        URL resource = this.getHugeResource();

        this.gridFsTemplate.store(
            resource.openStream(),
            resource.getPath(),
            "mime"
        );
    }
}

and ResourcesConfigProperties is:

@ConfigurationProperties(prefix = "files")
public class ResourcesConfigProperties {

    private String huge;

    /**
     * @return the huge
     */
    public String getHuge() {
        return huge;
    }

    /**
     * @param huge the huge to set
     */
    public void setHuge(String huge) {
        this.huge = huge;
    }

}

into my src/test/resources I have my application.properties file:

files.huge: /home/jcabre/Downloads/1GB.zip

Any ideas?

EDIT

Main Spring boot application:

@SpringBootApplication(
    //scanBasePackages = { "cat.gencat.ctti.canigo.arch.web.rs" },
    exclude = JmxAutoConfiguration.class
)
@EnableConfigurationProperties({
    ApiProperties.class,
    FileStoreProperties.class
})
@Import(RedisConfiguration.class)
public class ApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }
}
like image 366
Jordi Avatar asked Dec 13 '22 14:12

Jordi


1 Answers

TL;DR:

It is happening, because the @ConfigurationProperties are not managed by the application context you build in tests, although they will be load when the application launches, because you have @EnableConfigurationProperties on your app main class.

@EnableConfigurationProperties on main class only affect the application context you have when you bootRun, but not that in a @SpringBootTest. The application context you build in tests could be, under many circumstances, distinct with that of bootRun, just like in your case.

You can add @Component to make the context be aware of it, both in gradle bootRun and in gradle test. It's the easiest way, but not 100% the suggested way.


More proper way with details if you have time

Instead, you can add @EnableConfigurationProperties({Config1.class, Config2.class}) in a @SpringBootTest, to inject only some of the configuration properties class into the context, to avoid injection overhead.

It would be like:

//@RunWith(SpringRunner.class) // for JUnit4 in Spring
@ExtendWith(SpringExtension.class) // for JUnit5 in Spring. 
@SpringBootTest
@EnableConfigurationProperties({
        ResourcesConfigProperties.class,
})
@Data
public class ConfigsTest {

    @Autowired
    private ResourcesConfigProperties resourceConfigProperties;

    ...

}

Better yet, you can use @SpringBootTest(classes={}): classes within {} are those you want the application context of @SpringBootTest to manage(creation, initialization, loading properties from yaml files, and so on). Then you don't have to load all the context, but only part of it.

You can group all classes of @ConfigurationProperties in one class of @Configuration, and put it in the classes={} of @SpringBootTest, instead of repeating this list of @ConfigurationProperties everywhere. Something like:

//@RunWith(SpringRunner.class) // for JUnit4 in Spring
@ExtendWith(SpringExtension.class) // for JUnit5 in Spring. 
@SpringBootTest(classes = {
    TestConfiguration.class
})
@Data
public class ConfigsTest {

    @Autowired
    private ResourcesConfigProperties resourceConfigProperties;


    ...

}

TestConfiguration.java:

@EnableConfigurationProperties({
        ResourcesConfigProperties.class,
})
@Configuration
public class TestConfiguration {
}
like image 112
WesternGun Avatar answered Jan 14 '23 06:01

WesternGun