JavaRush /مدونة جافا /Random-AR /اختبار وحدة جافا: التقنيات والمفاهيم والممارسات

اختبار وحدة جافا: التقنيات والمفاهيم والممارسات

نشرت في المجموعة
من غير المرجح أن تجد اليوم تطبيقًا غير مغطى بالاختبارات، لذلك سيكون هذا الموضوع أكثر أهمية من أي وقت مضى للمطورين المبتدئين: بدون اختبارات لا يمكنك الوصول إلى أي مكان. كإعلان، أقترح عليك إلقاء نظرة على مقالاتي السابقة. يغطي بعضها الاختبارات (ومع ذلك ستكون المقالات مفيدة جدًا):
  1. اختبار تكامل قاعدة البيانات باستخدام MariaDB لتحل محل MySql
  2. تنفيذ تطبيق متعدد اللغات
  3. حفظ الملفات في التطبيق والبيانات المتعلقة بها في قاعدة البيانات
دعونا نفكر في أنواع الاختبارات المستخدمة من حيث المبدأ، وبعد ذلك سندرس بالتفصيل كل ما تحتاج لمعرفته حول اختبار الوحدة.

أنواع الاختبار

ما هو الاختبار؟ كما يقول Wiki: " الاختبار أو الاختبار هو وسيلة لدراسة العمليات الأساسية للنظام من خلال وضع النظام في مواقف مختلفة وتتبع التغييرات الملحوظة فيه." بمعنى آخر، هذا اختبار للتشغيل الصحيح لنظامنا في مواقف معينة. كل شيء عن اختبار الوحدة: الأساليب والمفاهيم والممارسات - 2حسنًا، دعونا نرى ما هي أنواع الاختبارات الموجودة:
  1. اختبار الوحدة هو اختبار مهمته اختبار كل وحدة من وحدات النظام على حدة. من المرغوب فيه أن تكون هذه الأجزاء قابلة للتقسيم إلى الحد الأدنى من النظام، على سبيل المثال، الوحدات النمطية.

  2. اختبار النظام هو اختبار عالي المستوى لاختبار تشغيل جزء أكبر من التطبيق أو النظام ككل.

  3. اختبار الانحدار هو اختبار يُستخدم للتحقق مما إذا كانت الميزات الجديدة أو إصلاحات الأخطاء تؤثر على الوظيفة الحالية للتطبيق وما إذا كانت الأخطاء القديمة ستظهر مرة أخرى.

  4. الاختبار الوظيفي هو التحقق من امتثال جزء من التطبيق للمتطلبات المذكورة في المواصفات وقصص المستخدم وما إلى ذلك.

    أنواع الاختبارات الوظيفية:

    • اختبار "الصندوق الأبيض" لامتثال جزء من الطلب للمتطلبات مع معرفة التنفيذ الداخلي للنظام؛
    • اختبار "الصندوق الأسود" لامتثال جزء من التطبيق للمتطلبات دون معرفة التنفيذ الداخلي للنظام.
  5. اختبار الأداء هو نوع من الاختبارات التي تتم كتابتها لتحديد السرعة التي يعمل بها النظام أو جزء منه تحت حمل معين.
  6. اختبار الحمل - اختبارات مصممة للتحقق من استقرار النظام تحت الأحمال القياسية والعثور على أقصى ذروة ممكنة يعمل عندها التطبيق بشكل صحيح.
  7. اختبار التحمل هو نوع من الاختبارات المصممة للتحقق من وظائف التطبيق في ظل الأحمال غير القياسية ولتحديد أقصى ذروة ممكنة لن يتعطل النظام عندها.
  8. اختبار الأمان - اختبارات تستخدم للتحقق من أمان النظام (من هجمات المتسللين والفيروسات والوصول غير المصرح به إلى البيانات السرية وغيرها من متع الحياة).
  9. اختبار التعريب هو اختبار التعريب لأحد التطبيقات.
  10. اختبار قابلية الاستخدام هو نوع من الاختبارات التي تهدف إلى التحقق من سهولة الاستخدام وقابلية الفهم والجاذبية وقابلية التعلم للمستخدمين.
  11. كل هذا يبدو جيدًا، ولكن كيف يتم تطبيقه عمليًا؟ الأمر بسيط: يتم استخدام هرم اختبار مايك كوهن: كل شيء عن اختبار الوحدة: الأساليب والمفاهيم والممارسات - 4هذه نسخة مبسطة من الهرم: وهي الآن مقسمة إلى أجزاء أصغر. لكننا اليوم لن ننحرف ونفكر في الخيار الأبسط.
    1. الوحدة - اختبارات الوحدة المستخدمة في طبقات مختلفة من التطبيق، واختبار أصغر منطق قابل للقسمة للتطبيق: على سبيل المثال، فئة، ولكن في أغلب الأحيان طريقة. تحاول هذه الاختبارات عادةً عزل قدر الإمكان عن المنطق الخارجي، أي خلق الوهم بأن بقية التطبيق يعمل في الوضع القياسي.

      يجب أن يكون هناك دائمًا الكثير من هذه الاختبارات (أكثر من الأنواع الأخرى)، نظرًا لأنها تختبر قطعًا صغيرة وخفيفة الوزن جدًا، ولا تستهلك الكثير من الموارد (أعني بالموارد ذاكرة الوصول العشوائي والوقت).

    2. التكامل - اختبار التكامل. فهو يتحقق من أجزاء أكبر من النظام، أي أنه إما مزيج من عدة أجزاء من المنطق (عدة طرق أو فئات)، أو صحة العمل مع مكون خارجي. عادة ما يكون عدد هذه الاختبارات أقل من اختبارات الوحدة، لأنها أثقل.

      وكمثال على اختبارات التكامل، يمكنك التفكير في الاتصال بقاعدة بيانات والتحقق من التنفيذ الصحيح لطرق العمل معها .

    3. واجهة المستخدم - الاختبارات التي تتحقق من تشغيل واجهة المستخدم. إنها تؤثر على المنطق على جميع مستويات التطبيق، ولهذا السبب يطلق عليها أيضًا اسم "النهاية إلى النهاية". كقاعدة عامة، هناك عدد أقل بكثير منهم، لأنهم الأكثر وزنا ويجب عليهم التحقق من المسارات الأكثر ضرورة (المستخدمة).

      في الشكل أعلاه نرى نسبة مساحات الأجزاء المختلفة للمثلث: يتم الحفاظ على نفس النسبة تقريبًا في عدد هذه الاختبارات في العمل الحقيقي.

      سنلقي اليوم نظرة فاحصة على الاختبارات الأكثر استخدامًا - اختبارات الوحدة، نظرًا لأن جميع مطوري Java الذين يحترمون أنفسهم يجب أن يكونوا قادرين على استخدامها على المستوى الأساسي.

    المفاهيم الأساسية لاختبار الوحدة

    تعد تغطية الاختبار (تغطية الكود) أحد التقييمات الرئيسية لجودة اختبار التطبيق. هذه هي النسبة المئوية للكود الذي غطته الاختبارات (0-100%). من الناحية العملية، يطارد العديد من الأشخاص هذه النسبة، وأنا لا أتفق معها، حيث يبدأون في إضافة اختبارات حيث لا تكون هناك حاجة إليها. على سبيل المثال، تحتوي خدمتنا على عمليات CRUD قياسية (إنشاء/حصول/تحديث/حذف) بدون منطق إضافي. هذه الأساليب عبارة عن وسطاء بحتين يقومون بتفويض العمل إلى الطبقة التي تعمل مع المستودع. في هذه الحالة، ليس لدينا ما نختبره: ربما تستدعي هذه الطريقة طريقة من الطاو، لكن هذا ليس جديًا. لتقييم تغطية الاختبار، يتم عادةً استخدام أدوات إضافية: JaCoCo، وCobertura، وClover، وEmma، وما إلى ذلك. للحصول على دراسة أكثر تفصيلاً حول هذه المشكلة، احتفظ بمقالتين مناسبتين: TDD (التطوير القائم على الاختبار) - التطوير القائم على الاختبار. في هذا النهج، أولا وقبل كل شيء، يتم كتابة اختبار للتحقق من رمز معين. اتضح أنه اختبار للصندوق الأسود: نحن نعرف ما هو موجود عند الإدخال ونعرف ما يجب أن يحدث عند الإخراج. وهذا يتجنب تكرار التعليمات البرمجية. يبدأ التطوير القائم على الاختبار بتصميم وتطوير الاختبارات لكل وظيفة صغيرة في التطبيق. في نهج TDD، أولاً، يتم تطوير اختبار يحدد ويتحقق من ما سيفعله الكود. الهدف الرئيسي من TDD هو جعل التعليمات البرمجية أكثر وضوحًا وبساطة وخالية من الأخطاء. كل شيء عن اختبار الوحدة: الأساليب والمفاهيم والممارسات - 6ويتكون النهج من المكونات التالية:
    1. نحن نكتب اختبارنا.
    2. نجري الاختبار، سواء نجح أم لا (نرى أن كل شيء أحمر - لا تخاف: هكذا ينبغي أن يكون).
    3. نضيف الكود الذي يجب أن يفي بهذا الاختبار (نجري الاختبار).
    4. نحن نعيد صياغة الكود.
    استنادًا إلى حقيقة أن اختبارات الوحدة هي أصغر العناصر في هرم أتمتة الاختبار، فإن TDD يعتمد عليها. بمساعدة اختبارات الوحدة يمكننا اختبار منطق الأعمال لأي فئة. BDD (التنمية المدفوعة بالسلوك) - التطوير من خلال السلوك. يعتمد هذا النهج على TDD. وبشكل أكثر تحديدًا، فإنه يستخدم أمثلة مكتوبة بلغة واضحة (عادةً باللغة الإنجليزية) توضح سلوك النظام لجميع المشاركين في التطوير. لن نتعمق أكثر في هذا المصطلح، لأنه يؤثر بشكل أساسي على المختبرين ومحللي الأعمال. حالة الاختبار - برنامج نصي يصف الخطوات والشروط المحددة والمعلمات اللازمة للتحقق من تنفيذ التعليمات البرمجية قيد الاختبار. Fixture هي حالة من بيئة الاختبار الضرورية للتنفيذ الناجح للطريقة قيد الاختبار. هذه مجموعة محددة مسبقًا من الكائنات وسلوكها في ظل الظروف المستخدمة.

    مراحل الاختبار

    يتكون الاختبار من ثلاث مراحل:
    1. تحديد البيانات المراد اختبارها (التركيبات).
    2. استخدام الكود قيد الاختبار (استدعاء الطريقة قيد الاختبار).
    3. التحقق من النتائج ومقارنتها بالنتائج المتوقعة.
    كل شيء عن اختبار الوحدة: الأساليب والمفاهيم والممارسات - 7لضمان نمطية الاختبار، يجب أن تكون معزولاً عن الطبقات الأخرى للتطبيق. ويمكن القيام بذلك باستخدام بذرة، يسخر والجواسيس. المحاكاة هي كائنات قابلة للتخصيص (على سبيل المثال، خاصة بكل اختبار) وتسمح لك بتعيين التوقعات لاستدعاءات الطريقة في شكل استجابات نخطط لاستلامها. يتم إجراء عمليات التحقق من التوقع من خلال استدعاء كائنات وهمية. بذرة - توفر استجابة سلكية للمكالمات أثناء الاختبار. يمكنهم أيضًا تخزين معلومات حول المكالمة (على سبيل المثال، المعلمات أو عدد هذه المكالمات). يتم استدعاؤهم أحيانًا بمصطلحهم الخاص - جاسوس ( جاسوس ). في بعض الأحيان يتم الخلط بين هذه المصطلحات كعب الروتين و وهمية : الفرق هو أن كعب الروتين لا يتحقق من أي شيء، ولكنه يحاكي فقط حالة معينة. الوهم هو كائن له توقعات. على سبيل المثال، يجب استدعاء طريقة فئة معينة لعدد معين من المرات. بمعنى آخر، لن ينكسر اختبارك أبدًا بسبب كعب الروتين، ولكنه قد ينقطع بسبب تقليد.

    بيئات الاختبار

    والآن دعونا ننتقل إلى العمل. هناك العديد من بيئات الاختبار (الأطر) المتاحة لـ Java. الأكثر شعبية منهم هي JUnit وTestNG. لمراجعتنا، نستخدم: كل شيء عن اختبار الوحدة: الأساليب والمفاهيم والممارسات - 8اختبار 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 الأكثر قوة. دعونا نلقي نظرة على بعض الطرق لمقارنة النتائج:
    • 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)- يتحقق مما إذا كان يفي بالشرط المحدد في المطابق.
    يوجد أيضًا نموذج مقارنة مفيد من التأكيد - 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 - نظرًا لأننا لم نقم بتعيين هذا في حقل مثيل التحديث، فيجب أن يظل كما هو، فلنتحقق منه. كل شيء عن اختبار الوحدة: الأساليب والمفاهيم والممارسات - 9لنبدأ: كل شيء عن اختبار الوحدة: الأساليب والمفاهيم والممارسات - 10الاختبار أخضر، يمكنك الزفير)) لذا، دعونا نلخص: الاختبار يعمل على تحسين جودة الكود ويجعل عملية التطوير أكثر مرونة وموثوقية. تخيل مقدار الجهد الذي يتعين علينا بذله عند إعادة تصميم البرنامج باستخدام مئات الملفات الصفية. بمجرد كتابة اختبارات الوحدة لجميع هذه الفئات، يمكننا إعادة البناء بثقة. والأهم من ذلك أنه يساعدنا في العثور بسهولة على الأخطاء أثناء التطوير. يا رفاق، هذا كل شيء بالنسبة لي اليوم: أضف الإعجابات، واكتب التعليقات))) كل شيء عن اختبار الوحدة: الأساليب والمفاهيم والممارسات - 11
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION