Tutorials Logic, IN info@tutorialslogic.com

Spring Testing Unit Tests, Integration Tests, MockMvc: Tutorial, Examples, FAQs & Interview Tips

Spring Testing Unit Tests, Integration Tests, MockMvc

Spring Testing Unit Tests, Integration Tests, MockMvc is an important Spring topic because it appears in real projects, debugging sessions, and interviews. Learn the meaning first, then connect it to a small working example so the rule does not stay abstract.

For this page, focus on what problem Spring Testing Unit Tests, Integration Tests, MockMvc solves, where developers usually make mistakes, and how to verify the result. The audit note for this lesson was: limited checklist/practice/mistake/FAQ notes .

A strong understanding of Spring Testing Unit Tests, Integration Tests, MockMvc should include syntax, behavior, one realistic use case, one failure case, and one quick way to check your work with tools or output.

Spring Testing Unit Tests Integration Tests MockMvc should be studied as a practical Spring lesson, not as a label. Start by naming the input, the rule that changes the input, and the result a learner should be able to predict after reading the page.

In the spring > spring-testing page, the notes should connect the definition with a working scenario, a mistake that beginners actually make, and the exact check that proves the fix. That makes the topic useful for coding, debugging, and interview revision.

Testing Annotations Overview

Annotation Description Use Case
@SpringBootTest Loads the full application context Integration tests
@WebMvcTest Loads only the web layer (controllers) Controller unit tests
@DataJpaTest Loads only JPA components, uses in-memory DB Repository tests
@MockBean Creates a Mockito mock and adds it to the Spring context Mocking dependencies
@ExtendWith(SpringExtension.class) Integrates Spring with JUnit 5 All Spring tests

Testing REST Controllers with @WebMvcTest and MockMvc

Testing REST Controllers with @WebMvcTest and MockMvc
package com.example.controller;

import com.example.model.User;
import com.example.service.UserService;
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 java.util.List;
import java.util.Optional;

import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

// @WebMvcTest loads only the web layer - fast, no DB needed
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc; // Simulates HTTP requests without starting a server

    @MockBean
    private UserService userService; // Replaces real UserService with a Mockito mock

    @Test
    void getAllUsers_shouldReturn200WithUserList() throws Exception {
        // Arrange: define mock behavior
        when(userService.findAll()).thenReturn(
            List.of(new User(1L, "Alice", "alice@example.com"),
                    new User(2L, "Bob",   "bob@example.com"))
        );

        // Act & Assert: perform GET and verify response
        mockMvc.perform(get("/api/users")
                .accept(MediaType.APPLICATION_JSON))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.length()").value(2))
               .andExpect(jsonPath("$[0].name").value("Alice"))
               .andExpect(jsonPath("$[1].email").value("bob@example.com"));

        verify(userService, times(1)).findAll();
    }

    @Test
    void getUserById_whenNotFound_shouldReturn404() throws Exception {
        when(userService.findById(99L)).thenReturn(Optional.empty());

        mockMvc.perform(get("/api/users/99"))
               .andExpect(status().isNotFound());
    }

    @Test
    void createUser_withValidData_shouldReturn201() throws Exception {
        User newUser = new User(null, "Charlie", "charlie@example.com");
        User saved   = new User(3L,   "Charlie", "charlie@example.com");
        when(userService.save(any(User.class))).thenReturn(saved);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"name\":\"Charlie\",\"email\":\"charlie@example.com\"}"))
               .andExpect(status().isCreated())
               .andExpect(jsonPath("$.id").value(3))
               .andExpect(jsonPath("$.name").value("Charlie"));
    }
}

Testing Annotations Overview

Testing Annotations Overview
package com.example.controller;

import com.example.model.User;
import com.example.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User saved = userService.save(user);
        return ResponseEntity.status(201).body(saved);
    }
}

Testing Services with Mockito

Service Unit Test with Mockito

Service Unit Test with Mockito
package com.example.service;

import com.example.model.User;
import com.example.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Optional;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

// Pure Mockito test - no Spring context loaded (very fast)
@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository; // Mockito mock

    @InjectMocks
    private UserService userService; // Injects the mock into UserService

    @Test
    void findById_whenUserExists_shouldReturnUser() {
        User user = new User(1L, "Alice", "alice@example.com");
        when(userRepository.findById(1L)).thenReturn(Optional.of(user));

        Optional<User> result = userService.findById(1L);

        assertThat(result).isPresent();
        assertThat(result.get().getName()).isEqualTo("Alice");
        verify(userRepository).findById(1L);
    }

    @Test
    void save_withDuplicateEmail_shouldThrowException() {
        User user = new User(null, "Bob", "bob@example.com");
        when(userRepository.existsByEmail("bob@example.com")).thenReturn(true);

        assertThatThrownBy(() -> userService.save(user))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("Email already exists");

        verify(userRepository, never()).save(any());
    }
}

@DataJpaTest for Repository Testing

Repository Test with @DataJpaTest

Repository Test with @DataJpaTest
package com.example.repository;

import com.example.model.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import java.util.Optional;

