Access Spring profiles in spring-junit test classes - spring

I am using Spring 4.3.0.I am writing a SDK in that i am having the following classes,
Providers.java
#ComponentScan
#Service
//#PropertySource("classpath:application.properties")
public class Providers {
#Autowired
ApplicationContext context;
public Providers(){
}
public Providers(ApplicationContext applicationContext){
this.context = applicationContext;
}
//...Other SDK component code
}
ProvidersBuilder.java
public class ProvidersBuilder {
//Set providers property
public Providers build() throws LifecycleException, InsufficientPropertiesException {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
StandardEnvironment env = new StandardEnvironment();
context.setEnvironment(env);
if(cond1){
context.getEnvironment().addActiveProfile("profile1");
}
if(cond2){
context.getEnvironment().addActiveProfile("profile2");
}
...etc
context.setParent(null);
context.register(Providers.class);
context.refresh();
Providers Providers = new Providers(context);
return Providers;
}
}
I have following configuration for Spring-Junit test classes,
SpringContextLoader.java
#ComponentScan(basePackages = "com.providers.global")
#PropertySource("classpath:application.properties")
public class SpringContextLoader {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringContextLoader.class);
}
}
In one of my test class, I am trying to print all the profiles,
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringContextLoader.class)
public class ProvidersTest{
#Autowired
ApplicationContext context;
#Before
public void beforeMethod() throws Exception {
String[] profiles = context.getEnvironment().getActiveProfiles();
if(profiles != null && profiles.length > 0){
for (String string : profiles) {
logger.info(String.format("Active Profiles in test::%s",string));
}
}
}
#Test
public void activateProviders() throws Exception{
...invoking test call
}
}
In the logs i am able to see only the profiles configured in application.properties, but i would like to get the profiles which are dynamically added in ProvidersBuilder.java.
Basically i would run ProvidersTest only for particular profiles for that i am using the following annotation,
#IfProfileValue(name = "spring.profiles.active", values = { "charging" })
Since application context always returns default profile configured in application.properties this class never get a chance to run.
Could anyone please help me to resolve this issue.Why the profiles added in ProvidersBuilder.java is not available in ProvidersTest.java?
**Edit 1 **
SpringContextLoader.java
#ComponentScan(basePackages = "com.providers.global")
#PropertySource("classpath:application.properties")
public class SpringContextLoader {
#Bean(name = "ConfigApplicationContext")
public AnnotationConfigApplicationContext applicationContext() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
return context;
}
}
Now we are not creating new AnnotationConfigApplicationContext anywhere in application.
ProvidersBuilder.java
public class ProvidersBuilder {
#Autowired
#Qualifier("ConfigApplicationContext")
public AnnotationConfigApplicationContext context;
//Set providers property
public Providers build() throws LifecycleException, InsufficientPropertiesException {
context.setEnvironment(env); **Here i am getting getting NullPointerException**
if(cond1){
context.getEnvironment().addActiveProfile("profile1");
}
if(cond2){
context.getEnvironment().addActiveProfile("profile2");
}
...etc
context.setParent(null);
context.register(Providers.class);
context.refresh();
Providers Providers = new Providers(context);
return Providers;
}
}
In the ProvidersBuilder.java while getting "AnnotationConfigApplicationContext context" using #Autowired it returns null.

Related

How to override DefaultListableBeanFactory in Spring Boot application?

