My Avatar

Shilong ZHAO

Unit Test in Spring Boot with Profiles

2017-03-24 00:00:00 +0100

In case you have any questions or suggestions, you can leave comments HERE . Thanks!

Usually during the process of creating a Spring Web application, the environments for developing, testing and deployment could be very different. For example, we might use an in-memory database, like HSQL, for test purposes and use MySql for development.

Instead of changing the configurations every time we switch the environment, Spring Boot allows us to define different property files and switch among these environments with no pain.

This post is mainly derived from mkyong.com, but there has been some modifications in order to be used in web applications.

The project structure is shown in the following picture:

The project needs to include the Spring Boot dependencies and the pom.xml is:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>it.dreamhack.spring</groupId>
    <artifactId>spring-boot-profiles</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.4.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <warSourceDirectory>src/main/</warSourceDirectory>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Note for the parent we use spring-boot-starter-parent.

And we have three application property files: application.properties, application-sunny.properties and application-raining.properties;

The setting of application.properties:

spring.profiles.active=raining
logging.level.=error
spring.main.banner-mode=off

spring.profiles.active specifies which profile is active and it si raining in this case;

For application-sunny.properties, the forecaster is set as tester, which means in profile sunny, the value of ${weatherservice.forecaster} is tester.

weatherservice.forecaster = tester

And similarly we define the forecaster in raining profile as developer:

weatherservice.forecaster = developer

Now we define the service interface WeatherService.java:

package it.dreamhack.service;

public interface WeatherService {
    String forecast();
}

And two interface implementations, SunnyDayService and RainingDayService, respectively:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
@Profile({"sunny", "default"})
public class SunnyDayService implements WeatherService {
    @Value("${weatherservice.forecaster}")
    private String forecaster;
    public String forecast() {
        return forecaster + " today is a sunny day!";
    }
}

In this way, depending on which profile is active, different WeatherService beans will be created: if raining the RainingDayService is created and SunnyDayService otherwise.

package it.dreamhack.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@Service
@Profile("raining")
public class RainingDayService implements WeatherService {
    @Value("${weatherservice.forecaster}")
    private String forecaster;
    public String forecast() {
        return forecaster + " today is a raining day!";
    }
}

For a real project, we might want to test our application in different profiles like dev, test, and prod (there configurations could be very different). In such case, all we need to do is create separate property files for them, namely application-{profilename}.properties. And active corresponding profile when we need to specify one.

That’s what we do in our tests:

package it.dreamhack.test;

import it.dreamhack.ApplicationContextConfig;
import it.dreamhack.service.WeatherService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("raining")
@SpringBootTest(classes = {ApplicationContextConfig.class})
public class RainingWeatherServiceTest {
    @Autowired
    private WeatherService service;
    @Test
    public void testWeatherService() {
        System.out.println(service.forecast());
        assert service.forecast().equals("developer today is a raining day!");
    }
}

And

package it.dreamhack.test;

import it.dreamhack.ApplicationContextConfig;
import it.dreamhack.service.WeatherService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("sunny")
@SpringBootTest(classes = {ApplicationContextConfig.class})
public class SunnyWeatherServiceTest {
    @Autowired
    private WeatherService service;
    @Test
    public void testWeatherService() {
        System.out.println(service.forecast());
        assert service.forecast().equals("tester today is a sunny day!");
    }
}

The annotation @ActiveProfiles let us to specify which profile or which setting we want to use.

And the Application Context is defined as

package it.dreamhack;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
Configuration ComponentScan("it.dreamhack.service")
public class ApplicationContextConfig {

}