- اختبار تكامل قاعدة البيانات باستخدام MariaDB لتحل محل MySql
- تنفيذ تطبيق متعدد اللغات
- حفظ الملفات في التطبيق والبيانات المتعلقة بها في قاعدة البيانات
أنواع الاختبار
ما هو الاختبار؟ كما يقول Wiki: " الاختبار أو الاختبار هو وسيلة لدراسة العمليات الأساسية للنظام من خلال وضع النظام في مواقف مختلفة وتتبع التغييرات الملحوظة فيه." بمعنى آخر، هذا اختبار للتشغيل الصحيح لنظامنا في مواقف معينة. حسنًا، دعونا نرى ما هي أنواع الاختبارات الموجودة:-
اختبار الوحدة هو اختبار مهمته اختبار كل وحدة من وحدات النظام على حدة. من المرغوب فيه أن تكون هذه الأجزاء قابلة للتقسيم إلى الحد الأدنى من النظام، على سبيل المثال، الوحدات النمطية.
-
اختبار النظام هو اختبار عالي المستوى لاختبار تشغيل جزء أكبر من التطبيق أو النظام ككل.
-
اختبار الانحدار هو اختبار يُستخدم للتحقق مما إذا كانت الميزات الجديدة أو إصلاحات الأخطاء تؤثر على الوظيفة الحالية للتطبيق وما إذا كانت الأخطاء القديمة ستظهر مرة أخرى.
-
الاختبار الوظيفي هو التحقق من امتثال جزء من التطبيق للمتطلبات المذكورة في المواصفات وقصص المستخدم وما إلى ذلك.
أنواع الاختبارات الوظيفية:
- اختبار "الصندوق الأبيض" لامتثال جزء من الطلب للمتطلبات مع معرفة التنفيذ الداخلي للنظام؛
- اختبار "الصندوق الأسود" لامتثال جزء من التطبيق للمتطلبات دون معرفة التنفيذ الداخلي للنظام.
- اختبار الأداء هو نوع من الاختبارات التي تتم كتابتها لتحديد السرعة التي يعمل بها النظام أو جزء منه تحت حمل معين.
- اختبار الحمل - اختبارات مصممة للتحقق من استقرار النظام تحت الأحمال القياسية والعثور على أقصى ذروة ممكنة يعمل عندها التطبيق بشكل صحيح.
- اختبار التحمل هو نوع من الاختبارات المصممة للتحقق من وظائف التطبيق في ظل الأحمال غير القياسية ولتحديد أقصى ذروة ممكنة لن يتعطل النظام عندها.
- اختبار الأمان - اختبارات تستخدم للتحقق من أمان النظام (من هجمات المتسللين والفيروسات والوصول غير المصرح به إلى البيانات السرية وغيرها من متع الحياة).
- اختبار التعريب هو اختبار التعريب لأحد التطبيقات.
- اختبار قابلية الاستخدام هو نوع من الاختبارات التي تهدف إلى التحقق من سهولة الاستخدام وقابلية الفهم والجاذبية وقابلية التعلم للمستخدمين. كل هذا يبدو جيدًا، ولكن كيف يتم تطبيقه عمليًا؟ الأمر بسيط: يتم استخدام هرم اختبار مايك كوهن: هذه نسخة مبسطة من الهرم: وهي الآن مقسمة إلى أجزاء أصغر. لكننا اليوم لن ننحرف ونفكر في الخيار الأبسط.
-
الوحدة - اختبارات الوحدة المستخدمة في طبقات مختلفة من التطبيق، واختبار أصغر منطق قابل للقسمة للتطبيق: على سبيل المثال، فئة، ولكن في أغلب الأحيان طريقة. تحاول هذه الاختبارات عادةً عزل قدر الإمكان عن المنطق الخارجي، أي خلق الوهم بأن بقية التطبيق يعمل في الوضع القياسي.
يجب أن يكون هناك دائمًا الكثير من هذه الاختبارات (أكثر من الأنواع الأخرى)، نظرًا لأنها تختبر قطعًا صغيرة وخفيفة الوزن جدًا، ولا تستهلك الكثير من الموارد (أعني بالموارد ذاكرة الوصول العشوائي والوقت).
-
التكامل - اختبار التكامل. فهو يتحقق من أجزاء أكبر من النظام، أي أنه إما مزيج من عدة أجزاء من المنطق (عدة طرق أو فئات)، أو صحة العمل مع مكون خارجي. عادة ما يكون عدد هذه الاختبارات أقل من اختبارات الوحدة، لأنها أثقل.
وكمثال على اختبارات التكامل، يمكنك التفكير في الاتصال بقاعدة بيانات والتحقق من التنفيذ الصحيح لطرق العمل معها .
-
واجهة المستخدم - الاختبارات التي تتحقق من تشغيل واجهة المستخدم. إنها تؤثر على المنطق على جميع مستويات التطبيق، ولهذا السبب يطلق عليها أيضًا اسم "النهاية إلى النهاية". كقاعدة عامة، هناك عدد أقل بكثير منهم، لأنهم الأكثر وزنا ويجب عليهم التحقق من المسارات الأكثر ضرورة (المستخدمة).
في الشكل أعلاه نرى نسبة مساحات الأجزاء المختلفة للمثلث: يتم الحفاظ على نفس النسبة تقريبًا في عدد هذه الاختبارات في العمل الحقيقي.
سنلقي اليوم نظرة فاحصة على الاختبارات الأكثر استخدامًا - اختبارات الوحدة، نظرًا لأن جميع مطوري Java الذين يحترمون أنفسهم يجب أن يكونوا قادرين على استخدامها على المستوى الأساسي.
- ومواد حول تغطية التعليمات البرمجية على JavaRush و Habré ؛
- نظرية الاختبار الأساسية .
- نحن نكتب اختبارنا.
- نجري الاختبار، سواء نجح أم لا (نرى أن كل شيء أحمر - لا تخاف: هكذا ينبغي أن يكون).
- نضيف الكود الذي يجب أن يفي بهذا الاختبار (نجري الاختبار).
- نحن نعيد صياغة الكود.
- تحديد البيانات المراد اختبارها (التركيبات).
- استخدام الكود قيد الاختبار (استدعاء الطريقة قيد الاختبار).
- التحقق من النتائج ومقارنتها بالنتائج المتوقعة.
assertEquals(Object expecteds, Object actuals)
— يتحقق مما إذا كانت الكائنات المرسلة متساوية.assertTrue(boolean flag)
- يتحقق مما إذا كانت القيمة التي تم تمريرها ترجع صحيحة.assertFalse(boolean flag)
- يتحقق مما إذا كانت القيمة التي تم تمريرها تُرجع خطأ.assertNull(Object object)
- التحقق مما إذا كان الكائن فارغًا.assertSame(Object firstObject, Object secondObject)
- يتحقق مما إذا كانت القيم التي تم تمريرها تشير إلى نفس الكائن.assertThat(T t, Matcher<T> matcher)
- يتحقق مما إذا كان يفي بالشرط المحدد في المطابق.
المفاهيم الأساسية لاختبار الوحدة
تعد تغطية الاختبار (تغطية الكود) أحد التقييمات الرئيسية لجودة اختبار التطبيق. هذه هي النسبة المئوية للكود الذي غطته الاختبارات (0-100%). من الناحية العملية، يطارد العديد من الأشخاص هذه النسبة، وأنا لا أتفق معها، حيث يبدأون في إضافة اختبارات حيث لا تكون هناك حاجة إليها. على سبيل المثال، تحتوي خدمتنا على عمليات CRUD قياسية (إنشاء/حصول/تحديث/حذف) بدون منطق إضافي. هذه الأساليب عبارة عن وسطاء بحتين يقومون بتفويض العمل إلى الطبقة التي تعمل مع المستودع. في هذه الحالة، ليس لدينا ما نختبره: ربما تستدعي هذه الطريقة طريقة من الطاو، لكن هذا ليس جديًا. لتقييم تغطية الاختبار، يتم عادةً استخدام أدوات إضافية: JaCoCo، وCobertura، وClover، وEmma، وما إلى ذلك. للحصول على دراسة أكثر تفصيلاً حول هذه المشكلة، احتفظ بمقالتين مناسبتين:مراحل الاختبار
يتكون الاختبار من ثلاث مراحل:بيئات الاختبار
والآن دعونا ننتقل إلى العمل. هناك العديد من بيئات الاختبار (الأطر) المتاحة لـ Java. الأكثر شعبية منهم هي JUnit وTestNG. لمراجعتنا، نستخدم: اختبار JUnit هو طريقة موجودة في فئة يتم استخدامها للاختبار فقط. تتم تسمية الفصل عادةً بنفس اسم الفصل الذي يختبره باستخدام +Test في النهاية. على سبيل المثال، CarService → CarServiceTest. يتضمن نظام البناء Maven تلقائيًا مثل هذه الفئات في منطقة الاختبار. في الواقع، تسمى هذه الفئة فئة الاختبار. دعنا نستعرض التعليقات التوضيحية الأساسية قليلاً: @Test - تعريف هذه الطريقة كطريقة اختبار (في الواقع، الطريقة المميزة بهذا التعليق التوضيحي هي اختبار وحدة). @Before - يمثل الطريقة التي سيتم تنفيذها قبل كل اختبار. على سبيل المثال، ملء بيانات اختبار الفصل، وقراءة بيانات الإدخال، وما إلى ذلك. @After - يوضع فوق الطريقة التي سيتم استدعاؤها بعد كل اختبار (تنظيف البيانات، واستعادة القيم الافتراضية). @BeforeClass - يوضع فوق الطريقة - مشابه لـBefore. ولكن يتم استدعاء هذه الطريقة مرة واحدة فقط قبل جميع الاختبارات لفئة معينة وبالتالي يجب أن تكون ثابتة. يتم استخدامه لإجراء المزيد من العمليات الثقيلة، مثل رفع قاعدة بيانات الاختبار. @AfterClass هو عكس @BeforeClass: يتم تنفيذه مرة واحدة لفئة معينة، ولكن يتم تنفيذه بعد كل الاختبارات. يُستخدم، على سبيل المثال، لتنظيف الموارد المستمرة أو قطع الاتصال بقاعدة البيانات. @Ignore - لاحظ أن الطريقة أدناه معطلة وسيتم تجاهلها عند تشغيل الاختبارات بشكل عام. يتم استخدامه في حالات مختلفة، على سبيل المثال، إذا تم تغيير الطريقة الأساسية ولم يكن هناك وقت لإعادة الاختبار لها. في مثل هذه الحالات، يُنصح أيضًا بإضافة وصف - @Ignore("Some description"). @Test (متوقع = Exception.class) - يستخدم للاختبارات السلبية. هذه هي الاختبارات التي تتحقق من سلوك الطريقة في حالة حدوث خطأ، أي أن الاختبار يتوقع من الطريقة أن تطرح بعض الاستثناءات. تتم الإشارة إلى هذه الطريقة من خلال التعليق التوضيحيTest، ولكن مع وجود خطأ يجب اكتشافه. @Test(timeout=100) - يتحقق من تنفيذ الطريقة في مدة لا تزيد عن 100 مللي ثانية. @Mock - يتم استخدام فئة فوق حقل لتعيين كائن معين كنموذج وهمي (هذا ليس من مكتبة Junit، ولكن من Mockito)، وإذا كنا بحاجة إليه، فسنقوم بتعيين سلوك النموذج في موقف معين مباشرة في طريقة الاختبار. @RunWith(MockitoJUnitRunner.class) - يتم وضع الطريقة فوق الفئة. هذا هو الزر لإجراء الاختبارات فيه. يمكن أن يكون المتسابقون مختلفين: على سبيل المثال، هناك ما يلي: MockitoJUnitRunner، JUnitPlatform، SpringRunner، وما إلى ذلك). في JUnit 5، تم استبدال التعليق التوضيحي @RunWith بالتعليق التوضيحي @ExtendWith الأكثر قوة. دعونا نلقي نظرة على بعض الطرق لمقارنة النتائج:assertThat(firstObject).isEqualTo(secondObject)
لقد تحدثت هنا عن الطرق الأساسية، حيث أن الباقي عبارة عن أشكال مختلفة مما سبق.
ممارسة الاختبار
الآن دعونا نلقي نظرة على المواد المذكورة أعلاه باستخدام مثال محدد. سنقوم باختبار طريقة الخدمة - التحديث. لن نأخذ في الاعتبار طبقة الداو، لأنها الطبقة الافتراضية لدينا. دعونا نضيف بداية للاختبارات:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
لذلك، فئة الخدمة:
@Service
@RequiredArgsConstructor
public class RobotServiceImpl implements RobotService {
private final RobotDAO robotDAO;
@Override
public Robot update(Long id, Robot robot) {
Robot found = robotDAO.findById(id);
return robotDAO.update(Robot.builder()
.id(id)
.name(robot.getName() != null ? robot.getName() : found.getName())
.cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
.producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
.build());
}
}
8 - سحب الكائن المحدث من قاعدة البيانات 9-14 - إنشاء الكائن من خلال المنشئ، إذا كان الكائن الوارد يحتوي على حقل - قم بتعيينه، وإذا لم يكن - اترك ما هو موجود في قاعدة البيانات وانظر إلى اختبارنا:
@RunWith(MockitoJUnitRunner.class)
public class RobotServiceImplTest {
@Mock
private RobotDAO robotDAO;
private RobotServiceImpl robotService;
private static Robot testRobot;
@BeforeClass
public static void prepareTestData() {
testRobot = Robot
.builder()
.id(123L)
.name("testRobotMolly")
.cpu("Intel Core i7-9700K")
.producer("China")
.build();
}
@Before
public void init() {
robotService = new RobotServiceImpl(robotDAO);
}
1 - عداءنا 4 - عزل الخدمة عن طبقة الداو عن طريق استبدال نموذج وهمي 11 - قم بتعيين كيان اختبار للفئة (الذي سنستخدمه كهامستر اختباري) 22 - قم بتعيين كائن الخدمة الذي سنختبره
@Test
public void updateTest() {
when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
Robot robotForUpdate = Robot
.builder()
.name("Vally")
.cpu("AMD Ryzen 7 2700X")
.build();
Robot resultRobot = robotService.update(123L, robotForUpdate);
assertNotNull(resultRobot);
assertSame(resultRobot.getId(),testRobot.getId());
assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
assertEquals(resultRobot.getProducer(),testRobot.getProducer());
}
وهنا نرى تقسيم واضح للاختبار إلى ثلاثة أجزاء: 3-9 - ضبط التركيبات 11 - تنفيذ الجزء الذي تم اختباره 13-17 - التحقق من النتائج مزيد من التفاصيل: 3-4 - ضبط السلوك لموكا داو 5 - ضبط مثيل أننا سنقوم بتحديث أعلى معيارنا 11 - استخدم الطريقة وأخذ المثيل الناتج 13 - تأكد من أنه ليس صفرًا 14 - تحقق من معرف النتيجة ووسائط الطريقة المحددة 15 - تحقق مما إذا كان الاسم قد تم تحديثه 16 - انظر في النتيجة بواسطة وحدة المعالجة المركزية 17 - نظرًا لأننا لم نقم بتعيين هذا في حقل مثيل التحديث، فيجب أن يظل كما هو، فلنتحقق منه. لنبدأ: الاختبار أخضر، يمكنك الزفير)) لذا، دعونا نلخص: الاختبار يعمل على تحسين جودة الكود ويجعل عملية التطوير أكثر مرونة وموثوقية. تخيل مقدار الجهد الذي يتعين علينا بذله عند إعادة تصميم البرنامج باستخدام مئات الملفات الصفية. بمجرد كتابة اختبارات الوحدة لجميع هذه الفئات، يمكننا إعادة البناء بثقة. والأهم من ذلك أنه يساعدنا في العثور بسهولة على الأخطاء أثناء التطوير. يا رفاق، هذا كل شيء بالنسبة لي اليوم: أضف الإعجابات، واكتب التعليقات)))
GO TO FULL VERSION