springspring-bootspring-testspringjunit4classrunner

why properties from appliction.properties not available in Junit test in spring boot application?


I have a spring boot application. I am writing Junit tests. I am trying to inject values from application.properties (defined in src/main/resources/application.properties) and a Status bean configured in AppConfig (src/main/java/hello/AppConfig.java). I see that the bean is autowired (through debugger it is not null) but the values are not set.

Here is application.properties

src/main/resources/application.properties

app.name=rest-service
app.version=1.0.0-SNAPSHOT

src/main/java/hello/AppConfig.java

@Configuration
public class AppConfig {

    @Value("${app.version}")
    private String version;
    @Value("${app.name}")
    private String name;


    @Bean
    public Status status(){
        return new Status(name,version);
    }

}

//Status is a simple pojo with name and version fields

src/test/java/hello/GreetingControllerTests.java

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@ContextConfiguration(classes={hello.TestConfig.class,hello.AppConfig.class})
@WebAppConfiguration
public class GreetingControllerTests {


    @Autowired
    Environment envi;

    @Autowired
    Status status;

    @Value("${app.name:notConfigured}")
    String appName;

    private String env;

    @Before
    public void init(){
        System.out.println(status.getName());
        System.out.println(appName);
        env=envi.getActiveProfiles()[0];
    }

    @Test
    public void should_fail(){
        if (env.equalsIgnoreCase("DEV")){
            assertThat(false).isFalse();
        }
        if (env.equalsIgnoreCase("QA1")){
            System.out.println("failing the test");
            fail();
        }
    }
}

I set debugger in the init method and found that both appName and status bean with right values set are NOT injected. Status bean is injected though. just the values are not set.


Solution

  • Thank you for sharing the project. It made it a lot easier to work with. Please see my pull request (with comments) to understand how I got it all working.

    https://bitbucket.org/SpringDevSeattle/gs-rest-service/pull-requests/4/updating-project-for-spring-boot-14/diff

    * Update * You had annotations being overwritten due to mixing some old Spring annotations with newer annotations. Credits to spring-boot properties not @Autowired for pointing me to the solution based on a google search for application.properties not being populated in the environment.

    If you look at the @SpringApplicationConfiguration:

    @ContextConfiguration(loader = SpringApplicationContextLoader.class)
    @Documented
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Deprecated
    public @interface SpringApplicationConfiguration {
    

    It is actually composed of a number of other annotations. The important one is the @ContextConfiguration. For the @SpringApplicationConfiguration it uses a loader and that loader is scanning the classpath for appropriate configuration files along with components to load (one being the thing that reads the application.properties into the environment). Within the test you override that annotation with your declaration:

    @ContextConfiguration(classes={hello.TestConfig.class,hello.AppConfig.class})
    

    This forces Spring to only load the TestConfig and AppConfig classes without scanning for any other configuration files. Commenting out that annotation and running it will make the test pass and debugging the init() method will show the values injected.