7

I am trying to write a unit test for the following method in my controller.

@Autowired
    private ApplicationContext context;

    private String getProperty() {
        try {
            Properties props = context.getBean("myProperties", Properties.class);
            String val = props.getProperty("myProperty");
......

The Bean is declared like this in my applicationContext:

<util:properties id="myProperties" scope="prototype" location="file:${catalina.base}/webapps/myProperties.properties"/>

How can I mock this so that I can test different values of the val variable?

I thought about creating a test properties file and mocking it like this:

context = Mockito.mock(ApplicationContext.class);
Mocikto.when(context.getBean("myProperties", Properties.class)).thenReturn(some test file)

but then I would have to declare the test file as a bean somewhere.

I was wondering if there was an easier way to do this?

Thanks

blong824
  • 3,920
  • 14
  • 54
  • 75
  • 1
    Are you using Spring MVC? Rather than giving your controller a reference to the ApplicationContext and pulling values from it, you could simply define a field on your Controller and set a value for it in the controller's bean definition. Then testing becomes a non-issue, because you can initialize your controller however you want in your tests - no need to mock the Application Context at all. – Jon Quarfoth Jun 17 '11 at 16:34

3 Answers3

5

If you're using spring-3, you can do:

<context:property-placeholder location="myprops.properties" />

And in your code:

@Value("${myProperty}")
private String myProp;

public String getMyProp() {
    return myProp;
}

This causes myprops.properties to be made available for variable substitutions via ${...} expressions, and the @Value annotation allows value injection of properties. Then in your unit test you can simply set different values of myProp.

edwardmlyte
  • 15,937
  • 23
  • 58
  • 83
Kevin
  • 24,871
  • 19
  • 102
  • 158
  • This ties into a question I asked before...what happens if myprops.proeprties can not be found? Currently I have this in a try catch block because this properties file lives outside the war and there is a slight possibility that it will not be in the correct location – blong824 Jun 17 '11 at 17:03
  • The spring container will throw. I suppose you can try to apply some defaults if the properties are not found, but I prefer having the container not start. If the property file is missing or in the wrong location or something, wouldn't you rather just find out right away and fix it rather than silently fall back to some possibly not correct properties? – Kevin Jun 17 '11 at 20:56
  • 1
    Where do you put the ``` ```? – Linkx_lair Jul 22 '21 at 15:26
1

The easier way is to use a org.springframework.beans.factory.config.PropertyPlaceholderConfigurer instead of pulling the properties explicitly from the spring application context. The PropertyPlaceholderConfigurer injects your bean with the properties you specify. Then you don't need Mockito at all, in the test you set the property value in the Controller to whatever you want it to be.

So you'd set up the configurer in the application context xml:

<context:property-placeholder 
  location="file:${catalina.base}/webapps/myProperties.properties"/>

and add some configuration for your controller (I expect there's a way to do this with annotations but don't know it):

 <bean id="whateverMyControllerIdIs" class="com.initech.foobar.MyControllerImpl">
   <property name="quux"><value>${myProperty}</value></property>
 </bean>

where the controller has an instance variable that you want to populate with the property, with a setter, like this:

String quux;

public void setQuux(String quux) {this.quux = quux;}

Just saw a blog post on Spring 3.1 enhancements, here's the new xml-free way to do this:

    @Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
1

So I can test without having to load the Spring Context I use a Config class for accessing all of the properties file(s) values from within code. The benefits are:

1) Spring doesn't load in your unit tests

2) You can force an Exception if the property is missing and it is required

3) You can return strongly type property values from the getter() methods (i.e. convert to a Date)

4) The "key" values expected from your properties files are documented in a single Java class (i.e. public static final PROP_XXX)

@Component
public class Config {

public static final String PROP_USER_NAME = "user.name";

private Properties applicationProperties;


/*** Functional methods ***/

/**
 * Helper method to ensure consistent Exception handling where requested property values are missing
 * from the properties files and they are "required" for the application to function correctly.
 *
 * @param key
 * @return The String value of the property requested
 */
private String readPropertyRequired(String key) {

    String value = readProperty(key);

    if(StringUtils.isBlank(value))  {
        throw new PropertyNotFoundException(key);
    }

    return value;
}

/**
 * Helper method to return String values from the properties files that have been loaded
 *
 * @param key
 * @return The String value of the property requested or NULL if empty
 */
private String readProperty(String key) {
    return applicationProperties.getProperty(key);
}


/*** Getters & Setters ***/

@Autowired
public void setApplicationProperties(Properties applicationProperties) {
    this.applicationProperties = applicationProperties;
}

public String getUserName() {
    return readPropertyRequired(PROP_USER_NAME);
}

}

You can then unit test this class by simply injecting a standard java.util.Properties

Brad
  • 15,186
  • 11
  • 60
  • 74