import static org.assertj.core.api.Assertions.*;

// @DataJpaTest: loads JPA layer only, uses H2 in-memory DB by default
// Each test runs in a transaction that is rolled back after the test
@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private TestEntityManager entityManager; // Helper for persisting test data

    @Autowired
    private UserRepository userRepository;

    @Test
    void findByEmail_whenExists_shouldReturnUser() {
        // Arrange: persist a user directly via EntityManager
        User user = new User(null, "Alice", "alice@example.com");
        entityManager.persistAndFlush(user);

        // Act
        Optional<User> found = userRepository.findByEmail("alice@example.com");

        // Assert
        assertThat(found).isPresent();
        assertThat(found.get().getName()).isEqualTo("Alice");
    }

    @Test
    void existsByEmail_whenNotExists_shouldReturnFalse() {
        boolean exists = userRepository.existsByEmail("nobody@example.com");
        assertThat(exists).isFalse();
    }

    @Test
    void save_shouldPersistAndGenerateId() {
        User user = new User(null, "Bob", "bob@example.com");
        User saved = userRepository.save(user);

        assertThat(saved.getId()).isNotNull();
        assertThat(saved.getId()).isGreaterThan(0);
    }
}

Integration Test with @SpringBootTest

Integration Test with @SpringBootTest
package com.example;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.*;

import static org.assertj.core.api.Assertions.*;

// @SpringBootTest: loads full context, starts embedded server on random port
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserIntegrationTest {

    @LocalServerPort
    private int port; // Injected random port

    @Autowired
    private TestRestTemplate restTemplate; // HTTP client for integration tests

    @Test
    void getUsers_shouldReturnOk() {
        ResponseEntity<String> response = restTemplate.getForEntity(
                "http://localhost:" + port + "/api/users", String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    @Test
    void createUser_shouldReturn201() {
        String body = "{\"name\":\"Test\",\"email\":\"test@example.com\"}";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        ResponseEntity<String> response = restTemplate.postForEntity(
                "http://localhost:" + port + "/api/users",
                new HttpEntity<>(body, headers), String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
    }
}

Spring Testing Unit Tests Integration Tests MockMvc Java review example

Spring Testing Unit Tests Integration Tests MockMvc Java review example
class SpringTestingUnitTestsIntegrationTestsMockMvcReview {
    public static void main(String[] args) {
        String state = "ready";
        System.out.println("Spring Testing Unit Tests Integration Tests MockMvc: " + state);
    }
}

Spring Testing Unit Tests Integration Tests MockMvc guard example

Spring Testing Unit Tests Integration Tests MockMvc guard example
String value = null;
if (value == null) {
    System.out.println("Spring Testing Unit Tests Integration Tests MockMvc: handle the missing value before continuing");
}
Key Takeaways
  • Explain the purpose of Spring Testing Unit Tests, Integration Tests, MockMvc before memorizing syntax.
  • Run or trace one small Spring example and confirm the output.
  • Test one normal case, one edge case, and one mistake case for Spring Testing Unit Tests, Integration Tests, MockMvc.
  • Write the rule in your own words after checking the example.
  • Connect Spring Testing Unit Tests, Integration Tests, MockMvc to a real project scenario instead of treating it as an isolated definition.
Common Mistakes to Avoid
WRONG Memorizing Spring Testing Unit Tests Integration Tests MockMvc without the situation where it is useful.
RIGHT Connect Spring Testing Unit Tests Integration Tests MockMvc to a concrete Spring task.
Purpose makes syntax easier to recall.
WRONG Testing Spring Testing Unit Tests Integration Tests MockMvc only with the perfect input.
RIGHT Include empty, missing, duplicate, incompatible, or failed cases when relevant.
Real bugs usually appear outside the perfect path.
WRONG Changing code before reading the visible symptom or error message.
RIGHT Inspect the output, state, configuration, or stack trace connected to Spring Testing Unit Tests Integration Tests MockMvc.
Evidence keeps debugging focused.
WRONG Memorizing Spring Testing Unit Tests Integration Tests MockMvc without the situation where it is useful.
RIGHT Connect Spring Testing Unit Tests Integration Tests MockMvc to a concrete Spring task.
Purpose makes syntax easier to recall.

Practice Tasks

  • Modify the example so it handles a different input or condition.
  • Write one mistake related to Spring Testing Unit Tests, Integration Tests, MockMvc, then fix it and explain the fix.
  • Summarize when to use Spring Testing Unit Tests, Integration Tests, MockMvc and when another approach is better.
  • Write a small example that uses Spring Testing Unit Tests Integration Tests MockMvc in a realistic Spring scenario.
  • Change one important value in the Spring Testing Unit Tests Integration Tests MockMvc example and predict the result first.

Frequently Asked Questions

The common mistake is memorizing syntax without understanding when the behavior changes or fails.

Remember the problem it solves in Spring, then attach the syntax or steps to that problem.

You can predict the result of a small example, explain a failure case, and choose it over a nearby alternative for a clear reason.

They often copy the syntax but skip the state, input, dependency, selector, route, type, or configuration that controls the behavior.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.