Spring Boot Profiles + Testing Basics
- What are Spring profiles?
- How to activate a profile?
- @Profile annotation
- application.properties vs application-dev.properties
- YAML profile support
- @ActiveProfiles in tests
- What is @SpringBootTest?
- @WebMvcTest vs @DataJpaTest vs @SpringBootTest
- Mockito basics
- @MockBean vs @Mock
- How to test REST controllers?
- Test JPA repositories
- Mock external services
- AssertJ vs JUnit assertions
- Best practices for Spring Boot Profiles + Testing Basics
What are Spring profiles?
=> Spring Profiles allow environment-specific configurations (dev, test, prod) via profile-specific files like application-dev.properties
1. Profile-Specific Files
=> application.properties — default (always loaded)
=> application-dev.properties — for dev profile
=> application-prod.properties — for prod profile
=> application-test.yml — YAML also supported
Example :
# application.properties (default)
server.port=8080
# application-dev.properties
server.port=8081
spring.datasource.url=jdbc:h2:mem:devdb
# application-prod.properties
server.port=80
spring.datasource.url=jdbc:postgresql://prod-db:5432/appdb
2. Activating a Profile
Command line (most common)
java -jar app.jar --spring.profiles.active=dev
application.properties
spring.profiles.active=dev
Environment variable
export SPRING_PROFILES_ACTIVE=prod
In tests
@SpringBootTest
@ActiveProfiles("test")
class MyTest { ... }
3. @Profile Annotation
Beans or methods active only in specific profiles
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource devDataSource() {
return new H2DataSource();
}
}
4. Multiple Profiles
--spring.profiles.active=dev,featureX
=> Loads application-dev.properties + application-featureX.properties
5. YAML Profiles
# application.yml
spring:
profiles:
active: dev
---
spring:
profiles: dev
server:
port: 8081
Best Practice
=> Default (application.properties) for common settings
=> Profile-specific files for overrides
=> Use in tests with @ActiveProfiles("test")
=> Production: Activate via env variables or Kubernetes ConfigMaps
How to activate a profile?
1. Command Line Argument (Most common in production/dev)
Bash
java -jar your-app.jar --spring.profiles.active=dev,test
2. In application.properties / application.yml (Good for local dev)
properties
spring.profiles.active=dev
3. Environment Variable (Best for Docker/Kubernetes)
Bash
export SPRING_PROFILES_ACTIVE=prod
java -jar your-app.jar
or in Docker:
YAML
environment:
- SPRING_PROFILES_ACTIVE=prod
4. Programmatically (Rare – in main method)
Java
SpringApplication.setAdditionalProfiles("dev");
SpringApplication.run(MyApp.class, args);
5. In Tests (Very common)
Java
@SpringBootTest
@ActiveProfiles("test")
class MyTest { ... }
Summary
=> Profiles are activated via command line (--spring.profiles.active=dev), application.properties (spring.profiles.active=dev), environment variable (SPRING_PROFILES_ACTIVE), or @ActiveProfiles in tests.
=> Command line has highest priority.
@Profile annotation
=> @Profile is a Spring annotation that conditionally includes or excludes beans, configurations, or components based on the active profile(s) at runtime
1. On a Bean / Configuration Class
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
=> This DataSource is created only when dev profile is active
2. On Individual Bean Methods
@Bean
@Profile("prod")
public DataSource prodDataSource() {
return new DataSourceBuilder()
.url("jdbc:postgresql://prod-db:5432/app")
.build();
}
3. On Component / Service / Controller
@Service
@Profile("test")
public class TestDataService {
// Only active in "test" profile
}
Multiple Profiles
@Profile({"dev", "local"}) // Active if either dev or local is active
Negation (Exclude Profile)
@Profile("!prod") // Active when prod is NOT active
application.properties vs application-dev.properties
=> application.properties is the default config loaded always.
=> application-dev.properties is profile-specific, loaded only when spring.profiles.active=dev, and overrides defaults. This allows environment-specific settings (e.g., H2 for dev, PostgreSQL for prod) without code changes
YAML profile support
=> We can use either application.properties or application.yml file for Spring Profiles
=> application.yml is more structured and readable when compared to application.properties
@ActiveProfiles in tests
=> @ActiveProfiles is a Spring Boot annotation used in test classes to specify which profile(s) should be activated during the test execution
=> It is used with @SpringBootTest
=> It tells Spring to load the environment-specific configuration for that test
=> Multiple profiles can be specified as array
=> Simulate different environments (test, dev, prod) in unit/integration tests
=> Load correct database, ports, logging levels, or beans for the test
=> Avoid using production config in tests (e.g., real DB vs in-memory H2)
1. Basic Example (Single Profile)
@SpringBootTest
@ActiveProfiles("test") // Loads application-test.properties or application-test.yml
class EmployeeServiceTest {
@Autowired
private EmployeeService service;
@Test
void testCreateEmployee() {
// Test runs with test profile config
}
}
2. Multiple Profiles
@SpringBootTest
@ActiveProfiles({"test", "integration"})
class IntegrationTest { ... }
=> Loads application-test.properties + application-integration.properties (last one wins for conflicts).
3. On Test Class or Method (Method-level overrides class-level)
@SpringBootTest
@ActiveProfiles("test")
class MyTests {
@Test
@ActiveProfiles("prod") // Overrides class-level for this test only
void prodSpecificTest() { ... }
}
What is @SpringBootTest?
=> @SpringBootTest loads the full Spring Boot application context for integration tests, including auto-configuration, beans, and properties
=> Starts the full context (like running the app) but in test mode (uses test properties if present)
=> Useful for end-to-end testing
=> Customize with classes, webEnvironment, properties, or @ActiveProfiles
=> It's heavier than @WebMvcTest or @DataJpaTest, so use only when full context is needed
Common Attributes
| Attribute | Default | Description |
|---|---|---|
classes | Main class | Specify main class (e.g., @SpringBootTest(classes = DemoApplication.class)) |
webEnvironment | MOCK | MOCK: Mock servlet environment RANDOM_PORT: Real server on random port DEFINED_PORT: Real server on defined port NONE: No web environment |
properties | None | Override properties for the test (e.g., properties = {"spring.datasource.url=jdbc:h2:mem:testdb"}) |
@ActiveProfiles | None | Activate specific profiles (e.g., @ActiveProfiles("test")) |
When to Use
=> Integration tests (full context: controllers, services, repositories, DB)
=> Testing auto-configuration, beans, properties
=> End-to-end flow (controller → service → repository)
When NOT to Use (Lighter Alternatives)
=> @WebMvcTest: Only web layer (controllers, no DB)
=> @DataJpaTest: Only JPA layer (repositories, no web)
=> @SpringBootTest is heavy — use only when you need the full context
@WebMvcTest vs @DataJpaTest vs @SpringBootTest
=> @WebMvcTest: Only web layer (controllers, no DB)
=> @DataJpaTest: Only JPA layer (repositories, no web)
=> @SpringBootTest is heavy — use only when you need the full context
| Annotation | What it loads | Scope / Loaded Components | Best For | Speed (Relative) | When to Use |
|---|---|---|---|---|---|
| @WebMvcTest | Only web layer (controllers, filters, message converters, etc.) | @Controller, @RestController, WebMvcConfigurer, HandlerMappings, etc. | Testing controllers (REST endpoints) | Fastest | Unit test controllers with MockMvc, mock services/repositories |
| @DataJpaTest | Only JPA layer (repositories, entities, DataSource, EntityManager) | @Entity, JpaRepository, DataSource, Hibernate, etc. | Testing repositories and JPA queries | Very Fast | Unit/integration test repositories, custom queries, JPA behavior |
| @SpringBootTest | Full application context (everything: controllers, services, repositories, config, etc.) | Entire Spring Boot app (like running the real app) | Full integration/end-to-end tests | Slowest | When you need the complete app (e.g., full flow, multiple layers) |
Quick Summary Table
| Feature | @WebMvcTest | @DataJpaTest | @SpringBootTest |
|---|---|---|---|
| Loads full context? | No | No | Yes |
| Loads controllers? | Yes | No | Yes |
| Loads repositories/JPA? | No | Yes | Yes |
| Loads services? | No | No | Yes |
| MockMvc available? | Yes | No | Yes (with webEnvironment) |
| Use @MockBean? | Yes (for services) | Yes (for external services) | Yes |
| Best for | Controller unit tests | Repository/JPA unit tests | Full integration tests |
| Speed | Fast | Fast | Slow |
Examples :
@WebMvcTest (Controller only)
@WebMvcTest(HelloController.class)
class HelloControllerTest {
@Autowired private MockMvc mockMvc;
@MockBean private HelloService service;
@Test
void testHello() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("Hello"));
}
}
@DataJpaTest (JPA only)
@DataJpaTest
class EmployeeRepositoryTest {
@Autowired private EmployeeRepository repo;
@Test
void testFindByName() {
Employee emp = new Employee("Virat");
repo.save(emp);
assertNotNull(repo.findByName("Virat"));
}
}
@SpringBootTest (Full app)
@SpringBootTest
class FullIntegrationTest {
@Autowired private EmployeeController controller;
@Test
void testFullFlow() {
// Test from controller to DB
}
}
Mockito basics
=> Mockito is the most popular Java mocking framework used for unit testing in Spring Boot applications
=> It allows you to create mock objects (fake versions of dependencies) to isolate the class under test from external dependencies (services, repositories, external APIs)
=> @Mock – Fake object that simulates behavior of a real dependency
=> @InjectMocks – Automatically inject mocks into the class under test
Step-by-Step Basics (with Code)
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class) // Enables Mockito annotations
class EmployeeServiceTest {
@Mock // Create mock dependency
private EmployeeRepository repository;
@InjectMocks // Inject mock into the class under test
private EmployeeService service;
@Test
void testFindEmployeeById() {
// Stub: Define what mock returns
Employee mockEmployee = new Employee(1, "Sachin", 800000);
when(repository.findById(1L)).thenReturn(Optional.of(mockEmployee));
// Call the method under test
Employee result = service.findById(1L);
// Assert
assertEquals("Sachin", result.getName());
// Verify: Check if repository method was called
verify(repository, times(1)).findById(1L);
}
@Test
void testSaveEmployee() {
Employee emp = new Employee(null, "Virat", 500000);
// Stub save to return saved employee with ID
when(repository.save(any(Employee.class))).thenAnswer(invocation -> {
Employee e = invocation.getArgument(0);
e.setId(2L); // Simulate DB generated ID
return e;
});
Employee saved = service.save(emp);
assertEquals(2L, saved.getId());
verify(repository, times(1)).save(emp);
}
}
Key Mockito Methods (Most Used)
| Method | Purpose | Example |
|---|---|---|
when(mock.method()).thenReturn(value) | Stub method return value | when(repo.findById(1L)).thenReturn(emp) |
when(mock.method()).thenThrow(ex) | Stub method to throw exception | when(service.callApi()).thenThrow(new RuntimeException()) |
verify(mock, times(n)).method() | Verify method called exactly n times | verify(repo, times(1)).save(emp) |
verify(mock).method(arg) | Verify called with specific argument | verify(repo).findByName("Virat") |
any() / any(Class) | Match any argument | when(repo.save(any())).thenReturn(emp) |
@InjectMocks | Auto-injects mocks into tested class | @InjectMocks private EmployeeService service; |
@Mock | Create mock object | @Mock private EmployeeRepository repo; |
Best Practices
=> Use @ExtendWith(MockitoExtension.class) for JUnit 5
=> Use @MockBean in @SpringBootTest to replace real beans
@MockBean vs @Mock
=> @Mock is plain Mockito for creating mocks in unit tests (no Spring context)
=> @MockBean is Spring Boot's annotation that replaces a real bean with a mock in the application context — used in integration tests with @SpringBootTest or @WebMvcTest
When to Choose Which
=> Use @Mock → For pure unit tests where you don't load Spring context (faster, no overhead)
=> Use @MockBean → For integration tests where you need the real Spring context but want to mock a specific bean (e.g., mock external service or repository)
Example :
@Mock (Unit Test – No Spring)
@ExtendWith(MockitoExtension.class)
class EmployeeServiceTest {
@Mock private EmployeeRepository repo;
@InjectMocks private EmployeeService service;
@Test
void testFindById() {
when(repo.findById(1L)).thenReturn(Optional.of(new Employee()));
assertNotNull(service.findById(1L));
}
}
@MockBean (Integration Test – With Spring)
@SpringBootTest
class EmployeeControllerTest {
@Autowired private MockMvc mockMvc;
@MockBean private EmployeeService service; // Real service replaced with mock
@Test
void testGetEmployee() throws Exception {
when(service.findById(1L)).thenReturn(new Employee());
mockMvc.perform(get("/employees/1"))
.andExpect(status().isOk());
}
}
How to test REST controllers?
=> @WebMvcTest: Only web layer (controllers, no DB)
=> @WebMvcTest is used for testing Rest controllers effectively
Step-by-Step Guide
1. Add Dependency (already included in spring-boot-starter-test) in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2. Basic Controller Example (to test)
@RestController
@RequestMapping("/employees")
public class EmployeeController {
@Autowired
private EmployeeService service;
@GetMapping("/{id}")
public Employee getEmployee(@PathVariable Long id) {
return service.findById(id);
}
}
3. Test Class with @WebMvcTest
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(EmployeeController.class)
class EmployeeControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private EmployeeService service; // Mock the service
@Test
void testGetEmployee_Success() throws Exception {
Employee emp = new Employee(1L, "Sachin", 800000);
when(service.findById(1L)).thenReturn(emp);
mockMvc.perform(get("/employees/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Sachin"))
.andExpect(jsonPath("$.salary").value(800000));
verify(service, times(1)).findById(1L);
}
@Test
void testGetEmployee_NotFound() throws Exception {
when(service.findById(999L)).thenThrow(new ResourceNotFoundException("Not found"));
mockMvc.perform(get("/employees/999"))
.andExpect(status().isNotFound());
}
}
Best Practice
=> Use @WebMvcTest for controller tests (fast, isolated)
=> Mock services/repositories with @MockBean
=> Use MockMvc for HTTP simulation (no real server)
=> Test status codes, JSON content, exceptions
Test JPA repositories
=> Use @DataJPATest that loads only the JPA layer (entities, repositories, DataSource, Hibernate)
AssertJ vs JUnit assertions?
=> JUnit assertions are static and basic (assertEquals, assertTrue).
=> AssertJ is fluent and chainable (assertThat(list).hasSize(5).contains("x")), with detailed error messages and soft assertions
=> AssertJ is preferred in modern Spring Boot projects for readability and power, while JUnit is simpler and sufficient for basic tests
Quick Example Comparison
JUnit (classic)
@Test
void testList() {
List<String> list = List.of("A", "B", "C");
assertEquals(3, list.size());
assertTrue(list.contains("B"));
assertEquals("A", list.get(0));
}
AssertJ (fluent & readable)
@Test
void testList() {
List<String> list = List.of("A", "B", "C");
assertThat(list)
.hasSize(3)
.contains("B")
.startsWith("A")
.doesNotContain("Z");
}
Best practices for Spring Boot Profiles + Testing Basics
=>Profiles: Use application-{profile}.properties for overrides, activate via command line/env variable, @Profile for beans, @ActiveProfiles in tests.
=> Testing: Choose @WebMvcTest/@DataJpaTest for fast isolated tests, @SpringBootTest for full integration, mock dependencies with @MockBean, use AssertJ for readable assertions, test validation and exceptions, keep tests fast with in-memory DB