MS Lesson 19: Unit Test
1. Unit test klasinin uzerine: @ExtendWith(MockitoExtension.class)
2.
package guru.springframework.cruddemo.testing;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class CalculatorServiceTest {
@InjectMocks
private CalculatorService calculatorService;
@Test
public void whenSumTwoNumbersThenSuccess() {
// Arrange
// Act
int result = calculatorService.sum(5, 7);
// Assert
Assertions.assertThat(result).isEqualTo(12);
}
}
3.
package guru.springframework.cruddemo.testing;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class CalculatorServiceTest {
@InjectMocks
private CalculatorService calculatorService;
@Test
public void whenSumTwoNumbersThenSuccess() {
// Arrange
// Act
int result = calculatorService.sum(5, 7);
// Assert
Assertions.assertThat(result).isEqualTo(12);
}
@Test
public void whenFirstDivideSecondThenSuccess() {
// Arrange
// Act
int result = calculatorService.divide(10, 2);
// Assert
Assertions.assertThat(result).isEqualTo(5);
}
@Test
public void whenFirstDivideToZeroThenException() {
// Arrange
// Act
// Assert
Assertions.assertThatThrownBy(() -> calculatorService.divide(5, 0))
.isInstanceOf(ArithmeticException.class)
.hasMessage("/ by zero");
}
}
4. Parametrized test
@ParameterizedTest(name = "sum({0}, {1}) should be {2}")
@CsvSource({
"5, 7, 12",
"0, 0, 0",
"-1, 1, 0",
"100, 200, 300"
})
void whenSumTwoNumbersThenSuccess(int first, int second, int expected) {
int result = calculatorService.sum(first, second);
Assertions.assertThat(result).isEqualTo(expected);
}
5.
package guru.springframework.cruddemo.service.impl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Optional;
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 guru.springframework.cruddemo.dto.response.AccountResponse;
import guru.springframework.cruddemo.entity.Account;
import guru.springframework.cruddemo.mapper.AccountMapperMapstruct;
import guru.springframework.cruddemo.repository.AccountRepository;
@ExtendWith(MockitoExtension.class)
class AccountServiceImplTest {
@Mock
private AccountRepository accountRepository;
@Mock
private AccountMapperMapstruct mapperMapstruct;
@InjectMocks
private AccountServiceImpl accountService;
@Test
void getById_whenAccountExists_returnsMappedResponse() {
// Arrange
Long id = 1L;
Account account = new Account();
account.setId(id);
account.setName("Ali");
AccountResponse expected = new AccountResponse();
expected.setName("Ali");
when(accountRepository.findById(id)).thenReturn(Optional.of(account));
when(mapperMapstruct.toResponse(account)).thenReturn(expected);
// Act
AccountResponse result = accountService.getById(id);
// Assert
assertThat(result).isEqualTo(expected);
verify(accountRepository, times(1)).findById(id);
verify(mapperMapstruct, times(1)).toResponse(account);
}
@Test
void getById_whenAccountNotFound_throwsRuntimeException() {
Long id = 99L;
when(accountRepository.findById(id)).thenReturn(Optional.empty());
assertThatThrownBy(() -> accountService.getById(id))
.isInstanceOf(RuntimeException.class)
.hasMessage("-------> Xeta");
verify(mapperMapstruct, never()).toResponse(any());
}
}
=================================================================================================================================================================================================================================
package guru.springframework.cruddemo.service.impl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.Optional;
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 guru.springframework.cruddemo.dto.request.CreateAccountRequest;
import guru.springframework.cruddemo.dto.request.UpdateAccountRequest;
import guru.springframework.cruddemo.dto.response.AccountResponse;
import guru.springframework.cruddemo.entity.Account;
import guru.springframework.cruddemo.mapper.AccountMapperMapstruct;
import guru.springframework.cruddemo.repository.AccountRepository;
@ExtendWith(MockitoExtension.class)
class AccountServiceImplTest {
@Mock
private AccountRepository accountRepository;
@Mock
private AccountMapperMapstruct accountMapper;
@InjectMocks
private AccountServiceImpl accountService;
@Test
void create_whenValidRequest_savesAndReturnsMappedResponse() {
// Given
CreateAccountRequest request = new CreateAccountRequest();
request.setName("Ali");
request.setAmount(100);
Account entity = new Account();
entity.setName("Ali");
entity.setAmount(100);
Account saved = new Account();
saved.setId(1L);
saved.setName("Ali");
saved.setAmount(100);
AccountResponse expected = new AccountResponse();
expected.setId(1L);
expected.setName("Ali");
expected.setAmount(100);
// When
when(accountMapper.toEntity(request)).thenReturn(entity);
when(accountRepository.save(entity)).thenReturn(saved);
when(accountMapper.toResponse(saved)).thenReturn(expected);
AccountResponse result = accountService.create(request);
// Then
assertThat(result).isEqualTo(expected);
verify(accountMapper).toEntity(request);
verify(accountRepository).save(entity);
verify(accountMapper).toResponse(saved);
}
@Test
void getById_whenAccountExists_returnsMappedResponse() {
// Given
Long id = 1L;
Account account = account(id, "Ali", 100);
AccountResponse expected = response(id, "Ali", 100);
when(accountRepository.findById(id)).thenReturn(Optional.of(account));
when(accountMapper.toResponse(account)).thenReturn(expected);
// When
AccountResponse result = accountService.getById(id);
// Then
assertThat(result).isEqualTo(expected);
verify(accountRepository).findById(id);
verify(accountMapper).toResponse(account);
}
@Test
void getById_whenAccountNotFound_throwsRuntimeException() {
// Given
Long id = 99L;
when(accountRepository.findById(id)).thenReturn(Optional.empty());
// When & Then
assertThatThrownBy(() -> accountService.getById(id))
.isInstanceOf(RuntimeException.class)
.hasMessage("Account not found: 99");
verify(accountMapper, never()).toResponse(any());
}
@Test
void getAll_whenAccountsExist_returnsMappedList() {
// Given
Account first = account(1L, "Ali", 100);
Account second = account(2L, "Leyla", 200);
when(accountRepository.findAll()).thenReturn(List.of(first, second));
when(accountMapper.toResponse(first)).thenReturn(response(1L, "Ali", 100));
when(accountMapper.toResponse(second)).thenReturn(response(2L, "Leyla", 200));
// When
List<AccountResponse> result = accountService.getAll();
// Then
assertThat(result).hasSize(2);
assertThat(result.get(0).getName()).isEqualTo("Ali");
assertThat(result.get(1).getName()).isEqualTo("Leyla");
verify(accountRepository).findAll();
}
@Test
void getAll_whenNoAccounts_returnsEmptyList() {
// Given
when(accountRepository.findAll()).thenReturn(List.of());
// When
List<AccountResponse> result = accountService.getAll();
// Then
assertThat(result).isEmpty();
verify(accountMapper, never()).toResponse(any());
}
@Test
void update_whenAccountExists_updatesAndReturnsMappedResponse() {
// Given
Long id = 1L;
UpdateAccountRequest request = new UpdateAccountRequest();
request.setName("Ali updated");
request.setAmount(150);
Account account = account(id, "Ali", 100);
AccountResponse expected = response(id, "Ali updated", 150);
when(accountRepository.findById(id)).thenReturn(Optional.of(account));
when(accountMapper.toResponse(account)).thenReturn(expected);
// When
AccountResponse result = accountService.update(id, request);
// Then
assertThat(result).isEqualTo(expected);
verify(accountMapper).updateFromRequest(request, account);
verify(accountMapper).toResponse(account);
}
@Test
void update_whenAccountNotFound_throwsRuntimeException() {
// Given
Long id = 99L;
UpdateAccountRequest request = new UpdateAccountRequest();
request.setName("X");
request.setAmount(1);
when(accountRepository.findById(id)).thenReturn(Optional.empty());
// When & Then
assertThatThrownBy(() -> accountService.update(id, request))
.isInstanceOf(RuntimeException.class)
.hasMessage("Account not found: 99");
verify(accountMapper, never()).updateFromRequest(any(), any());
}
@Test
void delete_whenAccountExists_deletesAccount() {
// Given
Long id = 1L;
Account account = account(id, "Ali", 100);
when(accountRepository.findById(id)).thenReturn(Optional.of(account));
// When
accountService.delete(id);
// Then
verify(accountRepository).delete(account);
}
@Test
void delete_whenAccountNotFound_throwsRuntimeException() {
// Given
Long id = 99L;
when(accountRepository.findById(id)).thenReturn(Optional.empty());
// When & Then
assertThatThrownBy(() -> accountService.delete(id))
.isInstanceOf(RuntimeException.class)
.hasMessage("Account not found: 99");
verify(accountRepository, never()).delete(any());
}
private static Account account(Long id, String name, int amount) {
Account account = new Account();
account.setId(id);
account.setName(name);
account.setAmount(amount);
return account;
}
private static AccountResponse response(Long id, String name, int amount) {
AccountResponse response = new AccountResponse();
response.setId(id);
response.setName(name);
response.setAmount(amount);
return response;
}
}
--------------------------------------------------------------------------------------------------------------------------------
package guru.springframework.cruddemo.controller;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import guru.springframework.cruddemo.dto.request.CreateAccountRequest;
import guru.springframework.cruddemo.dto.request.UpdateAccountRequest;
import guru.springframework.cruddemo.dto.response.AccountResponse;
import guru.springframework.cruddemo.service.AccountService;
@WebMvcTest(AccountController.class)
@Import(AccountControllerTest.TestRuntimeExceptionHandler.class)
class AccountControllerTest {
/**
* Yalnız MVC test slice üçündür — proyektə global handler yoxdur,
* amma MockMvc-də RuntimeException-in HTTP 500-ə çevrilməsini yoxlayırıq.
*/
@RestControllerAdvice
static class TestRuntimeExceptionHandler {
@ExceptionHandler(RuntimeException.class)
ResponseEntity<Map<String, String>> handle(RuntimeException ex) {
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("message", ex.getMessage()));
}
}
@Autowired
private MockMvc mockMvc;
@MockitoBean
private AccountService accountService;
@Test
void create_whenValidRequest_returns201AndBody() throws Exception {
// Given
AccountResponse created = response(1L, "Ali", 100);
when(accountService.create(any(CreateAccountRequest.class))).thenReturn(created);
// When & Then
mockMvc.perform(post("/accounts")
.contentType(APPLICATION_JSON)
.content("""
{"name":"Ali","amount":100}
"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Ali"))
.andExpect(jsonPath("$.amount").value(100));
verify(accountService).create(any(CreateAccountRequest.class));
}
@Test
void create_whenNameBlank_returns400() throws Exception {
// Given — service çağırılmamalı
// When & Then
mockMvc.perform(post("/accounts")
.contentType(APPLICATION_JSON)
.content("""
{"name":"","amount":100}
"""))
.andExpect(status().isBadRequest());
}
@Test
void create_whenAmountNegative_returns400() throws Exception {
// When & Then
mockMvc.perform(post("/accounts")
.contentType(APPLICATION_JSON)
.content("""
{"name":"Ali","amount":-1}
"""))
.andExpect(status().isBadRequest());
}
@Test
void getById_whenAccountExists_returns200AndBody() throws Exception {
// Given
Long id = 1L;
when(accountService.getById(id)).thenReturn(response(id, "Ali", 100));
// When & Then
mockMvc.perform(get("/accounts/{id}", id))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Ali"))
.andExpect(jsonPath("$.amount").value(100));
verify(accountService).getById(id);
}
@Test
void getById_whenAccountNotFound_returns500() throws Exception {
// Given
Long id = 99L;
when(accountService.getById(id))
.thenThrow(new RuntimeException("Account not found: 99"));
// When & Then
mockMvc.perform(get("/accounts/{id}", id))
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.message").value("Account not found: 99"));
verify(accountService).getById(id);
}
@Test
void getAll_whenAccountsExist_returns200AndList() throws Exception {
// Given
when(accountService.getAll()).thenReturn(List.of(
response(1L, "Ali", 100),
response(2L, "Leyla", 200)));
// When & Then
mockMvc.perform(get("/accounts"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(2))
.andExpect(jsonPath("$[0].name").value("Ali"))
.andExpect(jsonPath("$[1].name").value("Leyla"));
verify(accountService).getAll();
}
@Test
void getAll_whenNoAccounts_returns200AndEmptyList() throws Exception {
// Given
when(accountService.getAll()).thenReturn(List.of());
// When & Then
mockMvc.perform(get("/accounts"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(0));
verify(accountService).getAll();
}
@Test
void update_whenValidRequest_returns200AndBody() throws Exception {
// Given
Long id = 1L;
when(accountService.update(eq(id), any(UpdateAccountRequest.class)))
.thenReturn(response(id, "Ali updated", 150));
// When & Then
mockMvc.perform(put("/accounts/{id}", id)
.contentType(APPLICATION_JSON)
.content("""
{"name":"Ali updated","amount":150}
"""))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("Ali updated"))
.andExpect(jsonPath("$.amount").value(150));
verify(accountService).update(eq(id), any(UpdateAccountRequest.class));
}
@Test
void update_whenNameBlank_returns400() throws Exception {
// When & Then
mockMvc.perform(put("/accounts/{id}", 1L)
.contentType(APPLICATION_JSON)
.content("""
{"name":"","amount":100}
"""))
.andExpect(status().isBadRequest());
}
@Test
void update_whenAccountNotFound_returns500() throws Exception {
// Given
Long id = 99L;
when(accountService.update(eq(id), any(UpdateAccountRequest.class)))
.thenThrow(new RuntimeException("Account not found: 99"));
// When & Then
mockMvc.perform(put("/accounts/{id}", id)
.contentType(APPLICATION_JSON)
.content("""
{"name":"X","amount":1}
"""))
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.message").value("Account not found: 99"));
}
@Test
void delete_whenAccountExists_returns204() throws Exception {
// Given
Long id = 1L;
// When & Then
mockMvc.perform(delete("/accounts/{id}", id))
.andExpect(status().isNoContent());
verify(accountService).delete(id);
}
@Test
void delete_whenAccountNotFound_returns500() throws Exception {
// Given
Long id = 99L;
doThrow(new RuntimeException("Account not found: 99"))
.when(accountService).delete(id);
// When & Then
mockMvc.perform(delete("/accounts/{id}", id))
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.message").value("Account not found: 99"));
verify(accountService).delete(id);
}
private static AccountResponse response(Long id, String name, int amount) {
AccountResponse response = new AccountResponse();
response.setId(id);
response.setName(name);
response.setAmount(amount);
return response;
}
}
Комментарии
Отправить комментарий