اختبار التكامل في Java مع Docker: من الألف إلى الياء
\n\nاختبار التكامل هو مرحلة أساسية في التطوير، تتيح التحقق من تفاعل مكونات التطبيق مع الأنظمة الخارجية: قواعد البيانات، قوائم انتظار الرسائل، خدمات REST. غالبًا ما يؤدي النهج التقليدي باستخدام H2 أو الخوادم المدمجة إلى اختلافات بين بيئة الاختبار وبيئة الإنتاج. الحل هو Docker. في هذه المقالة، سنشرح كيفية إعداد اختبار التكامل لتطبيقات Java باستخدام Docker، مع استخدام مكتبة Testcontainers و Docker Compose. ستتعلم كيفية كتابة اختبارات تعمل مع حاويات حقيقية، وكيفية دمجها في CI/CD.
\n\nلماذا Docker لاختبارات التكامل؟
\nيتيح Docker رفع حاويات معزولة بإصدارات دقيقة من التبعيات (PostgreSQL، Redis، Kafka) مباشرة قبل الاختبارات وتدميرها بعد ذلك. وهذا يضمن:
\n- \n
- التطابق مع بيئة الإنتاج — تختبر بقاعدة بيانات حقيقية، وليس بمحاكاة. \n
- العزل — كل تشغيل اختبار يبدأ بحالة نظيفة. \n
- التشغيل المتوازي — يمكن لعدة فئات اختبار استخدام حاويات مختلفة. \n
- قابلية إعادة الإنتاج — تعمل الاختبارات بنفس الطريقة على الجهاز المحلي وفي CI. \n
إعداد المشروع مع Testcontainers
\nTestcontainers هي مكتبة Java تدير دورة حياة حاويات Docker. أضف التبعية في pom.xml (Maven):
<dependency>\n <groupId>org.testcontainers</groupId>\n <artifactId>testcontainers</artifactId>\n <version>1.19.3</version>\n <scope>test</scope>\n</dependency>\n<dependency>\n <groupId>org.testcontainers</groupId>\n <artifactId>postgresql</artifactId>\n <version>1.19.3</version>\n <scope>test</scope>\n</dependency>\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-test</artifactId>\n <scope>test</scope>\n</dependency>\n\nبالنسبة لـ Gradle، أضف في build.gradle:
testImplementation 'org.testcontainers:testcontainers:1.19.3'\ntestImplementation 'org.testcontainers:postgresql:1.19.3'\n\nمثال: اختبار المستودع مع PostgreSQL
\nلننظر إلى خدمة بسيطة للعمل مع المستخدمين. سننشئ اختبارًا يرفع حاوية PostgreSQL ويتحقق من حفظ البيانات.
\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.DynamicPropertyRegistry;\nimport org.springframework.test.context.DynamicPropertySource;\nimport org.testcontainers.containers.PostgreSQLContainer;\nimport org.testcontainers.junit.jupiter.Container;\nimport org.testcontainers.junit.jupiter.Testcontainers;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@Testcontainers\n@SpringBootTest\nclass UserRepositoryTest {\n\n @Container\n static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")\n .withDatabaseName("testdb")\n .withUsername("test")\n .withPassword("test");\n\n @DynamicPropertySource\n static void configureProperties(DynamicPropertyRegistry registry) {\n registry.add("spring.datasource.url", postgres::getJdbcUrl);\n registry.add("spring.datasource.username", postgres::getUsername);\n registry.add("spring.datasource.password", postgres::getPassword);\n }\n\n @Autowired\n private UserRepository userRepository;\n\n @Test\n void shouldSaveAndFindUser() {\n User user = new User("john_doe", "john@example