مقدمة: لماذا نحتاج إلى الاختبار في Java؟
\nكل مطور Java واجه مرة واحدة على الأقل موقفًا حيث "انهار" النظام بأكمله بعد تغيير سطر واحد من الكود. لتجنب مثل هذه الكوارث، طورت الصناعة نظامًا متكاملًا للاختبار. في هذه المقالة، سنشرح ثلاثة مستويات من الاختبار في Java: unit (اختبار الوحدة)، integration (اختبار التكامل)، وe2e (الاختبار الشامل). ستتعرف على الاختلافات بينها، والأدوات التي يجب استخدامها، وكيفية كتابة الاختبارات بشكل صحيح في Java.
\nبغض النظر عما إذا كنت تكتب خدمات مصغرة على Spring Boot أو تطبيقات سطح المكتب، فإن فهم هرم الاختبار هو مهارة أساسية. هيا بنا!
\n\n1. اختبار الوحدة: أساس الجودة
\nاختبارات الوحدة تتحقق من عمل الفئات أو الطرق الفردية بمعزل عن التبعيات الخارجية. هذا هو أسرع وأرخص مستوى من الاختبار.
\n\n1.1 الأدوات الأساسية
\n- \n
- JUnit 5 — الإطار القياسي لـ Java (الإصدار الأحدث هو Jupiter). \n
- Mockito — مكتبة لإنشاء كائنات mock (كائنات وهمية). \n
- AssertJ — مكتبة تحتوي على مجموعة غنية من التأكيدات السلسة (fluent assertions). \n
1.2 مثال على اختبار وحدة باستخدام JUnit 5 و Mockito
\nلنفترض أن لدينا خدمة UserService تعتمد على مستودع UserRepository:
public class UserService {\n private final UserRepository repository;\n\n public UserService(UserRepository repository) {\n this.repository = repository;\n }\n\n public String getUserName(Long id) {\n User user = repository.findById(id);\n return user != null ? user.getName() : "Unknown";\n }\n}\nسنكتب اختبار وحدة، مع عزل التبعية الخارجية باستخدام Mockito:
\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.when;\n\n@ExtendWith(MockitoExtension.class)\nclass UserServiceTest {\n\n @Mock\n private UserRepository userRepository;\n\n @InjectMocks\n private UserService userService;\n\n @Test\n void testGetUserName_WhenUserExists() {\n // ترتيب\n User mockUser = new User(1L, "Alice");\n when(userRepository.findById(1L)).thenReturn(mockUser);\n\n // تنفيذ\n String result = userService.getUserName(1L);\n\n // تأكيد\n assertEquals("Alice", result);\n }\n\n @Test\n void testGetUserName_WhenUserNotFound() {\n when(userRepository.findById(99L)).thenReturn(null);\n\n String result = userService.getUserName(99L);\n\n assertEquals("Unknown", result);\n }\n}\nقاعدة حسن السير: اختبار واحد — فحص واحد. استخدم أسماء طرق واضحة (مثل testGetUserName_WhenUserExists).
\n\n2. اختبار التكامل: التحقق من التفاعلات
\nاختبارات التكامل تتحقق من كيفية عمل مكونات النظام معًا: قاعدة البيانات، نظام الملفات، واجهات API الخارجية. إنها أبطأ من اختبارات الوحدة، لكنها تمنح ثقة أكبر في صحة الترابط.
\n\n2.1 أدوات اختبار التكامل في Java
\n- \n
- Spring Boot Test —
@SpringBootTestلرفع سياق التطبيق. \n - Testcontainers — تشغيل قواعد بيانات حقيقية (PostgreSQL, MySQL) في حاويات Docker. \n
- H2 — قاعدة بيانات في الذاكرة للاختبارات السريعة. \n
2.2 مثال على اختبار تكامل باستخدام Spring Boot و Testcontainers
\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.t