Bu bo‘limda Spring Boot’da birinchi testlarni yozishni o‘rganamiz. Bbu mavzu ichida @SpringBootTest, @WebMvcTest, MockMvc, controller testlari va @MockBean bor.
1. Test nima uchun kerak?
Test - kod to‘g‘ri ishlayotganini avtomatik tekshiradigan kod.
Masalan, biz GET /users/1 endpoint yozdik. Qo‘lda Postman’da tekshirish mumkin. Lekin har safar qo‘lda tekshirish sekin va ishonchsiz.
Test bilan esa:
Kod o‘zgarsa ham endpoint buzilmaganini tekshiramiz
Buglarni erta topamiz
Refactor qilish xavfsizroq bo‘ladi
CI/CD’da avtomatik tekshiruv ishlaydiOddiy qilib:
Test - "men yozgan kod hali ham to‘g‘ri ishlayaptimi?" degan savolga avtomatik javob beradi.
2. Spring Boot’da testlar qayerda yoziladi?
Spring Boot loyihada testlar odatda shu joyda bo‘ladi:
src/test/javaMasalan:
src
├── main
│ └── java
│ └── uz/jts/demo
│ ├── controller
│ ├── service
│ └── repository
└── test
└── java
└── uz/jts/demo
├── controller
├── service
└── repository3. Kerakli dependency
Agar Spring Initializr’da loyiha ochsak, odatda test dependency avtomatik qo‘shiladi:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>Bu dependency ichida quyidagilar bor:
JUnit 5
AssertJ
Mockito
Spring Test
MockMvc
JSON test utilities4. Test turlari
Spring Boot’da boshlang'ich bosqichda 3 ta asosiy testni bilish yetarli:
Test turi | Nima tekshiradi? | Tezligi |
|---|---|---|
Unit test | Bitta class yoki method | Juda tez |
Slice test | Faqat ma’lum layer, masalan controller | Tez |
Integration test | Butun Spring context | Sekinroq |
boshlang'ich bosqichda asosiy mavzular:
@SpringBootTest
@WebMvcTest
MockMvc
@MockBean5. Unit test nima?
Unit test - bitta classni alohida tekshirish.
Masalan, UserService bor:
@Service
public class UserService {
public String getUserName(Long id) {
return "Ali";
}
}Test:
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class UserServiceTest {
@Test
void shouldReturnUserName() {
UserService userService = new UserService();
String result = userService.getUserName(1L);
assertThat(result).isEqualTo("Ali");
}
}Bu yerda Spring umuman ishga tushmaydi. Shuning uchun test tez ishlaydi.
6. @SpringBootTest nima?
@SpringBootTest - butun Spring Boot application context’ni ishga tushirib test qiladi.
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
}
}Bu test nimani tekshiradi?
Spring application context xatosiz ochiladimi?
Agar beanlarda muammo bo‘lsa, dependency topilmasa, config xato bo‘lsa - bu test yiqiladi.
7. contextLoads() testining ma’nosi
Spring Initializr ko‘pincha shunday test yaratadi:
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
}
}Ichida hech narsa yo‘qdek ko‘rinadi. Lekin aslida muhim:
Spring Boot ishga tushadi
Beanlar yaratiladi
Dependency injection tekshiriladi
Configuration tekshiriladiAgar app start bo‘lmasa, test fail bo‘ladi.
8. @SpringBootTest qachon ishlatiladi?
@SpringBootTest quyidagi holatda ishlatiladi:
Butun application start bo‘lishini tekshirish
Bir nechta layer birga ishlashini tekshirish
Real database yoki test database bilan integration test
Configuration to‘g‘ri ekanini tekshirishLekin har bir kichik testga @SpringBootTest ishlatish shart emas.
Sababi:
Sekinroq ishlaydi
Butun context ochiladi
Testlar ko‘payganda vaqt cho‘ziladi9. @WebMvcTest nima?
@WebMvcTest - faqat web/controller layer’ni test qilish uchun ishlatiladi.
Masalan:
@WebMvcTest(UserController.class)
class UserControllerTest {
}Bu testda Spring faqat MVC qismni ko‘taradi:
Controller
JSON converter
Validation
Spring MVC infrastructureLekin service, repository, database to‘liq ochilmaydi.
Shuning uchun controller dependency’larini mock qilish kerak bo‘ladi.
10. Mock nima?
Mock - haqiqiy obyekt o‘rniga ishlatiladigan soxta obyekt.
Masalan, UserController UserServicega bog‘liq:
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
}Controller testida haqiqiy UserServiceni ishlatmasdan mock qilamiz.
Ya’ni:
"Agar
userService.getUser(1L)chaqirilsa,Aliqaytar" deb oldindan aytib qo‘yamiz.
11. @MockBean nima?
@MockBean - Spring context ichiga mock bean qo‘shadi.
@MockBean
private UserService userService;Bu degani:
Haqiqiy UserService o‘rniga mock UserService ishlatiladi
Controller shu mock service’ni oladi
Testda service javobini o‘zimiz belgilaymizMuhim: Spring Boot 3.4+ da @MockBean deprecated bo‘lgan. Yangi loyihalarda @MockitoBean ishlatiladi. Lekin ko‘p real loyihalarda hali ham @MockBean uchraydi.
12. MockMvc nima?
MockMvc - controller endpointlarni haqiqiy server ochmasdan test qilish vositasi.
Masalan, test ichida:
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk());Bu haqiqiy browser yoki Postman emas. Lekin Spring MVC request/response jarayonini test qiladi.
13. Mini loyiha: User API
Avval oddiy API tayyorlaymiz.
DTO
public record UserResponse(
Long id,
String name,
String email
) {
}Service
@Service
public class UserService {
public UserResponse getUserById(Long id) {
return new UserResponse(id, "Ali", "ali@mail.com");
}
public List<UserResponse> getUsers() {
return List.of(
new UserResponse(1L, "Ali", "ali@mail.com"),
new UserResponse(2L, "Vali", "vali@mail.com")
);
}
}Controller
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public UserResponse getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@GetMapping
public List<UserResponse> getUsers() {
return userService.getUsers();
}
}14. Birinchi @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.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private UserService userService;
@Test
void shouldReturnUserById() throws Exception {
when(userService.getUserById(1L))
.thenReturn(new UserResponse(1L, "Ali", "ali@mail.com"));
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk());
}
}Bu test:
GET /users/1 endpointni chaqiradi
HTTP status 200 OK ekanini tekshiradi
UserService haqiqiy emas, mock15. JSON response tekshirish
Faqat status emas, body ichidagi qiymatlarni ham tekshiramiz.
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.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private UserService userService;
@Test
void shouldReturnUserById() throws Exception {
when(userService.getUserById(1L))
.thenReturn(new UserResponse(1L, "Ali", "ali@mail.com"));
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Ali"))
.andExpect(jsonPath("$.email").value("ali@mail.com"));
}
}Bu yerda:
$.id → JSON ichidagi id field
$.name → JSON ichidagi name field
$.email → JSON ichidagi email field16. List response test qilish
Endpoint:
GET /usersTest:
@Test
void shouldReturnUsers() throws Exception {
when(userService.getUsers())
.thenReturn(List.of(
new UserResponse(1L, "Ali", "ali@mail.com"),
new UserResponse(2L, "Vali", "vali@mail.com")
));
mockMvc.perform(get("/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(2))
.andExpect(jsonPath("$[0].name").value("Ali"))
.andExpect(jsonPath("$[1].name").value("Vali"));
}JSON array bo‘lsa:
$[0] → birinchi element
$[1] → ikkinchi element17. POST endpoint test qilish
Endi create endpoint qo‘shamiz.
Request DTO
public record CreateUserRequest(
String name,
String email
) {
}Service
public UserResponse createUser(CreateUserRequest request) {
return new UserResponse(1L, request.name(), request.email());
}Controller
@PostMapping
public ResponseEntity<UserResponse> createUser(
@RequestBody CreateUserRequest request
) {
UserResponse response = userService.createUser(request);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(response);
}18. POST test
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.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private UserService userService;
@Test
void shouldCreateUser() throws Exception {
when(userService.createUser(any(CreateUserRequest.class)))
.thenReturn(new UserResponse(1L, "Ali", "ali@mail.com"));
String requestJson = """
{
"name": "Ali",
"email": "ali@mail.com"
}
""";
mockMvc.perform(post("/users")
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Ali"))
.andExpect(jsonPath("$.email").value("ali@mail.com"));
}
}Muhim qismlar:
.contentType(MediaType.APPLICATION_JSON) → JSON yuboryapmiz
.content(requestJson) → request body
status().isCreated() → 201 Created kutyapmiz19. any() nima?
when(userService.createUser(any(CreateUserRequest.class)))Bu Mockito’dan keladi.
Ma’nosi:
createUser()ichiga istalganCreateUserRequestkelsa, mana bu response qaytar.
Agar aniq object solishtirmoqchi bo‘lsang, keyin ArgumentCaptor o‘rganiladi. boshlang'ich bosqichda any() yetarli.
20. @SpringBootTest + MockMvc
Ba’zan butun application context bilan endpoint test qilamiz.
@SpringBootTest
@AutoConfigureMockMvc
class UserApiIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturnUsers() throws Exception {
mockMvc.perform(get("/users"))
.andExpect(status().isOk());
}
}Bu @WebMvcTestdan farqli:
Annotation | Nima ochiladi? |
|---|---|
| Faqat controller/web layer |
| Butun Spring Boot context |
21. @WebMvcTest vs @SpringBootTest
Mezon |
|
|
|---|---|---|
Maqsad | Controller test | Integration test |
Tezlik | Tez | Sekinroq |
Service | Mock qilinadi | Haqiqiy bean |
Repository | Odatda yo‘q | Haqiqiy yoki test DB |
Qachon? | Endpoint response/status tekshirish | Layerlar birga ishlashini tekshirish |
Asosiy qoida:
Controller alohida test → @WebMvcTest
Butun app integration test → @SpringBootTest22. Service unit test
Service test uchun ko‘pincha Spring shart emas.
Masalan:
class UserServiceTest {
@Test
void shouldReturnUserById() {
UserService userService = new UserService();
UserResponse response = userService.getUserById(1L);
assertThat(response.id()).isEqualTo(1L);
assertThat(response.name()).isEqualTo("Ali");
}
}Agar service repository’ga bog‘liq bo‘lsa, repository mock qilinadi.
23. Service + mock repository
Repository
public interface UserRepository extends JpaRepository<User, Long> {
}Service
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public UserResponse getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User topilmadi"));
return new UserResponse(user.getId(), user.getName(), user.getEmail());
}
}Test
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldReturnUserById() {
User user = new User("Ali", "ali@mail.com");
user.setId(1L);
when(userRepository.findById(1L))
.thenReturn(Optional.of(user));
UserResponse response = userService.getUserById(1L);
assertThat(response.id()).isEqualTo(1L);
assertThat(response.name()).isEqualTo("Ali");
assertThat(response.email()).isEqualTo("ali@mail.com");
}
}Bu yerda Spring yo‘q. Mockito bor.
24. @Mock vs @MockitoBean
Annotation | Qayerda ishlatiladi? |
|---|---|
| Oddiy unit testda, Spring context yo‘q |
| Mocklarni class constructoriga beradi |
| Spring test context ichida mock bean yaratadi |
| Eskiroq Spring Boot loyihalarda ishlatiladi |
Spring Boot 3.4+:
@MockitoBean
private UserService userService;Eski loyihalarda ko‘proq ko‘rasan:
@MockBean
private UserService userService;25. Test naming
Test nomi tushunarli bo‘lishi kerak.
Yomon:
@Test
void test1() {
}Yaxshi:
@Test
void shouldReturnUserById() {
}Yana yaxshi:
@Test
void shouldReturn404WhenUserNotFound() {
}26. Test structure: Arrange, Act, Assert
Yaxshi test odatda 3 qismdan iborat:
Arrange → test uchun data tayyorlash
Act → method yoki endpoint chaqirish
Assert → natijani tekshirishMisol:
@Test
void shouldReturnUserById() {
// Arrange
UserService userService = new UserService();
// Act
UserResponse response = userService.getUserById(1L);
// Assert
assertThat(response.name()).isEqualTo("Ali");
}27. Error case test qilish
Faqat muvaffaqiyatli holatni emas, xato holatni ham test qilish kerak.
Masalan service user topilmasa exception tashlaydi:
public UserResponse getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User topilmadi"));
return new UserResponse(user.getId(), user.getName(), user.getEmail());
}Test:
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.when;
@Test
void shouldThrowExceptionWhenUserNotFound() {
when(userRepository.findById(99L))
.thenReturn(Optional.empty());
assertThatThrownBy(() -> userService.getUserById(99L))
.isInstanceOf(RuntimeException.class)
.hasMessage("User topilmadi");
}28. Controller 404 test
Real loyihada exception handler bo‘lsa, 404 qaytarish mumkin.
Masalan:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}Service:
public UserResponse getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User topilmadi"));
return new UserResponse(user.getId(), user.getName(), user.getEmail());
}Test:
@Test
void shouldReturn404WhenUserNotFound() throws Exception {
when(userService.getUserById(99L))
.thenThrow(new UserNotFoundException("User topilmadi"));
mockMvc.perform(get("/users/99"))
.andExpect(status().isNotFound());
}29. Eng ko‘p uchraydigan xatolar
1. @WebMvcTestda service mock qilinmagan
Xato:
No qualifying bean of type 'UserService' availableSabab:
@WebMvcTest faqat controller layerni ochadi
UserService bean sifatida topilmaydi
Uni @MockitoBean yoki @MockBean qilish kerakTo‘g‘ri:
@MockitoBean
private UserService userService;2. POST testda contentType yo‘q
Xato:
mockMvc.perform(post("/users")
.content(requestJson))To‘g‘ri:
mockMvc.perform(post("/users")
.contentType(MediaType.APPLICATION_JSON)
.content(requestJson))3. JSON path noto‘g‘ri
Agar response object bo‘lsa:
{
"id": 1,
"name": "Ali"
}To‘g‘ri:
jsonPath("$.name").value("Ali")Agar response array bo‘lsa:
[
{
"id": 1,
"name": "Ali"
}
]To‘g‘ri:
jsonPath("$[0].name").value("Ali")4. Testga butun Spring contextni keraksiz ochish
Yomon:
@SpringBootTest
class UserServiceTest {
}Agar faqat service logic test qilinayotgan bo‘lsa, oddiy Mockito unit test yetarli.
30. Interview uchun javoblar
Savol: @SpringBootTest nima?
@SpringBootTestbutun Spring Boot application context’ni ishga tushirib test qiladi. Integration testlar uchun ishlatiladi.
Savol: @WebMvcTest nima?
@WebMvcTestfaqat Spring MVC/controller layer’ni test qilish uchun ishlatiladi. Service va repository odatda mock qilinadi.
Savol: MockMvc nima?
MockMvchaqiqiy server ochmasdan Spring MVC endpointlarini test qilish imkonini beradi.
Savol: @MockBean yoki @MockitoBean nima?
Spring test context ichida haqiqiy bean o‘rniga mock bean qo‘yish uchun ishlatiladi. Spring Boot 3.4+ da
@MockitoBeantavsiya qilinadi, eski loyihalarda@MockBeanko‘p uchraydi.
Savol: Unit test va integration test farqi?
Unit test bitta class yoki methodni alohida tekshiradi. Integration test esa bir nechta layer yoki butun application context birga ishlashini tekshiradi.
31. Boshlang'ich uchun xulosa
Bugungi mavzudan eslab qoling:
Test - kod to‘g‘ri ishlashini avtomatik tekshiradi
@SpringBootTest - butun Spring contextni ochadi
@WebMvcTest - faqat controller/web layerni test qiladi
MockMvc - endpointlarni server ochmasdan test qiladi
@MockitoBean / @MockBean - Spring context ichida mock bean beradi
@Mock - oddiy Mockito mock
@InjectMocks - mocklarni classga inject qiladi
status().isOk() - 200 OK tekshiradi
jsonPath("$.name") - JSON field tekshiradi32. Amaliy vazifa
ProductController uchun test yoz:
Endpointlar
GET /products/1
GET /products
POST /productsResponse DTO
public record ProductResponse(
Long id,
String name,
Double price
) {
}Request DTO
public record CreateProductRequest(
String name,
Double price
) {
}Testlar
shouldReturnProductById()
shouldReturnProducts()
shouldCreateProduct()
shouldReturn404WhenProductNotFound()