Friday, June 23, 2017

Overriding Spring Context Initializers

Consider the scenario where you need to override certain properties across the integration tests

User class with name and hobby

PropertySources :

NameHobbyPropertySource
public class NameHobbyPropertySource extends PropertySource<properties> {

 static Properties source = new Properties();
 
 public NameHobbyPropertySource(String name) {
  super(name, source);
  source.setProperty("name", "chinnu");
  source.setProperty("hobby", "cycling");
 }

 @Override
 public Object getProperty(String key) {
  return getSource().get(key);
 }
}

HobbyOnlyPropertySource
public class HobbyOnlyPropertySource extends PropertySource<properties> {

 static Properties source = new Properties();
 
 public HobbyOnlyPropertySource(String name) {
  super(name, source);
  source.setProperty("hobby", "running");
 }

 @Override
 public Object getProperty(String key) {
  return getSource().get(key);
 }
}

And this is your default context initializer for your application (that uses NameHobby property source)

public class MainApplicationContextInitializer implements ApplicationContextInitializer<configurableapplicationcontext> {

 public void initialize(ConfigurableApplicationContext applicationContext) {
  System.out.println("Main initializer called");
  NameHobbyPropertySource nameHobbyPropertySource = new NameHobbyPropertySource("name-hobby");
  applicationContext.getEnvironment().getPropertySources().addFirst(nameHobbyPropertySource);
 }

}
Say your BaseIntegrationTest is configured with initializer that has NameHobbyPropertySource

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers = { MainApplicationContextInitializer.class
  }, locations = { "classpath:application-context.xml" })
public abstract class BaseApplicationTest {
 
}

but you want to override this for only one test class.
i.e. your special test case should run with overridden hobby from HobbyOnlyPropertySource. You can achieve this using inheritInitializers as follows,

public class SubSetApplicationContextInitializer implements
  ApplicationContextInitializer<configurableapplicationcontext> {

 public void initialize(ConfigurableApplicationContext applicationContext) {
  System.out.println("Subset initializer called");
  HobbyOnlyPropertySource hobbyOnlyPropertySource = new HobbyOnlyPropertySource(
    "hobby");
  applicationContext.getEnvironment().getPropertySources()
    .addFirst(hobbyOnlyPropertySource);
 }

}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers = { SubSetApplicationContextInitializer.class }, inheritInitializers = true)
public class ApplicationTest extends BaseApplicationTest {

but this will not work because, By default the initializers of the derived class gets called and then the base class's initializers are called.

To override this behaviour, BOTH the initializer should implement Ordered interface and define an order.

Whichever one wants to run later should be given higher order. You can also try @Order annotation for the same.

 Lets say MainApplicationContextInitializer is from third party lib, and it doesn't implement or annotated with Order, then this wouldn't work. So have a wrapper over the third party initializer and annotate it with @Order.

@Order(0)
public class WrapperMainApplicationContextInitializer extends MainApplicationContextInitializer{

}
And so the BaseApplicationTest should now use this wrapper,
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers = { WrapperMainApplicationContextInitializer.class
  }, locations = { "classpath:application-context.xml" })
public abstract class BaseApplicationTest {
 
}

and your subset application context initializer should also be ordered
public class SubSetApplicationContextInitializer implements
  ApplicationContextInitializer<configurableapplicationcontext>, Ordered {

 public void initialize(ConfigurableApplicationContext applicationContext) {
  System.out.println("Subset initializer called");
  HobbyOnlyPropertySource hobbyOnlyPropertySource = new HobbyOnlyPropertySource(
    "hobby");
  applicationContext.getEnvironment().getPropertySources()
    .addFirst(hobbyOnlyPropertySource);
 }

 public int getOrder() {
  return 1;
 }
}

Refer :

No comments:

Post a Comment