Are there ways to override properties of DefaultListableBeanFactory in Spring Boot application?
For example, I want to set the DefaultListableBeanFactory.allowBeanDefinitionOverriding property to false.
EDIT
Use case.
I have pretty plain class:
#Data
#AllArgsConstructor
class MyTempComponent {
private String value;
}
Which I want use as #Bean in my application but this bean can be defined several times, for example:
#Configuration
class TestConfiguration1 {
#Bean
MyTempComponent myTempComponent() {
return new MyTempComponent("Value 1");
}
}
#Configuration
class TestConfiguration2 {
#Bean
MyTempComponent myTempComponent() {
return new MyTempComponent("Value 2");
}
}
Also there is a consumer of that bean:
#Component
class TestConfiguration3 {
private MyTempComponent myTempComponent;
#Autowired
public TestConfiguration3(MyTempComponent myTempComponent) {
this.myTempComponent = myTempComponent;
}
#PostConstruct
public void print() {
System.out.println(this.myTempComponent.getValue());
}
}
When an application starts it prints "Value 2" message, i.e. initializes myTempComponent bean from TestConfiguration2.
I want to prohibit creation of that bean (and any other beans) if there are two or more candidates.
As I realized I can reach this goal via setting DefaultListableBeanFactory.allowBeanDefinitionOverriding to false.
But how to set this property in Spring Boot application? Could you provide an example please?
You can set
private static class CustomAppCtxInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext applicationContext) {
applicationContext
.getDefaultListableBeanFactory()
.setAllowBeanDefinitionOverriding(false);
}
}
and then have
public static void main(String[] args) {
try {
final SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.addInitializers(new CustomAppCtxInitializer());

unable to use #AspectJ with Spring-Apache CXF services

I am new to spring and am working on a rest service written using Spring and Apache CXF with Java Configurations. I have the following rest service.
#Path("/release/")
#Component
#RestService
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ReleaseResource extends AbstractService implements IResource {
#Override
#CustomLogger
#GET
public Response get() {
//Some Logic
return Response.ok("Success!!").build();
}
}
I have created an aspect using #AspectJ for logging. However, the aspect is not working on the services written in CXF. I did a bit of searching in net and found that Spring needs proxy beans for the aspects to work. Then I tried few approaches such as
Making the service class implement an interface
Using CGLIB library and scope proxy mode TARGET_CLASS
Extending a class with method
#Override
public void setMessageContext(MessageContext context) {
this.context = context;
}
But none of them worked.
Any idea if it is possible to run the aspect around the services?
If yes, can someone please tell me how to.
I have read that this can be achieved by bytecode weaving the aspectj manually instead of using spring aspectj autoproxy (not sure how to do it though). Can someone tell me if this is a good option and how to do it?
EDIT:
Sorry for the incomplete info provided. Attaching the other classes
#Aspect
#Configuration
public class LoggerAspect {
#Pointcut(value = "execution(* *(..))")
public void anyPublicMethod() {
}
#Around("anyPublicMethod() && #annotation(CustomLogger)")
public Object logAction(ProceedingJoinPoint pjp, CustomLogger customLogger) throws Throwable {
//Log Some Info
return pjp.proceed();
}
}
Web Initializer class:
#Configuration
public class WebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(new ContextLoaderListener(createWebAppContext()));
addApacheCxfServlet(servletContext);
}
private void addApacheCxfServlet(ServletContext servletContext) {
CXFServlet cxfServlet = new CXFServlet();
ServletRegistration.Dynamic appServlet = servletContext.addServlet("CXFServlet", cxfServlet);
appServlet.setLoadOnStartup(1);
appServlet.addMapping("/*");
}
private WebApplicationContext createWebAppContext() {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(TestConfig.class);
return appContext;
}
}
Config Class:
#Configuration
#ComponentScan(basePackages = "com.my.package")
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class TestConfig {
private static final String RESOURCES_PACKAGE = "com.my.package";
#ApplicationPath("/")
public class JaxRsApiApplication extends Application {
}
#Bean(destroyMethod = "shutdown")
public SpringBus cxf() {
return new SpringBus();
}
#Bean
public JacksonJsonProvider jacksonJsonProvider() {
return new JacksonJsonProvider();
}
#Bean
public LoggerAspect getLoggerAspect() {
return new LoggerAspect();
}
#Bean
IResource getReleaseResource() {
return new ReleaseResource();
}
#Bean
#DependsOn("cxf")
public Server jaxRsServer(ApplicationContext appContext) {
JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint(jaxRsApiApplication(),
JAXRSServerFactoryBean.class);
factory.setServiceBeans(restServiceList(appContext));
factory.setProvider(jacksonJsonProvider());
return factory.create();
}
private List<Object> restServiceList(ApplicationContext appContext) {
return RestServiceBeanScanner.scan(appContext, TestConfig.RESOURCES_PACKAGE);
}
#Bean
public JaxRsApiApplication jaxRsApiApplication() {
return new JaxRsApiApplication();
}
}
RestServiceBeanScanner class
public final class RestServiceBeanScanner {
private RestServiceBeanScanner() {
}
public static List<Object> scan(ApplicationContext applicationContext, String... basePackages) {
GenericApplicationContext genericAppContext = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(genericAppContext, false);
scanner.addIncludeFilter(new AnnotationTypeFilter(RestService.class));
scanner.scan(basePackages);
genericAppContext.setParent(applicationContext);
genericAppContext.refresh();
List<Object> restResources = new ArrayList<>(
genericAppContext.getBeansWithAnnotation(RestService.class).values());
return restResources;
}
}

Spring Boot Apache Camel Routes testing

I have a Springboot application, where I have some Camel routes configured.
public class CamelConfig {
private static final Logger LOG = LoggerFactory.getLogger(CamelConfig.class);
#Value("${activemq.broker.url:tcp://localhost:61616}")
String brokerUrl;
#Value("${activemq.broker.maxconnections:1}")
int maxConnections;
#Bean
ConnectionFactory jmsConnectionFactory() {
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(new ActiveMQConnectionFactory(brokerUrl));
pooledConnectionFactory.setMaxConnections(maxConnections);
return pooledConnectionFactory;
}
#Bean
public RoutesBuilder route() {
LOG.info("Initializing camel routes......................");
return new SpringRouteBuilder() {
#Override
public void configure() throws Exception {
from("activemq:testQueue")
.to("bean:queueEventHandler?method=handleQueueEvent");
}
};
}
}
I want to test this route from activemq:testQueue to queueEventHandler::handleQueueEvent.
I tried different things mentioned here http://camel.apache.org/camel-test.html, but doesn't seem to get it working.
I am trying to do something like this:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {CamelConfig.class, CamelTestContextBootstrapper.class})
public class CamelRouteConfigTest {
#Produce(uri = "activemq:testQueue")
protected ProducerTemplate template;
#Test
public void testSendMatchingMessage() throws Exception {
template.sendBodyAndHeader("testJson", "foo", "bar");
// Verify handleQueueEvent(...) method is called on bean queueEventHandler by mocking
}
But my ProducerTemplate is always null. I tried auto-wiring CamelContext, for which I get an exception saying it cannot resolve camelContext. But that can be resolved by adding SpringCamelContext.class to #SpringBootTest classes. But my ProducerTemplate is still null.
Please suggest. I am using Camel 2.18 and Spring Boot 1.4.
In Camel 2.22.0 and ongoing, which supports Spring Boot 2 you can use the following template to test your routes with Spring Boot 2 support:
#RunWith(CamelSpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
Route1.class,
Route2.class,
...
})
#EnableAutoConfiguration
#DisableJmx
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class RouteTest {
#TestConfiguration
static class Config {
#Bean
CamelContextConfiguration contextConfiguration() {
return new CamelContextConfiguration() {
#Override
public void beforeApplicationStart(CamelContext camelContext) {
// configure Camel here
}
#Override
public void afterApplicationStart(CamelContext camelContext) {
// Start your manual routes here
}
};
}
#Bean
RouteBuilder routeBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:someEndpoint").to("mock:done");
}
};
}
// further beans ...
}
#Produce(uri = "direct:start")
private ProducerTemplate template;
#EndpointInject(uri = "mock:done")
private MockEndpoint mockDone;
#Test
public void testCamelRoute() throws Exception {
mockDone.expectedMessageCount(1);
Map<String, Object> headers = new HashMap<>();
...
template.sendBodyAndHeaders("test", headers);
mockDone.assertIsSatisfied();
}
}
Spring Boot distinguishes between #Configuration and #TestConfiguration. The primer one will replace any existing configuration, if annotated on a top-level class, while #TestConfiguration will be run in addition to the other configurations.
Further, in larger projects you might run into auto-configuration issues as you can't rely on Spring Boot 2 to configure your custom database pooling or what not correctly or in cases where you have a specific directory structure and the configurations are not located within a direct ancestor directory. In that case it is proabably preferable to omit the #EnableAutoConfiguration annotation. In order to tell Spring to still auto-configure Camel you can simply pass CamelAutoConfiguration.class to the classes mentioned in #SpringBootTest
#SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
Route1.class,
Route2.class,
RouteTest.Config.class,
CamelAutoConfiguration.class
}
As no automatic configuration is performed, Spring won't load the test configuration inside your test class nor initialize Camel as well. By adding those configs to the boot classes manually Spring will do it for you.
For one route with MQ and Spring Boot like this:
#Component
public class InboundRoute extends RouteBuilder {
#Override
public void configure() {
JaxbDataFormat personDataFormat = new JaxbDataFormat();
personDataFormat.setContextPath(Person.class.getPackage().getName());
personDataFormat.setPrettyPrint(true);
from("direct:start").id("InboundRoute")
.log("inbound route")
.marshal(personDataFormat)
.to("log:com.company.app?showAll=true&multiline=true")
.convertBodyTo(String.class)
.inOnly("mq:q.empi.deim.in")
.transform(constant("DONE"));
}
}
I use adviceWith in order to replace the endpoint and use only mocks:
#RunWith(CamelSpringBootRunner.class)
#UseAdviceWith
#SpringBootTest(classes = InboundApp.class)
#MockEndpoints("mock:a")
public class InboundRouteCamelTest {
#EndpointInject(uri = "mock:a")
private MockEndpoint mock;
#Produce(uri = "direct:start")
private ProducerTemplate template;
#Autowired
private CamelContext context;
#Test
public void whenInboundRouteIsCalled_thenSuccess() throws Exception {
mock.expectedMinimumMessageCount(1);
RouteDefinition route = context.getRouteDefinition("InboundRoute");
route.adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() {
weaveByToUri("mq:q.empi.deim.in").replace().to("mock:a");
}
});
context.start();
String response = (String) template.requestBodyAndHeader("direct:start",
getSampleMessage("/SimplePatient.xml"), Exchange.CONTENT_TYPE, MediaType.APPLICATION_XML);
assertThat(response).isEqualTo("DONE");
mock.assertIsSatisfied();
}
private String getSampleMessage(String filename) throws Exception {
return IOUtils
.toString(this.getClass().getResourceAsStream(filename), StandardCharsets.UTF_8.name());
}
}
I use the following dependencies: Spring Boot 2.1.4-RELEASE and Camel 2.23.2. The complete source code is available on Github.
This is how I did this finally:
#RunWith(SpringRunner.class)
public class CamelRouteConfigTest extends CamelTestSupport {
private static final Logger LOG = LoggerFactory.getLogger(CamelRouteConfigTest.class);
private static BrokerService brokerSvc = new BrokerService();
#Mock
private QueueEventHandler queueEventHandler;
#BeforeClass
// Sets up an embedded broker
public static void setUpBroker() throws Exception {
brokerSvc.setBrokerName("TestBroker");
brokerSvc.addConnector("tcp://localhost:61616");
brokerSvc.setPersistent(false);
brokerSvc.setUseJmx(false);
brokerSvc.start();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new CamelConfig().route();
}
// properties in .yml has to be loaded manually. Not sure of .properties file
#Override
protected Properties useOverridePropertiesWithPropertiesComponent() {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
try {
PropertySource<?> applicationYamlPropertySource = loader.load(
"properties", new ClassPathResource("application.yml"),null);// null indicated common properties for all profiles.
Map source = ((MapPropertySource) applicationYamlPropertySource).getSource();
Properties properties = new Properties();
properties.putAll(source);
return properties;
} catch (IOException e) {
LOG.error("application.yml file cannot be found.");
}
return null;
}
#Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry jndi = super.createRegistry();
MockitoAnnotations.initMocks(this);
jndi.bind("queueEventHandler", queueEventHandler);
return jndi;
}
#Test
// Sleeping for a few seconds is necessary, because this line template.sendBody runs in a different thread and
// CamelTest takes a few seconds to do the routing.
public void testRoute() throws InterruptedException {
template.sendBody("activemq:productpushevent", "HelloWorld!");
Thread.sleep(2000);
verify(queueEventHandler, times(1)).handleQueueEvent(any());
}
#AfterClass
public static void shutDownBroker() throws Exception {
brokerSvc.stop();
}
}
Did you try using Camel test runner?
#RunWith(CamelSpringJUnit4ClassRunner.class)
If you are using camel-spring-boot dependency, you may know that it uses auto configuration to setup Camel:
CamelAutoConfiguration.java
It means that you may also need to add #EnableAutoConfiguration to your test.

Using test application.properties file with CamelSpringTestSupport in Spring Boot

Prerequisites
Apache Tomcat 7
Spring 4.1.5.RELEASE
Spring Boot 1.2.2.RELEASE
Apache Camel 2.15.1
Problem
I am Using Spring Boot with a configuration class which is also used by EndpointSetup.
#SpringBootApplication
#Import({MyConfiguration.class, EndpointSetup.class})
public class MyFatJarRouter extends FatJarRouter { ... }
#Configuration
#ConfigurationProperties(prefix = "camel.route", ignoreUnknownFields = false)
public class MyConfiguration {
private List<String> brokerUrl = new ArrayList<>();
public List<String> getBrokerUrl() {return brokerUrl;}
public void setBrokerUrl(List<String> brokerUrl) {this.brokerUrl = brokerUrl;}
}
In production properties will be read from conf/application.properties by default.
I want to test my routes via CamelSpringTestSupport
So I have tried following:
I have placed a application.properties under test/resources/config/application.properties (--> in classpath of test)
then wrote following:
public class MyJmsTest extends CamelSpringTestSupport {
#Override
protected AbstractApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext(MyFatJarRouter.class);
}
#Test
public void myTest() throws Exception {
...
}
}
In the example above the configuration is not read from the application.properties placed in test folder.
How can I read a test specific config file in my CamelSpringTestSupport Unit-Test?
I may be little late in answering, but there is a better way than hacking endpoints. The following solution uses toD introduced in Camel 2.16. I wrote a custom component "github" (there's an official one as well), and the following is how I test it. Note that I'm not using a single Camel proprietary annotation. To inject properties, I can either use the properties attribute in #SpringBootTest, or any of the other standard techniques available in Spring Boot.
Note that I'm using $simple{...} to avoid clash with Spring property resolution.
<rant>
And yes, Camel documentation sucks! They write it like release notes, with a section dedicated to each release, and don't seem to update the doc to keep up with the latest versions (the following technique is not documented). Imagine going to a restaurant and asking for the special, only to be told by the server about the special for the day before, and the week before, and so on. How about versioning the doc instead?
</rant>
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
public class GitHubRouteTest {
#Autowired
private CamelContext camelContext;
#Autowired
private ProducerTemplate template;
#Autowired
private GitHubClient gitHubClient;
#Test
public void testGitHubClientInvoked() throws InterruptedException {
template.sendBodyAndHeader("direct:start", "whatever",
"endpoint", "commits/test/test?username=test&password=test");
verify(gitHubClient).getCommitsForARepo(eq("test"), eq("master"), eq("test"), eq(20));
}
#SpringBootApplication
public static class TestApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(TestApplication.class)
.web(false)
.run(args);
}
#Bean
public RouteBuilder testRoute() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.toD("github:$simple{in.header.endpoint}");
}
};
}
#Bean
public GitHubClient mockGitHubClient() {
GitHubClient mock = Mockito.mock(GitHubClient.class);
return mock;
}
}
}
I solved it by using standard spring unit-tests like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#ActiveProfiles("test") // Load applicaton-test.properties in test/resources/config/application-test.properties
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) // cleanup spring context because jms broker does not exit properly
public class MyJmsTest {
private static final String MOCK_MY_ENDPOINT = "mock:myEndpoint";
#Autowired
CamelContext context;
#Autowired
ApplicationContext applicationContext;
#Autowired
ProducerTemplate producerTemplate;
#Before
public void configureMocks() throws Exception {
context.getRouteDefinition("MyRoute")
.adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveByToString(".*myEndPointId.*")
.replace()
.to(MOCK_MY_ENDPOINT);
}
});
final MockEndpoint endpoint = context.getEndpoint(MOCK_MY_ENDPOINT, MockEndpoint.class);
endpoint.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
InputStream inStream = getClass().getClassLoader().getResourceAsStream("xml/my.xml");
String in = context.getTypeConverter().convertTo(String.class, inStream);
exchange.getIn().setBody(in);
}
});
}
#Test
public void synchronousCallBasic_1() throws Exception {
final MyConfiguration MyConfiguration = applicationContext.getBean(MyConfiguration.class);
final String myMessageBody =
context.getTypeConverter().convertTo(String.class, getClass().getClassLoader()
.getResourceAsStream("xml/0010_example.xml"));
final Object myResult = producerTemplate.requestBody(MyConfiguration.getActiveMqSynchronousEndpointUri(), myMessageBody);
assertThat(myResult, notNullValue());
assertThat((String)myResult, is("<example>1</example>"));
}
}
I solved this issue, with a lot of annotation which I found here, and now the test properties are correctly injected:
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#ActiveProfiles("test")
#EnableAutoConfiguration
#ComponentScan
#ContextConfiguration()
public class MessageDeliveryTest{
}
Also, the test properties file needs to be named application-{env}.properties, where "env" is the profile used here. For eg. for test the properties file should be application-test.properties

