In this article, we will see an example of spring annotation ActiveProfiles
. ActiveProfiles
is used to to specify the active profiles that should be used when building the application context. In our example, we will show how to make use of @ActiveProfiles
to simply switch testing from data based on database to an in-memory source.
We will first run our test on database then on in-memory.
Define DataSource
Let’s first define the datasource. We are using mysql, below is the data source bean. Note that it is annotated with @Profile
set to ‘prod’ (production).
DataSourceConfig:
package com.javarticles.spring; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; @Configuration @Profile("prod") public class DataSourceConfig { @Bean public DataSource dataSource() { DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost/test"); ds.setUsername("root"); ds.setPassword("mnrpass"); return ds; } @Bean public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dataSource()); } }
Here is the employee bean.
Employee:
package com.javarticles.spring; public class Employee { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
EmployeeExtractor:
package com.javarticles.spring; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ResultSetExtractor; public class EmployeeExtractor implements ResultSetExtractor { public Employee extractData(ResultSet rs) throws SQLException, DataAccessException { rs.next(); Employee employee = new Employee(); employee.setName(rs.getString("name")); return employee; } }
We want to find an employee by name. We will be testing the below API using a test.
EmployeeFinder:
package com.javarticles.spring; public interface EmployeeFinder { Employee findEmployeeByName(String name); }
Here is an implementation of employee finder that relies on database.
EmployeeFinderImpl:
package com.javarticles.spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; public class EmployeeFinderImpl implements EmployeeFinder { @Autowired private JdbcTemplate jdbcTemplate; public Employee findEmployeeByName(String name) { System.out.println("Find emp from db"); return jdbcTemplate.query("select * from employee where name = ?", new EmployeeExtractor(), name); } }
Below test class has @ActiveProfiles
set to’prod’ so by default it runs tests that uses data from database.
We also have another set of configuration that relies on in-memory. We will see how one can switch the datasource from a database driven test to an in-memory driven test. The in-memory based test overrides “employeeFinder” bean by providing an in-memory implementation using HashMap
.
EmployeeTests:
package com.javarticles.spring; import java.util.HashMap; import java.util.Map; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @ActiveProfiles("prod") public class EmployeeTests { @Autowired private EmployeeFinder employeeFinder; @Test public void findEmployee() { Employee emp = employeeFinder.findEmployeeByName("Sam"); Assert.assertNotNull(emp); Assert.assertEquals("Sam", emp.getName()); } @Profile("prod") @Configuration @ComponentScan("com.javarticles.spring") @Sql({ "drop_schema.sql", "schema.sql", "data.sql" }) static class ProdConfig { @Bean EmployeeFinder employeeFinder() { return new EmployeeFinderImpl(); } } @Profile("in-memory") @Configuration @ComponentScan("com.javarticles.spring") static class TestConfig { static Map<String, Employee> empMap = new HashMap<String, Employee>(); static { Employee emp1 = new Employee(); emp1.setName("Sam"); empMap.put("Sam", emp1); Employee emp2 = new Employee(); emp2.setName("Joe"); empMap.put("Joe", emp2); } @Bean EmployeeFinder employeeFinder() { return new EmployeeFinder() { public Employee findEmployeeByName(String name) { System.out.println("Find emp from memory"); return empMap.get(name); } }; } } }
When you run the case, the database based employee finder fetches the employee.
Output:
Feb 11, 2016 11:09:48 PM org.springframework.test.context.support.AbstractContextLoader generateDefaultLocations INFO: Could not detect default resource locations for test class [com.javarticles.spring.EmployeeTests]: no resource found for suffixes {-context.xml}. Feb 11, 2016 11:09:48 PM org.springframework.test.context.support.AbstractDelegatingSmartContextLoader processContextConfiguration INFO: AnnotationConfigContextLoader detected default configuration classes for context configuration [[email protected] declaringClass = 'com.javarticles.spring.EmployeeTests', classes = '{class com.javarticles.spring.EmployeeTests$ProdConfig, class com.javarticles.spring.EmployeeTests$TestConfig}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader']. Feb 11, 2016 11:09:48 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames INFO: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] Feb 11, 2016 11:09:48 PM org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners INFO: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext] Feb 11, 2016 11:09:48 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners INFO: Using TestExecutionListeners: [org.springframework.test[email protected]7a79be86, org.springframewor[email protected]34ce8af7, org.springfra[email protected]b684286, org.springframew[email protected]880ec60, org.sp[email protected]3f3afe78] Feb 11, 2016 11:09:48 PM org.springframework.context.support.GenericApplicationContext prepareRefresh INFO: Refreshing [email protected]61f5df: startup date [Thu Feb 11 23:09:48 IST 2016]; root of context hierarchy Feb 11, 2016 11:09:48 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName INFO: Loaded JDBC driver: com.mysql.jdbc.Driver Find emp from db Feb 11, 2016 11:09:49 PM org.springframework.context.support.GenericApplicationContext doClose INFO: Closing [email protected]61f5df: startup date [Thu Feb 11 23:09:48 IST 2016]; root of context hierarchy
Switch profile using @ActiveProfiles
We will now extend the above database based test and then override the @ActiveProfiles
declared to use ‘in-memory’ based beans.
InMemoryBasedEmpTests:
package com.javarticles.spring; import org.springframework.test.context.ActiveProfiles; @ActiveProfiles("in-memory") public class InMemoryBasedEmpTests extends EmployeeTests { }
When you re-run the above test class, you will see that the in-memory based employee finder is invoked instead of the databased based finder.
Output:
Feb 11, 2016 11:08:48 PM org.springframework.test.context.support.AbstractContextLoader generateDefaultLocations INFO: Could not detect default resource locations for test class [com.javarticles.spring.EmployeeTests]: no resource found for suffixes {-context.xml}. Feb 11, 2016 11:08:48 PM org.springframework.test.context.support.AbstractDelegatingSmartContextLoader processContextConfiguration INFO: AnnotationConfigContextLoader detected default configuration classes for context configuration [[email protected] declaringClass = 'com.javarticles.spring.EmployeeTests', classes = '{class com.javarticles.spring.EmployeeTests$ProdConfig, class com.javarticles.spring.EmployeeTests$TestConfig}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader']. Feb 11, 2016 11:08:48 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getDefaultTestExecutionListenerClassNames INFO: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] Feb 11, 2016 11:08:48 PM org.springframework.test.context.support.DefaultTestContextBootstrapper instantiateListeners INFO: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext] Feb 11, 2016 11:08:48 PM org.springframework.test.context.support.DefaultTestContextBootstrapper getTestExecutionListeners INFO: Using TestExecutionListeners: [org.springframework.test[email protected]34ce8af7, org.springframewor[email protected]b684286, org.springfra[email protected]880ec60, org.springframew[email protected]3f3afe78, org.sp[email protected]7f63425a] Feb 11, 2016 11:08:48 PM org.springframework.context.support.GenericApplicationContext prepareRefresh INFO: Refreshing [email protected]51a94: startup date [Thu Feb 11 23:08:48 IST 2016]; root of context hierarchy Find emp from memory
Download the source code
This was an example about spring @ActiveProfiles annotation.