بالدرسة رح نعمل Unit Tests للـ Controller والـ Service. الـ Unit Test بتركز على وحدة واحدة (Function أو Class) بمعزل عن باقي المكونات.
بالـ Unit Test ما بهمنا نختبر الـ Database أو الإيميل أو أي Service خارجي. همنا بس نتأكد إنه الكود المحدد يعمل صح.
الـ Mock هو إحلال أي Dependency خارجية بـ Fake Implementation. بدل ما نروح الـ Database، بنقوله "خذ هالـ Data الوهمية وكفي الكود".
Spring Boot بيجي مع spring-boot-starter-test تلقائياً. بنضيف كمان:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
بننشئ Class بمجلد test:
@WebMvcTest(EmployeeController.class)
class EmployeeControllerTest {
@MockBean
private JwtHelper jwtHelper;
@MockBean
private EmployeeService employeeService;
@MockBean
private LeaveRequestService leaveRequestService;
@Autowired
private
اشترك في النشرة البريدية
دروس جديدة، مقالات، وأدوات مباشرة لبريدك.
@WebMvcTest(EmployeeController.class) - يحمّل بس هالـ Controller، مش كل التطبيق@MockBean - يحل كل Dependency خارجية بـ MockMockMvc - يسمح لنا نبعث HTTP Requests وهمية@Test
void shouldReturnAllEmployees() throws Exception {
// Arrange - تجهيز الداتا الوهمية
List<Employee> fakeEmployees = List.of(
new Employee(UUID.randomUUID(), "John", "Doe", "john@example.com",
"555-1234", LocalDate.now(), department)
);
Mockito.when(employeeService.findAll(0, 3))
.thenReturn(fakeEmployees);
// Act - تنفيذ الـ Request
mockMvc.perform(
MockMvcRequestBuilders.get("/employees")
.with(SecurityMockMvcRequestPostProcessors.user("john").roles("ADMIN"))
)
// Assert - التحقق من النتيجة
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.data[0].firstName").value("John"));
}الـ Test بيمر على ثلاث مراحل:
| المرحلة | الوصف |
|---|---|
| Arrange | تجهيز البيانات والـ Mocks |
| Act | تنفيذ العملية |
| Assert | التحقق من النتيجة |
إذا كان في كود مشترك بين أكثر من Test Case، بنحطه بالـ @BeforeEach:
private Department department;
private UUID departmentId;
@BeforeEach
void setup() {
departmentId = UUID.randomUUID();
department = new Department(departmentId, "IT");
}هاي بتتشغل قبل كل Test Case وبتعيد إنشاء الـ Objects من جديد.
لو بدنا نتأكد إنه Method معين بيعمل Throw لـ Exception بحالات معينة:
@Test
void shouldThrowExceptionWhenDepartmentNotFound() {
// Arrange
Mockito.when(departmentRepository.findById(departmentId))
.thenReturn(Optional.empty());
// Act & Assert
CustomResponseException exception = assertThrows(
CustomResponseException.class,
() -> employeeService.createOne(employeeCreateDto)
);
assertTrue(exception.getMessage().contains("Department with id"));
}@Test
void shouldSendEmailOnceWhenCreatingEmployee() {
// ...
// تأكيد إنه الإيميل انبعث مرة وحدة بس
Mockito.verify(emailService, Mockito.times(1))
.sendAccountCreationEmail(anyString(), anyString());
}verify بيتأكد إنه Method معين انستدعى عدد مرات محدد. مفيد لاكتشاف Bugs زي استدعاء الإيميل أكثر من مرة بالغلط.
نفس المنطق بينطبق على الـ Service:
@ExtendWith(MockitoExtension.class)
class EmployeeServiceTest {
@Mock
private EmployeeRepository employeeRepository;
@Mock
private DepartmentRepository departmentRepository;
@InjectMocks
private EmployeeServiceImpl employeeService;
@Test
void shouldCreateEmployee() {
Mockito.when(departmentRepository.findById(departmentId))
.thenReturn(Optional.of(department));
Employee result = employeeService.createOne(employeeCreateDto);
assertEquals("John", result.getFirstName());
}
}الـ Unit Tests ما بتحكي مع قاعدة بيانات حقيقية. كل الـ Repositories والـ Services الخارجية يصيروا Mocks. هالشي بيجعل التستس:
بالدرس الجاي رح نحكي عن Integration Tests التي بتختبر الكود من البداية للنهاية مع Database حقيقية.