Instantiate different cache manager in each Test Class

In my Spring-Boot Web Application project I'm using Spring Cache to implement caching. Cache can be enabled/disabled by configuration key defined in application.yml. I already have existing test cases where tests are written assuming there is no cache. So by default in my integration-test profile caching is disabled and I initialize NoOpCacheManager and all my tests work.
#Profile(value = { "default", "production", "integration-test" })
#Configuration
#EnableCaching(mode = AdviceMode.ASPECTJ)
public class CacheBeanConfig extends CachingConfigurerSupport {
#Autowired
private CacheConfig cacheConfig;
#Bean
#Override
public CacheManager cacheManager() {
if (cacheConfig.isEnabled()) {
System.out.println("****************Couchbase CacheBeanTestsConfig cache init.**********************");
Map<String, DeclaraCouchbaseTemplate> cacheCouchTemplateMap = Maps.newHashMap();
Map<String, Integer> cacheTtlMap = Maps.newHashMap();
for (CacheConfig.CacheConfigParam cacheParam : cacheConfig.getCaches()) {
try {
cacheCouchTemplateMap.put(cacheParam.getName(),
couchbaseManager.getCouchbaseTemplate(cacheParam.getName()));
cacheTtlMap.put(cacheParam.getName(), cacheParam.getTtl());
} catch (IOException | URISyntaxException e) {
throw new FaultException("Unable to get couchbase template.");
}
}
return new CouchbaseCacheManager(cacheCouchTemplateMap, cacheTtlMap, metricRegistry);
} else {
System.out.println("****************NoOp CacheBeanTestsConfig cache init.**********************");
NoOpCacheManager noopCacheManager = new NoOpCacheManager();
return noopCacheManager;
}
}
}
I also want to write tests for verification of Caching functionality. I created a CachedControllerTest class where all the cache specific tests are written.
Problem is when I run
mvn test -Dtest=CachedControllersTest -Dspring.profiles.active=integration-test
All the tests in CachedControllerTest class are failing because cache manager is initialized with NoOpCacheManager even though I enabled the caching in the bean function.
I tried to create a separate profile for CachedControllerTest and it still fails because once cacheManager bean is initialized it is not getting reset.
mvn test -Dtest=CachedControllersTest -Dspring.profiles.active=integration-test,integration-cache-test
Here is my CachedControllerTest class
#ActiveProfiles("integration-cache-test")
#DirtiesContext
public class CachedControllersTest extends AbstractRestControllerTest {
#Configuration
#EnableCaching(mode = AdviceMode.ASPECTJ)
#Profile("integration-cache-test")
public static class CachedControllerTestsBeanConfig {
#Autowired
private CouchbaseManager couchbaseManager;
#Autowired
private CacheConfig cacheConfig;
#Autowired
private MetricRegistry metricRegistry;
#Autowired
GlobalApplicationConfig globalAppConfig;
#Bean
public CacheManager cacheManager() {
System.out.println("**************** CachedControllerTestsBeanConfig EnabledCaching**********************");
cacheConfig.setEnabled(true);
if (cacheConfig.isEnabled()) {
System.out.println("****************Couchbase CachedControllerTestsBeanConfig cache init.**********************");
Map<String, DeclaraCouchbaseTemplate> cacheCouchTemplateMap = Maps.newHashMap();
Map<String, Integer> cacheTtlMap = Maps.newHashMap();
for (CacheConfig.CacheConfigParam cacheParam : cacheConfig.getCaches()) {
try {
cacheCouchTemplateMap.put(cacheParam.getName(),
couchbaseManager.getCouchbaseTemplate(cacheParam.getName()));
cacheTtlMap.put(cacheParam.getName(), cacheParam.getTtl());
} catch (IOException | URISyntaxException e) {
throw new FaultException("Unable to get couchbase template.");
}
}
return new CouchbaseCacheManager(cacheCouchTemplateMap, cacheTtlMap, metricRegistry);
} else {
System.out.println("****************NoOp CachedControllerTestsBeanConfig cache init.**********************");
NoOpCacheManager noopCacheManager = new NoOpCacheManager();
return noopCacheManager;
}
}
#Bean(name = "mtlKeyGenerator")
public KeyGenerator keyGenerator() {
System.out.println("****************CachedControllerTestsBeanConfig mtlKeyGenerator.**********************");
return new MultiTenantKeyGenerator(globalAppConfig.getTenantId());
}
#Bean(name = CacheManagementConfigUtils.CACHE_ASPECT_BEAN_NAME)
#Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AnnotationGroupCacheAspect cacheAspect() {
AnnotationGroupCacheAspect cacheAspect = AnnotationGroupCacheAspect.aspectOf();
CacheManager cacheManager = (CacheManager) StaticContextHolder.getApplicationContext().getBean("cacheManager");
cacheAspect.setCacheManager(cacheManager);
KeyGenerator keyGenerator = (KeyGenerator) StaticContextHolder.getApplicationContext().getBean("mtlKeyGenerator");
cacheAspect.setKeyGenerator(keyGenerator);
return cacheAspect;
}
}
#Component
public static class StaticContextHolder implements ApplicationContextAware {
private static ApplicationContext appContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
appContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return appContext;
}
}
}
application.yml
spring:
profiles: integration-test
cache:
enabled: false
---
spring:
profiles: integration-cache-test
cache:
enabled: false
My requirement is to reinitialize the cacheManage for each Test Class and CacheConfig is the bean which I want to modify at the runtime so that appropriate CacheManager can be initialized.
In isolation if I run the CachedControllerTest class tests they all pass because there is no other Test Class run before that which would have initialized the cacheManager to NoOpCacheManager.
Thanks in advance for any help/suggestion to make this situation work.
Edit 1
Based on the suggestion by Sam, Added #ActiveProfiles.
Edit 2
AbstractRestControllerTest Class Definition
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class AbstractRestControllerTest {
}
#Profile has zero effect on a test class.
To set the active bean definition profiles for an integration test, you need to use #ActiveProfiles from the spring-testmodule.
Consult the Context configuration with environment profiles section of the Spring Reference Manual for details.
Also, CachedControllerTestsBeanConfig must be annotated with #Configuration not #Component.
Regards,
Sam (author of the Spring TestContext Framework)

Resources