2

I'm trying to write tests for an application that uses @RefreshScope. I would like to add a test that actually changes out properties and asserts that the application responds correctly. I have figured out how to trigger the refresh (autowiring in RefreshScope and calling refresh(...)), but I haven't figured out a way to modify the properties. If possible, I'd like to write directly to the properties source (rather than having to work with files), but I'm not sure where to look.

Update

Here's an example of what I'm looking for:

public class SomeClassWithAProperty {
    @Value{"my.property"}
    private String myProperty;

    public String getMyProperty() { ... }
}

public class SomeOtherBean {
    public SomeOtherBean(SomeClassWithAProperty classWithProp) { ... }

    public String getGreeting() {
        return "Hello " + classWithProp.getMyProperty() + "!";
    }
}

@Configuration
public class ConfigClass {
    @Bean
    @RefreshScope
    SomeClassWithAProperty someClassWithAProperty() { ...}

    @Bean
    SomeOtherBean someOtherBean() {
        return new SomeOtherBean(someClassWithAProperty());
    }
}

public class MyAppIT {
    private static final DEFAULT_MY_PROP_VALUE = "World";

    @Autowired
    public SomeOtherBean otherBean;

    @Autowired
    public RefreshScope refreshScope;

    @Test
    public void testRefresh() {
        assertEquals("Hello World!", otherBean.getGreeting());

        [DO SOMETHING HERE TO CHANGE my.property TO "Mars"]
        refreshScope.refreshAll();

        assertEquals("Hello Mars!", otherBean.getGreeting());
    }
}
Cobra1117
  • 1,150
  • 3
  • 11
  • 26

2 Answers2

4

You could do this (I assume you mistakenly omitted the JUnit annotations at the top of your sample, so I'll add them back for you):

@SpringBootTest
public class MyAppIT {

    @Autowired
    public ConfigurableEnvironment environment;

    @Autowired
    public SomeOtherBean otherBean;

    @Autowired
    public RefreshScope refreshScope;

    @Test
    public void testRefresh() {
        assertEquals("Hello World!", otherBean.getGreeting());

        EnvironmentTestUtils.addEnvironment(environment, "my.property=Mars");
        refreshScope.refreshAll();

        assertEquals("Hello Mars!", otherBean.getGreeting());
    }
}

You also need a @SpringBootApplication annotation on your main class.

But you aren't really testing your code, only the refresh scope features of Spring Cloud (which are already tested extensively for this kind of behaviour).

I'm pretty sure you could have got this from the existing tests for refresh scope as well.

Dave Syer
  • 56,583
  • 10
  • 155
  • 143
  • Thanks for pointing me the right direction--I ended up using a very similar solution involving MockPropertySource. While I agree that this is completely tested in Spring Cloud, I wanted to make sure that OUR code was using it correctly. (In this case, the test wasn't passing because the @Config class was injecting the value (via @Value) rather than having the value inside the refresh-scoped bean. Because some of our developers had trouble with the documentation on refresh scope, I needed to come up with a solution for testing their code to make sure it uses Spring properly. Thanks again! – Cobra1117 Feb 16 '16 at 18:01
  • in my case i get the old value after refresh - the otherbean injected still returns the old values even though its refreshscoped. if i get value from environment i have the new value. – dermoritz Mar 14 '23 at 13:10
  • Is your test a `@SpringBootTest` (i.e. does it have autconfiguration enabled)? – Dave Syer Apr 21 '23 at 09:22
-3

Properties used in the application must be variables annotated with @Value. These variables must belong to a class that is managed by Spring, like in a class with the @Component annotation.

If you want to change the value of the properties file, you can set up different profiles and have various .properties files for each profile.

We should note that these files are meant to be static and loaded once, so changing them programmatically is sort of out of the scope of ther intended use. However, you could set up a simple REST endpoint in a spring boot app that modifies the file on the host's file system (most likely in the jar file you are deploying) and then calls Refresh on the original spring boot app.

Andonaeus
  • 872
  • 1
  • 5
  • 21
  • Thanks for the comment! I think I may have been unclear, so I've updated the question to show what I'm looking for. It probably would be possible to use a profile to point Cloud Config at a properties file and edit that file, but I'd prefer to somehow either host a mock Cloud Config service or otherwise splice directly into the loading process to avoid having to write out properties files during testing. – Cobra1117 Feb 11 '16 at 15:12
  • They don't have to be `@Value` at all. The can be in `@ConfigurationProperties` or directly used via `Environment`. – spencergibb Feb 11 '16 at 15:14
  • Perhaps I should have specified that it was one way of doing it. – Andonaeus Feb 11 '16 at 15:24