Configuration, Profiles and Testing
application.yml, profiles, and unit and integration tests.
What you will learn
- Externalize configuration
- Use profiles
- Write MockMvc tests
The last pieces of a production-ready service: configuration that changes per environment, and automated tests that let you change code with confidence.
# application.yml
server:
port: 8080
app:
greeting: Hello
max-tasks: 100import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app")
public record AppProperties(String greeting, int maxTasks) {}
// enable with @ConfigurationPropertiesScan on the application class
// inject: public HelloController(AppProperties props) { ... }Spring reads properties from many places, later ones overriding earlier ones: the file, environment variables (APP_MAXTASKS=50), command-line arguments. This is how the same jar runs in every environment. Keep secrets in environment variables, never in git.
Profiles
Profiles switch configuration and even beans per environment.
# application-dev.yml
spring:
datasource:
url: jdbc:h2:mem:devdb
# application-prod.yml
spring:
datasource:
url: jdbc:postgresql://db:5432/appjava -jar app.jar --spring.profiles.active=prod
# or: SPRING_PROFILES_ACTIVE=prodUnit tests
Test services in isolation with plain JUnit and Mockito. They start instantly because no Spring container is needed.
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
class TaskServiceTest {
@Mock TaskRepository repo;
@InjectMocks TaskService service;
@Test
void getThrowsWhenMissing() {
when(repo.findById(1L)).thenReturn(java.util.Optional.empty());
assertThrows(TaskNotFoundException.class, () -> service.get(1L));
}
}import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(TaskController.class)
class TaskControllerTest {
@Autowired MockMvc mvc;
@MockBean TaskService service;
@Test
void rejectsBlankTitle() throws Exception {
mvc.perform(post("/api/tasks")
.contentType("application/json")
.content("{\"title\": \"\"}"))
.andExpect(status().isBadRequest());
}
@Test
void returnsTask() throws Exception {
when(service.findById(1L)).thenReturn(new TaskResponse(1L, "Write tests", false));
mvc.perform(get("/api/tasks/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.title").value("Write tests"));
}
}@WebMvcTest loads only the web layer, so it stays fast. Use @SpringBootTest for full integration tests, ideally with Testcontainers to run a real PostgreSQL in Docker.
./mvnw clean package
java -jar target/demo-0.0.1-SNAPSHOT.jarBoot builds a single executable jar containing the embedded server. It runs anywhere Java runs, and easily fits into a Docker image.
Spring Security for authentication and authorization, Spring Cache, messaging with Kafka or RabbitMQ, and Micrometer with Actuator for observability.
Try it yourself
Write a MockMvc test asserting that GET /api/tasks/999 returns 404 when the service throws TaskNotFoundException.
Show solution
@Test
void missingTaskIs404() throws Exception {
when(service.findById(999L)).thenThrow(new TaskNotFoundException(999L));
mvc.perform(get("/api/tasks/999"))
.andExpect(status().isNotFound());
}