8

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.

brain storm
  • 30,124
  • 69
  • 225
  • 393

2 Answers2

4

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.

Community
  • 1
  • 1
Shawn Clark
  • 3,330
  • 2
  • 18
  • 30
  • thank you for taking time to refactor. I appreciate your effort. but I really want to why I wasn't able to inject values from application.properties. if you can help with that, please do. I will merge your pull request but I do want to know where I am going wrong. – brain storm Aug 12 '16 at 05:59
  • The comments on my pull request explain. Spring Boot 1.4 has changed the way tests are run. You can see that your annotations are deprecated. I could go back and stay with the old 1.3 annotations and try to make it work but seeing as you already set the project up for 1.4 I thought I would show you how to leverage the new stuff. – Shawn Clark Aug 12 '16 at 06:26
  • Found the issue... updated answer as I can't type it all here. – Shawn Clark Aug 12 '16 at 06:41
  • great find. thanks. but I have only two config classes (AppConfig and TestConfig). so scanning them should be sufficient correct? what If I want to scan additional TestConfig to override a bean definition of AppConfig for testing purposes.. – brain storm Aug 12 '16 at 15:57
  • With Spring it isn't just about your `@Configuration` classes. There are a number of other things that are [auto configured](http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html) based on what is available on the classpath. For your case of wanting to override things that is still possible with the `@SpringApplicationConfiguration` by doing something like `@SpringApplicationConfiguration(classes = { TestConfig.class })` – Shawn Clark Aug 12 '16 at 17:40
  • so should I include Application.class or not. I mean is this correct? @SpringApplicationConfiguration(classes= {Application.class, TestConfig.class}) – brain storm Aug 12 '16 at 17:54
  • I haven't played with that before myself so I can't say for sure. My understanding though is that the loader is still running so it will automatically pickup the Application.class and any other related configurations. You are just telling the loader to use TestConfig.class specifically for some of the beans. – Shawn Clark Aug 12 '16 at 17:56
  • I tried adding @SpringApplicationConfiguration(classes = {Application.class,TestConfig.class}) and it failed "java.lang.NoClassDefFoundError: org/springframework/boot/web/support/ServletContextApplicationContextInitializer" error – brain storm Aug 13 '16 at 01:00
  • Not sure then. I would open a new question directed at that issue as the original question was more of an issue with the annotations you were using. Maybe someone else can assist you with how to do that if the context of the question is around it. – Shawn Clark Aug 14 '16 at 23:43
1

You need another property file in the resource directory under your tests.

/src/test/resources

That's where junit is looking.

Robert Moskal
  • 21,737
  • 8
  • 62
  • 86
  • By default, it should be named application-test.properties. If you want to use a different name, you need to use `@TestPropertySource` as well. – JudgingNotJudging Aug 11 '16 at 17:51
  • if I have different profiles, for ex application-dev.properties, do I need to have application-dev-test.propeties in src/test/resources? – brain storm Aug 11 '16 at 18:17
  • by the way, I added application-test.properties in src/test/resources and I still do not see the value injected? – brain storm Aug 11 '16 at 18:18
  • Try application.properties – Robert Moskal Aug 11 '16 at 18:32
  • I tried that. it did not work either. you can browse the code here. https://bitbucket.org/SpringDevSeattle/gs-rest-service/src/a9806c4f77e375667ed279fc20104afb1036382e?at=master – brain storm Aug 11 '16 at 19:29