Week 7 Day 1 — Spring Boot Testing Mental Model
Goal
Today I want to understand the big picture of testing in Spring Boot.
Main questions:
- Why do we write tests?
- What is the test pyramid?
- What is a unit test?
- What is a slice test?
- What is an integration test?
- What is the Spring TestContext?
- Why are Spring tests slower than pure unit tests?
- What does
@SpringBootTestdo? - What are test slices like
@WebMvcTestand@DataJpaTest? - When should I use mocks?
- When should I use the real Spring context?
- What are common exam traps?
1. Quick Review from Week 6
In Week 6, I learned:
- Spring Security protects requests before controllers.
- Authentication means proving identity.
- Authorization means checking permissions.
- 401 means not authenticated.
- 403 means authenticated but not allowed.
SecurityFilterChaindefines security rules.UserDetailsServiceloads users.PasswordEncoderhashes passwords.@PreAuthorizeprotects methods.- CSRF and CORS are different.
- Security tests should test allowed and denied cases.
Memory sentence:
Security tests prove that allowed users are allowed and forbidden users are blocked.
Now I move to a bigger topic:
How do I test a Spring Boot application well?
2. Why Do We Write Tests?
Tests help answer:
Does my code work?
Does it still work after changes?
Did I break an old feature?
Does my controller return the right response?
Does my service follow business rules?
Does my repository query the database correctly?
Does security block the wrong users?
Does validation reject invalid input?
Tests are not only for checking code.
They are also documentation.
A good test says:
This is how the application should behave.
Memory sentence:
Tests protect behavior.
3. What Should We Test?
In a Spring Boot app, I often test:
business logic
controllers
request validation
exception handling
repositories
database queries
transactions
security rules
JSON serialization
external API clients
configuration
full application flow
But I should not test everything in the same way.
Some things need small fast tests.
Some things need Spring context.
Some things need a real database.
Memory sentence:
Different problems need different test types.
4. The Test Pyramid
The test pyramid is a mental model.
E2E Tests
/ \
/ Integration \
/ Tests \
/ \
/ Slice Tests \
/ \
/ Unit Tests \
/_________________________\
Meaning:
many unit tests
some slice/integration tests
few end-to-end tests
Why?
unit tests are fast
integration tests are slower
end-to-end tests are slowest and more fragile
Memory sentence:
Have many fast tests and fewer slow full-system tests.
5. Main Test Types
| Test type | What it tests | Spring context? | Speed |
|---|---|---|---|
| Unit test | one class/method | no | very fast |
| Slice test | one Spring layer | partial | medium |
| Integration test | multiple layers/full app | yes | slower |
| End-to-end test | real user flow | often full system | slowest |
Memory sentence:
Unit tests are small. Integration tests are realistic.
6. Unit Test
A unit test tests one small piece of code.
Usually:
no Spring context
no database
no web server
no real HTTP
no real external API
Example target:
TaskService business logic
PriceCalculator
PermissionService
Validator helper
Mapper
Memory sentence:
A unit test tests one unit without starting Spring.
7. Unit Test Example
Service:
public class TaskTitlePolicy {
public void validate(String title) {
if (title == null || title.isBlank()) {
throw new IllegalArgumentException("Title must not be blank");
}
if (title.length() > 100) {
throw new IllegalArgumentException("Title is too long");
}
}
}
Unit test:
class TaskTitlePolicyTest {
private final TaskTitlePolicy policy = new TaskTitlePolicy();
@Test
void rejectsBlankTitle() {
assertThatThrownBy(() -> policy.validate(" "))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Title must not be blank");
}
@Test
void acceptsValidTitle() {
policy.validate("Prepare tax documents");
}
}
No Spring.
No database.
Very fast.
Memory sentence:
If I can test without Spring, test without Spring.
8. Unit Test with Mockito
Service:
public class TaskService {
private final TaskRepository taskRepository;
public TaskService(TaskRepository taskRepository) {
this.taskRepository = taskRepository;
}
public TaskDto findById(Long id) {
TaskEntity task = taskRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Task", id));
return new TaskDto(task.getId(), task.getTitle(), task.getStatus());
}
}
Unit test:
class TaskServiceTest {
private final TaskRepository taskRepository = mock(TaskRepository.class);
private final TaskService taskService = new TaskService(taskRepository);
@Test
void returnsTaskById() {
TaskEntity task = new TaskEntity(1L, "Learn tests", "OPEN");
when(taskRepository.findById(1L))
.thenReturn(Optional.of(task));
TaskDto result = taskService.findById(1L);
assertThat(result.id()).isEqualTo(1L);
assertThat(result.title()).isEqualTo("Learn tests");
assertThat(result.status()).isEqualTo("OPEN");
}
}
This test checks service behavior.
It does not check if the repository query really works.
Memory sentence:
Mock dependencies when testing one class in isolation.
9. What Unit Tests Are Good For
Unit tests are good for:
business rules
if/else logic
calculations
mapping logic
permission logic
validation helper logic
error behavior
edge cases
Examples:
Can user access task?
Is invoice overdue?
Can task be completed?
Is password strong enough?
Should notification be sent?
Memory sentence:
Unit tests are best for business logic and edge cases.
10. What Unit Tests Are Not Good For
Unit tests are not enough for:
JPA mapping
real SQL queries
Spring MVC request mapping
Jackson JSON conversion
Bean Validation integration
Spring Security filter chain
transaction behavior
real application configuration
For these, I need Spring tests.
Memory sentence:
Unit tests do not prove Spring wiring or framework integration.
11. Slice Test
A slice test loads only part of the Spring application.
Examples:
@WebMvcTest
@DataJpaTest
@JsonTest
@RestClientTest
Slice tests are faster than full integration tests because they do not load the whole application.
They test one layer or one technical slice.
Memory sentence:
A slice test loads only the part of Spring needed for that layer.
12. Common Slice Tests
| Annotation | Tests |
|---|---|
@WebMvcTest | controllers, MVC, validation, JSON, MockMvc |
@DataJpaTest | JPA entities, repositories, queries |
@JsonTest | JSON serialization/deserialization |
@RestClientTest | REST clients |
@JdbcTest | JDBC components |
Memory sentence:
Slice annotations focus the Spring context.
13. @WebMvcTest
@WebMvcTest is for testing the web layer.
It commonly tests:
controller request mapping
request parameters
request body JSON
validation
HTTP status
response JSON
controller exception handling
security rules if included
Example:
@WebMvcTest(TaskController.class)
class TaskControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private TaskService taskService;
@Test
void returnsTask() throws Exception {
when(taskService.findById(1L))
.thenReturn(new TaskDto(1L, "Learn MVC test", "OPEN"));
mockMvc.perform(get("/api/tasks/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.title").value("Learn MVC test"));
}
}
Important:
The service is mocked.
The controller is tested.
Memory sentence:
@WebMvcTesttests the controller layer, not the real service/database.
14. @DataJpaTest
@DataJpaTest is for testing JPA persistence.
It commonly tests:
entity mapping
repository methods
derived queries
@Query methods
relationships
constraints
pagination
sorting
database behavior
Example:
@DataJpaTest
class TaskRepositoryTest {
@Autowired
private TaskRepository taskRepository;
@Autowired
private TestEntityManager entityManager;
@Test
void findsByStatus() {
entityManager.persist(new TaskEntity("Task A", "OPEN"));
entityManager.persist(new TaskEntity("Task B", "DONE"));
entityManager.flush();
List<TaskEntity> result = taskRepository.findByStatus("OPEN");
assertThat(result)
.extracting(TaskEntity::getTitle)
.containsExactly("Task A");
}
}
Memory sentence:
@DataJpaTesttests repositories and JPA mapping.
15. Slice Test Mental Model
A slice test asks:
Does this Spring layer work correctly?
Examples:
Does controller parse JSON correctly?
Does validation return 400?
Does exception handler return error response?
Does repository query return the right rows?
Does Jackson serialize this DTO correctly?
It does not ask:
Does the whole application work end-to-end?
Memory sentence:
Slice tests are focused Spring tests.
16. Integration Test
An integration test checks multiple parts working together.
Example:
Controller + Service + Repository + Database
or:
Security + Controller + Service
or:
Application context + configuration + external client mock
Integration tests often use:
@SpringBootTest
Memory sentence:
Integration tests prove that multiple parts work together.
17. @SpringBootTest
@SpringBootTest loads the full Spring Boot application context.
Example:
@SpringBootTest
class ApplicationContextTest {
@Test
void contextLoads() {
}
}
This checks:
Can the application context start?
Are beans configured correctly?
Are dependencies wired correctly?
Is configuration valid?
But it can be slower.
Memory sentence:
@SpringBootTeststarts the full application context.
18. @SpringBootTest with MockMvc
Example:
@SpringBootTest
@AutoConfigureMockMvc
class TaskApiIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
void getsTask() throws Exception {
mockMvc.perform(get("/api/tasks/1"))
.andExpect(status().isOk());
}
}
This can test:
full application context
security filters
controllers
services
repositories
database
exception handling
JSON conversion
Memory sentence:
@SpringBootTest + MockMvctests web flow without starting a real server.
19. @SpringBootTest Web Environment
@SpringBootTest can use different web environments.
Common options:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
Meaning:
| Mode | Meaning |
|---|---|
MOCK | mock servlet environment, no real server |
RANDOM_PORT | starts real server on random port |
DEFINED_PORT | starts real server on configured port |
NONE | no web environment |
Memory sentence:
MOCKis common with MockMvc;RANDOM_PORTstarts a real server.
20. Integration Test with Real HTTP
Example:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TaskApiRealHttpTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void getsHealth() {
ResponseEntity<String> response =
restTemplate.getForEntity("/actuator/health", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
}
This starts a real embedded server.
Useful when I want to test:
real HTTP layer
server port
filters
serialization
full request/response behavior
Memory sentence:
RANDOM_PORTtests through real HTTP.
21. What Is Spring TestContext?
Spring TestContext is the testing framework support that manages Spring application contexts in tests.
It helps with:
loading application context
dependency injection into tests
transaction management in tests
test properties
active profiles
context caching
DirtiesContext
integration with JUnit
Example:
@SpringBootTest
class TaskServiceIntegrationTest {
@Autowired
private TaskService taskService;
}
Spring injects beans into the test because the TestContext framework is active.
Memory sentence:
Spring TestContext manages Spring application contexts for tests.
22. Why Are Spring Tests Slower?
Spring tests can be slower because they may:
start application context
create many beans
load auto-configuration
connect to database
start embedded web environment
initialize security filters
run migrations
scan classpath
Unit tests avoid all this.
Memory sentence:
Starting Spring costs time; use it only when the test needs Spring.
23. Context Caching
Spring can cache application contexts between tests.
If two tests use the same context configuration, Spring can reuse the context.
This makes tests faster.
But if I change the context with:
@DirtiesContext
then Spring may close and rebuild the context.
Memory sentence:
Spring caches test contexts; do not dirty the context unnecessarily.
24. @DirtiesContext
@DirtiesContext tells Spring:
This test changed the context; do not reuse it.
Example use case:
test changes global bean state
test modifies application context
test changes environment in a way that affects future tests
But it slows down the test suite.
Memory sentence:
Use
@DirtiesContextrarely.
25. Test Profiles
Tests often use a special profile.
Example:
@ActiveProfiles("test")
@SpringBootTest
class TaskServiceIntegrationTest {
}
Then Spring can load:
application-test.yml
Example:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/test_db
Memory sentence:
Use test profile for test-specific configuration.
26. Test Properties
Inline properties:
@SpringBootTest(properties = {
"feature.email.enabled=false",
"app.security.demo-mode=true"
})
class AppTest {
}
This overrides properties for the test.
Useful for:
disable external integrations
change feature flags
use fake URLs
configure test behavior
Memory sentence:
Test properties customize the application context for tests.
27. @MockBean
@MockBean replaces a Spring bean with a Mockito mock in the application context.
Example:
@WebMvcTest(TaskController.class)
class TaskControllerTest {
@MockBean
private TaskService taskService;
}
Meaning:
When controller needs TaskService, Spring injects the mock.
Useful for slice tests.
Memory sentence:
@MockBeanmocks a Spring bean inside the test context.
28. Mockito mock() vs @MockBean
| Tool | Where it works |
|---|---|
mock() | plain unit test |
@Mock | Mockito test |
@MockBean | Spring test context |
Example plain unit test:
TaskRepository repository = mock(TaskRepository.class);
TaskService service = new TaskService(repository);
Example Spring test:
@MockBean
TaskService taskService;
Memory sentence:
Use
mock()for unit tests; use@MockBeanwhen Spring must inject the mock.
29. @SpyBean
@SpyBean wraps a real Spring bean with a Mockito spy.
It can call real methods but still allow verification/stubbing.
Use carefully.
Example:
@SpyBean
private EmailService emailService;
Then:
verify(emailService).sendWelcomeEmail(any());
But too much spying can make tests fragile.
Memory sentence:
@SpyBeanobserves a real Spring bean, but use it carefully.
30. Test Data
Tests need data.
Options:
create data in test code
use SQL scripts
use TestEntityManager
use repositories
use fixtures/builders
use migrations with clean database
Good practice:
Make test data clear and close to the test.
Example:
TaskEntity task = new TaskEntity("Task A", "OPEN");
entityManager.persistAndFlush(task);
Memory sentence:
Clear test data makes tests easier to understand.
31. Arrange, Act, Assert
A common test structure:
Arrange
Act
Assert
Example:
@Test
void completesTask() {
// Arrange
TaskEntity task = new TaskEntity("Task A", "OPEN");
when(taskRepository.findById(1L)).thenReturn(Optional.of(task));
// Act
TaskDto result = taskService.complete(1L);
// Assert
assertThat(result.status()).isEqualTo("DONE");
}
Memory sentence:
Arrange data, act once, assert result.
32. Given, When, Then
Another naming style:
given
when
then
Example:
@Test
void givenOpenTask_whenComplete_thenStatusIsDone() {
}
Or simpler:
@Test
void completesOpenTask() {
}
Good test names explain behavior.
Memory sentence:
Test names should describe behavior.
33. What Makes a Good Test?
A good test is:
clear
fast enough
repeatable
independent
focused
deterministic
easy to debug
close to business behavior
Bad test:
depends on test order
depends on current time randomly
depends on external API
has unclear data
tests many things at once
requires manual setup
Memory sentence:
Good tests are repeatable and easy to understand.
34. Independent Tests
Each test should be independent.
Bad:
Test B only passes if Test A runs first.
Good:
Each test creates its own data.
Each test can run alone.
Memory sentence:
Tests should not depend on execution order.
35. Deterministic Tests
A deterministic test gives the same result every time.
Bad causes:
random data without control
real current time
external network calls
shared mutable state
test order dependency
real email sending
real payment API
Better:
use fixed Clock
mock external APIs
create isolated data
use test containers or test database
Memory sentence:
A test should not sometimes pass and sometimes fail.
36. Testing Time
Bad service:
public boolean isOverdue(TaskEntity task) {
return task.getDueDate().isBefore(LocalDate.now());
}
Harder to test because current date changes.
Better:
public class TaskDeadlineService {
private final Clock clock;
public TaskDeadlineService(Clock clock) {
this.clock = clock;
}
public boolean isOverdue(TaskEntity task) {
LocalDate today = LocalDate.now(clock);
return task.getDueDate().isBefore(today);
}
}
Test:
Clock fixedClock = Clock.fixed(
Instant.parse("2026-07-07T10:00:00Z"),
ZoneOffset.UTC
);
Memory sentence:
Inject
Clockinstead of calling current time directly in business logic.
37. Testing External APIs
Do not call real external APIs in normal automated tests.
Bad:
test calls real payment provider
test sends real email
test uploads to real S3 bucket
test calls production API
Better:
mock external client in unit/slice tests
use fake server in integration tests
use test environment/sandbox carefully
Memory sentence:
Automated tests should not depend on real external services.
38. Test Database Choices
Common options:
H2/in-memory database
local PostgreSQL test database
Testcontainers PostgreSQL
mock repository
Use based on test type.
| Option | Good for |
|---|---|
| Mock repository | service unit tests |
| H2 | fast simple repository tests |
| Testcontainers | production-like database tests |
| Local test DB | local integration testing |
Memory sentence:
Mock repositories for service logic; real database for repository behavior.
39. H2 vs PostgreSQL
H2 is fast and simple.
But it may behave differently from PostgreSQL.
Differences can include:
SQL syntax
JSON columns
case sensitivity
indexes
constraints
date/time behavior
native queries
locking behavior
migration scripts
For important persistence tests, use the same database type as production.
Memory sentence:
H2 is fast, but production-like database tests catch production-like bugs.
40. Testcontainers Preview
Testcontainers can start real services in Docker during tests.
Example services:
PostgreSQL
Redis
Kafka
RabbitMQ
LocalStack
For Spring Boot database tests, Testcontainers can provide a real PostgreSQL database.
We will study this later.
Memory sentence:
Testcontainers gives real infrastructure for integration tests.
41. Which Test Type Should I Choose?
Ask:
What do I want to prove?
If I want to prove business logic:
unit test
If I want to prove controller mapping and validation:
@WebMvcTest
If I want to prove repository query:
@DataJpaTest
If I want to prove full flow:
@SpringBootTest
If I want to prove real database behavior:
@DataJpaTest or @SpringBootTest with real DB/Testcontainers
Memory sentence:
Choose the smallest test that proves the behavior.
42. Example: Testing Create Task
Feature:
POST /api/tasks creates a task.
Possible tests:
Unit Test
Test service business logic:
given valid request
when create task
then repository save is called
then DTO is returned
Web Slice Test
Test controller:
given JSON request
when POST /api/tasks
then status 201
then validation errors return 400
Repository Test
Test repository:
given saved task
when findByStatus
then task is returned
Integration Test
Test full flow:
given request
when POST /api/tasks
then controller, service, repository, database work together
Memory sentence:
One feature can have different tests at different levels.
43. What Not to Over-Test
Avoid tests that only test the framework.
Bad:
@Test
void repositorySaveWorks() {
TaskEntity saved = taskRepository.save(new TaskEntity("A", "OPEN"));
assertThat(saved.getId()).isNotNull();
}
This mostly tests Spring Data/Hibernate.
Better:
test custom query
test mapping constraint
test relationship
test business behavior
test validation
Memory sentence:
Test your code and configuration, not Spring itself.
44. Common Exam Traps
Trap 1
Unit tests do not need Spring context.
Trap 2
@SpringBootTest loads the full application context.
Trap 3
Full context tests are slower than unit tests.
Trap 4
Slice tests load only part of the application.
Trap 5
@WebMvcTest tests web layer, not real service/database.
Trap 6
@DataJpaTest tests JPA repositories/entities, not controllers.
Trap 7
@MockBean replaces a Spring bean in the test context.
Trap 8
Mockito mock() is for plain unit tests.
Trap 9
Spring TestContext can cache contexts.
Trap 10
@DirtiesContext can make tests slower.
Trap 11
Use @ActiveProfiles("test") for test configuration.
Trap 12
H2 may behave differently from PostgreSQL.
Trap 13
Do not call real external APIs in automated tests.
Trap 14
Tests should be independent and deterministic.
Trap 15
Choose the smallest test that proves the behavior.
45. Real Exam Question: Unit Test
Question:
What is a unit test?
Answer:
A unit test tests a small piece of code, usually one class or method, without starting the Spring context or real infrastructure.
46. Real Exam Question: Slice Test
Question:
What is a slice test?
Answer:
A slice test loads only a specific part of the Spring application context, such as the web layer or JPA layer.
47. Real Exam Question: Integration Test
Question:
What is an integration test?
Answer:
An integration test checks multiple parts of the application working together, often with a Spring application context.
48. Real Exam Question: @SpringBootTest
Question:
What does @SpringBootTest do?
Answer:
@SpringBootTest loads the full Spring Boot application context for integration testing.
49. Real Exam Question: @WebMvcTest
Question:
What does @WebMvcTest test?
Answer:
@WebMvcTest tests the web/MVC layer, such as controllers, request mapping, validation, JSON responses, and controller advice. Service dependencies are often mocked.
50. Real Exam Question: @DataJpaTest
Question:
What does @DataJpaTest test?
Answer:
@DataJpaTest tests JPA components such as entities, repositories, mappings, and queries.
51. Real Exam Question: @MockBean
Question:
What does @MockBean do?
Answer:
@MockBean replaces a Spring bean in the application context with a Mockito mock.
52. Real Exam Question: TestContext
Question:
What does the Spring TestContext framework do?
Answer:
It manages Spring application contexts for tests, supports dependency injection into tests, test properties, profiles, transactions, and context caching.
53. Interview Answer
Question:
Explain the difference between unit tests, slice tests, and integration tests.
Good answer:
A unit test tests a small piece of code without starting Spring, usually with mocks for dependencies. A slice test starts only part of the Spring context, such as the MVC layer with @WebMvcTest or the JPA layer with @DataJpaTest. An integration test uses a larger or full Spring context, often with @SpringBootTest, to verify that multiple parts of the application work together.
54. Interview Answer
Question:
When would you use
@SpringBootTest?
Good answer:
I use @SpringBootTest when I need the full application context or want to test multiple layers together, such as controller, service, repository, security configuration, and database integration. I avoid using it for every test because it is slower than unit or slice tests.
55. Interview Answer
Question:
When would you use
@WebMvcTest?
Good answer:
I use @WebMvcTest to test the controller layer. It is good for request mapping, request body handling, validation, response status, JSON response, and controller exception handling. I usually mock service dependencies with @MockBean.
56. Interview Answer
Question:
When would you use
@DataJpaTest?
Good answer:
I use @DataJpaTest to test repository queries, JPA mappings, entity relationships, constraints, pagination, and sorting. It focuses on the persistence layer and does not load controllers or normal service beans.
57. Interview Answer
Question:
How do you decide which test type to write?
Good answer:
I ask what behavior I want to prove. If it is pure business logic, I write a unit test. If it is controller behavior, I use @WebMvcTest. If it is repository behavior, I use @DataJpaTest. If I need multiple layers working together, I use @SpringBootTest. I choose the smallest test that proves the behavior.
58. Tiny Code Practice
Question:
For each situation, choose the best test type.
Situation 1
I want to test this method:
public int calculateDiscount(int price, int percent) {
return price * percent / 100;
}
Best test type:
Unit test
Why?
No Spring needed.
Situation 2
I want to test that invalid JSON returns 400 from a controller.
Best test type:
@WebMvcTest
Why?
This is web/controller behavior.
Situation 3
I want to test that findByEmail works.
Best test type:
@DataJpaTest
Why?
This is repository/database behavior.
Situation 4
I want to test login + security + controller + database together.
Best test type:
@SpringBootTest
Why?
Multiple application layers must work together.
59. Tiny Bug Practice 1
Problem:
@SpringBootTest
class PriceCalculatorTest {
@Autowired
private PriceCalculator priceCalculator;
@Test
void calculatesDiscount() {
assertThat(priceCalculator.calculateDiscount(100, 10)).isEqualTo(10);
}
}
Question:
What is the issue?
Answer:
This is pure calculation logic. Starting the full Spring context is unnecessary. Use a plain unit test.
60. Tiny Bug Practice 2
Problem:
@WebMvcTest(TaskController.class)
class TaskControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void returnsTask() throws Exception {
mockMvc.perform(get("/api/tasks/1"))
.andExpect(status().isOk());
}
}
But the controller depends on TaskService, and the test fails to start.
Question:
What is missing?
Answer:
A mock service bean is missing.
@MockBean
private TaskService taskService;
61. Tiny Bug Practice 3
Problem:
@DataJpaTest
class TaskServiceTest {
@Autowired
private TaskService taskService;
}
Question:
What is wrong?
Answer:
@DataJpaTest loads the JPA slice, not normal service beans. Use a unit test for service logic or @SpringBootTest if Spring integration is needed.
62. Tiny Bug Practice 4
Problem:
A test sometimes passes and sometimes fails.
Possible causes:
depends on current time
depends on test order
depends on random data
depends on external API
shared mutable state
Fix:
make data explicit
use fixed Clock
mock external services
avoid test order dependency
reset shared state
Practice Questions and Answers
Question 1
Why do we write tests?
Answer:
We write tests to protect application behavior, catch bugs, prevent regressions, and document how the system should work.
Question 2
What is the test pyramid?
Answer:
The test pyramid means having many fast unit tests, some slice/integration tests, and fewer slow end-to-end tests.
Question 3
What is a unit test?
Answer:
A unit test tests one small piece of code, usually one class or method, in isolation.
Question 4
Does a unit test need Spring context?
Answer:
No. A true unit test usually does not start the Spring context.
Question 5
What is a slice test?
Answer:
A slice test loads only a specific part of the Spring application context, such as MVC or JPA.
Question 6
What is an integration test?
Answer:
An integration test checks multiple parts of the application working together.
Question 7
What does @SpringBootTest do?
Answer:
@SpringBootTest loads the full Spring Boot application context.
Question 8
What does @WebMvcTest test?
Answer:
@WebMvcTest tests the web layer, including controllers, request mapping, validation, JSON, and controller advice.
Question 9
What does @DataJpaTest test?
Answer:
@DataJpaTest tests JPA components such as entities, repositories, mappings, and queries.
Question 10
What does @MockBean do?
Answer:
@MockBean replaces a Spring bean in the test context with a Mockito mock.
Question 11
What is the difference between mock() and @MockBean?
Answer:
mock() creates a plain Mockito mock for unit tests. @MockBean creates/replaces a mock bean inside the Spring test context.
Question 12
What is Spring TestContext?
Answer:
Spring TestContext manages Spring application contexts for tests and supports dependency injection, test properties, profiles, transactions, and context caching.
Question 13
Why are Spring tests slower than unit tests?
Answer:
Because they may start application context, create beans, load auto-configuration, connect to databases, initialize filters, and run migrations.
Question 14
What is context caching?
Answer:
Context caching means Spring can reuse the same test application context across tests with the same configuration.
Question 15
What does @DirtiesContext do?
Answer:
@DirtiesContext tells Spring that the context was changed and should not be reused.
Question 16
Why use @ActiveProfiles("test")?
Answer:
To use test-specific configuration, such as test database URLs, fake integrations, or disabled external services.
Question 17
Why can H2 be risky for repository tests?
Answer:
Because H2 can behave differently from production databases like PostgreSQL, especially with native SQL, constraints, JSON, date/time, and locking behavior.
Question 18
What is Testcontainers useful for?
Answer:
Testcontainers is useful for running real infrastructure like PostgreSQL, Redis, or Kafka in Docker during integration tests.
Question 19
What does deterministic test mean?
Answer:
A deterministic test gives the same result every time and does not randomly pass or fail.
Question 20
How do I choose the right test type?
Answer:
Choose the smallest test that proves the behavior: unit for business logic, slice test for one Spring layer, integration test for multiple layers.
Final Memory Sentences
- Tests protect behavior.
- Different problems need different test types.
- The test pyramid has many unit tests and fewer full integration/E2E tests.
- Unit tests do not start Spring.
- Slice tests load only one part of Spring.
- Integration tests check multiple parts together.
@SpringBootTestloads the full application context.@WebMvcTesttests the MVC/controller layer.@DataJpaTesttests repositories and JPA mapping.@MockBeanreplaces a Spring bean with a mock.- Use
mock()for unit tests. - Use
@MockBeanin Spring tests. - Spring TestContext manages test application contexts.
- Spring tests are slower because they start Spring infrastructure.
- Spring caches test contexts.
- Use
@DirtiesContextrarely. - Use test profiles for test configuration.
- H2 is fast but may differ from PostgreSQL.
- Testcontainers provides production-like infrastructure.
- Tests should be independent and deterministic.
- Choose the smallest test that proves the behavior.