Spring @ActiveProfiles Annotation Example

0

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.

You can download the source code here: springActiveProfilesAnnotationExample.zip
Share.

Comments are closed.