Spring Boot : Integration tests with environment variables

Omkar Birade
Nov 7 · 4 min read

If you want to read about setting up an EmailService which reads the credentials from environment variables please refer to my article on:

Spring Boot : Configuring environment variables

Writing integration tests for an EmailService

Now that our code works, we want to write an integration test for EmailService.

It can be done using GreenMail which creates a SMTP server for us and receives all the emails we send through our test.

You can read about about GreenMail here:

So let’s see how our EmailServiceTest class looks like:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
public class EmailServiceTest {
@Autowired
private EmailService emailService;
private GreenMail greenMail;

And our application-test.properties looks like this:

SMTP_SERVER_PORT=3025
SMTP_SERVER_HOST=localhost
SMTP_USER_NAME=user@localhost.com
SMTP_USER_PASSWORD=password

But the above test will fail saying invalid credentials as @Value will pick the environment variables with real SMTP server credentials and not our set credentails for GreenMail.

co.interleap.controllers.EmailServiceTest > shouldSendAnEmail FAILED
java.lang.AssertionError at EmailServiceTest.java:52

One way to get around this is to make sure that the jar is not provided with these environment variables, which sounds like a little unnecessary hassle at-least in this case.


What is the best way to get around this problem then?

Spring Boot comes to rescue us here with it’s annotations.

We can use an annotation called @TestPropertySource to make our SMTP server use the right credentials.

TestPropertySource

@TestPropertySource is a class-level annotation that is used to configure the locations() of properties files and inlined properties() to be added to the Environment's set of PropertySources for an ApplicationContext for integration tests.

Test property sources have higher precedence than those loaded from the operating system’s environment or Java system properties as well as property sources added by the application declaratively via @PropertySource or programmatically (e.g., via an ApplicationContextInitializer or some other means).

Thus, test property sources can be used to selectively override properties defined in system and application property sources. Furthermore, inlined properties() have higher precedence than properties loaded from resource locations().

What this means is any property that is declared using@TestPropertySource will overwrite the properties picked externally. In our case, environment variables having our SMTP_SERVER credentials.

Properties

public abstract String[] properties

Inlined properties in the form of key-value pairs that should be added to the Spring Environment before the ApplicationContext is loaded for the test. All key-value pairs will be added to the enclosing Environment as a single test PropertySource with the highest precedence.

So in our case all we need to ensure that our test runs correctly is annotate the test class with this annotation and declare the SMTP configuration inside :

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {"SMTP_SERVER_PORT=3025",
"SMTP_SERVER_HOST=localhost",
"SMTP_USER_NAME=user@localhost.com",
"SMTP_USER_PASSWORD=password"})
@ActiveProfiles("test")
class EmailServiceTest {
/*
Your tests
*/
}

If you have multiple properties you want to overwrite, you can just create another or use any existing application.properties file and mention it in locations attribute of @TestPropertySource :

Once we are done with these changes, we can see our test passes even when it has access to the environment variables.

Our code will look like this once we use application-test.properties in locations attribute:

EmailServiceTest.java

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = {"classpath:application-test.properties"})
@ActiveProfiles("test")
class EmailServiceTest {
/*
Your tests
*/
}

application-test.properties

SMTP_SERVER_PORT=3025
SMTP_SERVER_HOST=localhost
SMTP_USER_NAME=user@localhost.com
SMTP_USER_PASSWORD=password

Our Final Code

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(locations = {"classpath:application-test.properties"},properties = "management.port=0")
@ActiveProfiles("test")
public class EmailServiceTest {
@Autowired
private EmailService emailService;
private GreenMail greenMail;

application-test.properties

SMTP_SERVER_PORT=3025
SMTP_SERVER_HOST=localhost
SMTP_USER_NAME=user@localhost.com
SMTP_USER_PASSWORD=password

And now the tests will work as expected.

You can find the EmailService.java and application-test.properties file here:


Hope you learned something interesting.

Happy Coding!!!

Omkar Birade

Written by

Software developer at Interleap who likes to solve trivial to complex problems in most creative ways.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade