أتمتة بناء كتلة أساسية - اختبار
رود جونسون

لست سفيرًا لاختبار واجهات الويب ، ولكن من المرجح أن يكون هذا المقال مفيدًا للرفاق الذين لديهم خبرة في هذا المجال.
سيكون أيضًا مفيدًا للمبتدئين ، كما أقوم بتوفير الكود المصدري حيث يمكنك رؤية كيفية تنظيم التفاعل مع السيلينيوم في المنتج النهائي.
سأتحدث عن كيف ، من الصفر ، ولدي خبرة قليلة في التطوير ، كتبت منصة لإجراء الاختبارات ، وعن النظام الأساسي نفسه. أنا شخصياً أعتقد أن المنتج الخاص بي كان فعالاً للغاية ، مما يعني أنه سيكون مفيدًا للكثيرين وله مكان للنظر فيه.
من المفهوم
تعتمد عملية الاختبار على نظام المعلومات.
لتتذكر مفهومي ، من الضروري فهم الأنظمة التي أركز عليها في المقام الأول - تلك الأنظمة التي عادة ما توجد فيها عمليات أعمال خطية محددة يتم تعيينها كمفتاح عند إجراء اختبارات الانحدار.
لذلك ، نظام مثل SRM. الكيان التجاري الرئيسي هو عروض البائعين. الاعتبار الرئيسي في إجراء اختبار الانحدار هو سلامة العملية التجارية.
تبدأ عملية الأعمال من تسجيل المورد في النظام ، ثم يتم إنشاء عرض الاقتراح التجاري - حيث يتم الانتقال إلى مرحلة النظر ، والذي يتم تنفيذه بواسطة أنواع مختلفة من المستخدمين الداخليين (ولكل مستخدم واجهة فريدة) حتى يتم إرجاع القرار الخاص بدراسة الاقتراح إلى المورد.
اتضح أننا نمر بعدد من الواجهات المختلفة ، ونعمل دائمًا مع واجهات مختلفة. في الواقع - إذا نظرت إليه مباشرةً - يبدو الأمر وكأنه يشاهد شريط فيديو - على سبيل المثال هذه عملية لها بداية ونهاية ، وهي خطية تمامًا - لا تتفرع ، عند كتابة اختبار ، نحن نعرف دائمًا النتيجة المتوقعة. أي أريد أن أقول أنه بالنظر إلى هذه الصورة بالفعل ، يمكننا أن نستنتج أن إجراء اختبارات متعددة الأشكال من غير المرجح أن ينجح. في ضوء ذلك ، عند إنشاء نظام أساسي لإجراء الاختبارات ، فإن العامل الرئيسي الذي أعددته هو سرعة كتابة الاختبارات.
المفاهيم التي حددتها لنفسي:- يجب إنشاء Autotest في أسرع وقت ممكن. إذا حققت هذا النوعي ، فيجب أن تأتي جوانب أخرى ، مثل الموثوقية وسهولة الاستخدام ، من تلقاء نفسها.
- يجب الإعلان عن الاختبارات بشكل مباشر والعيش بشكل منفصل عن الرمز. لم أر حتى خيارًا آخر. هذا يزيد من سرعة الكتابة ، كما إذا كان لديك مترجم فوري جاهز - نظامنا الأساسي ، فلن تحتاج إلى إضافة أي شيء لاحقًا ، ليس عليك أن تدخل الرمز مرة أخرى - بشكل عام ، يمكنك نسيان النظام الأساسي النهائي باستخدام IDE. لذلك الاختبارات أسهل في الصيانة. في هذا النموذج ، من الأسهل تعلم الكتابة ، لأنه ليست هناك حاجة إلى مهارات التنمية ، ولكن فقط فهم لغة الترميز. في هذا النموذج ، تكون مفهومة لجميع المشاركين في العملية.
ما قررت رفضه في البداية:
- لا التفاف النظام الخاص بك في إطار اختبار. يمكنك البدء في تنفيذ عملية دون إطار اختبار. "أنت تريد اختراع دراجة!" - سيقول الكثيرون. أعتقد بشكل مختلف. تم إنشاء أطر الاختبار المستخدمة الشائعة في المقام الأول لاختبار الشفرة من الداخل ، وسنقوم باختبار الجزء الخارجي من النظام من الخارج. يبدو الأمر كما لو كنت أمتلك دراجة هوائية على الطريق ، وأحتاج إلى النزول عبر الطرق الوعرة (غير مهذب ، لكن قطار الفكر ينعكس). بشكل عام ، سنكتب الإطار بأنفسنا - باستخدام لعبة البلاك جاك و ... (على الرغم من أنني أدرك ، على سبيل المثال ، JUnit 5 بالفعل أكثر تكيفًا لمثل هذه المهام).
- رفض استخدام الأغلفة للسيلينيوم. في الواقع ، المكتبة الرئيسية نفسها صغيرة. لفهم أنك تحتاج إلى استخدام 5 في المائة من وظائفها ، في حين أن التجريف الكامل لها ، سوف يستغرق عدة ساعات. التوقف عن البحث في كل مكان عن وسيلة لكتابة رمز أقل وتعود على قعادة. في العالم الحديث ، غالبًا ما تؤدي هذه الرغبة إلى العبث وتسبب دائمًا ضررًا في المرونة (أعني مناهج "كتابة كود أقل" وليس حالات الأطر المعمارية).
- عرض جميل للنتائج ليست ضرورية. قدم هذا البند ، ل لم أواجه مرة واحدة هذا. عند اكتمال الاختبار التلقائي ، أحتاج إلى معرفة شيئين: النتيجة الإجمالية (الإيجابية / السلبية) ، وإذا كان هناك خطأ - حيث بالضبط. ربما كنت لا تزال بحاجة للحفاظ على الإحصاءات. كل شيء آخر من حيث النتائج ليس من الضروري على الإطلاق. للنظر في التصميم الجميل كإضافة كبيرة ، أو قضاء بعض الوقت في هذا التصميم الجميل في المراحل الأولية - تعتبر عروضاً زائدة عن الحاجة.
سأتحدث أكثر قليلاً عن مستوى التطور في الشركة وشروط إنشاء الأداة لتوضيح بعض التفاصيل بشكل كامل.
بسبب بعض الظروف السرية ، لا أفصح عن الشركة التي أعمل بها.
في شركتنا ، كان التطوير موجودًا بالفعل لسنوات عديدة ، وبالتالي تم إنشاء جميع العمليات منذ فترة طويلة. ومع ذلك ، فهي بعيدة عن الاتجاهات الحالية.
يدرك جميع ممثلي تكنولوجيا المعلومات أنه من الضروري تغطية الشفرة مع الاختبارات ، وكتابة البرامج النصية للاختبارات التلقائية في وقت تنسيق المتطلبات للوظائف المستقبلية ، والتقنيات المرنة توفر الوقت والموارد بشكل كبير ، و CI التي تأخذ الحياة وتبسطها ببساطة. ولكن كل هذا حتى الآن يصل بنا ببطء فقط ...
وكذلك خدمة مراقبة جودة البرنامج - يتم إجراء جميع الاختبارات يدويًا ، إذا نظرت إلى العملية "من أعلى" - فهذه هي "عنق الزجاجة" لعملية التطوير بأكملها.
وصف التجمع
تم كتابة المنصة بلغة جافا باستخدام JDK 12
أدوات البنية التحتية الرئيسية - برنامج Web Selenium ، OJDBC
لكي يعمل التطبيق ، يجب تثبيت إصدار متصفح FireFox 52 على جهاز الكمبيوتر
بناء التطبيق التكوين

مع التطبيق ، 3 مجلدات وملفان مطلوبان:
• مجلد
BuildKit - يحتوي على:
- jdk12 ، من خلالها يتم تشغيل التطبيق (JVM) ؛
- geckodriver.exe - لبرنامج Selenium Web Driver للعمل مع متصفح FireFox ؛
- SprintAutoTest.jar - مباشرة مثيل التطبيق
• مجلد
التقارير - يتم حفظ التقارير إليه بعد اكتمال التطبيق لحالة الاختبار. يجب أن يحتوي أيضًا على مجلد ErrorScreens ، حيث يتم حفظ لقطة الشاشة في حالة حدوث خطأ
• مجلد
TestSuite - حزم الويب ،
ومخطوطات جافا ، ومجموعة من حالات الاختبار (ستتم مناقشة ملء هذا المجلد بالتفصيل بشكل منفصل)
• config.properties file - يحتوي على تهيئة للاتصال بقاعدة بيانات Oracle وقيم التوقعات الواضحة لـ WebDriverWait
• starter.bat - ملف لبدء تشغيل التطبيق (من الممكن تشغيل التطبيق تلقائيًا دون تحديد TestCase يدويًا إذا قمت بإدخال اسم TestCase كمعلمة في النهاية).
وصف موجز للتطبيق
يمكن تشغيل التطبيق باستخدام المعلمة (اسم TestCase) أو بدونه - في هذه الحالة ، يجب إدخال اسم حالة الاختبار في وحدة التحكم بنفسك.
مثال على المحتويات العامة لملف الخفافيش ليتم تشغيله بدون معلمة : بدء تشغيل "AutoTest launcher"٪ cd٪ \ BuildKit \ jdk-12 \ bin \ java.exe -Xmx768M -jar --enable-preview٪ cd٪ \ BuildKit \ SprintAutoTest.jarعند التشغيل العام للتطبيق ، فإنه يبحث في ملفات xml الموجودة في الدليل "\ TestSuite \ TestCase" (بدون عرض محتويات المجلدات الفرعية). في هذه الحالة ، يحدث التحقق الأساسي من ملفات xml إلى صحة البنية (أي أن جميع العلامات من وجهة نظر علامة xml صحيحة) ، ويتم أخذ الأسماء المشار إليها في علامة "testCaseName" ، وبعد ذلك تتم مطالبة المستخدم بإدخال أحد الأسماء المحتملة للاختبار المتاح الحالات. في حالة وجود إدخال خاطئ ، سيطلب منك النظام إدخال الاسم مرة أخرى.
بعد تلقي اسم TestCase ، تم بناء النموذج الداخلي ، وهو عبارة عن مجموعة من TestCase (برنامج نصي للاختبار) - WebPackage (تخزين العناصر) في شكل كائنات java. بعد بناء النموذج ، يتم بناء TestCase (الكائن القابل للتنفيذ من البرنامج) مباشرة. في مرحلة إنشاء TestCase ، يتم التحقق من الصحة الثانوية أيضًا - يتم التحقق من أن جميع النماذج المحددة في TestCase موجودة في WebPackage المقترن وأن جميع العناصر المحددة في الإجراء موجودة في WebPackage داخل الصفحات المحددة. (يتم وصف بنية TestCase و WebPackage بالتفصيل أدناه)
بعد إنشاء TestCase ، يتم تشغيل البرنامج النصي مباشرةً
خوارزمية تشغيل البرنامج النصي (منطق رئيسي)
TestCase عبارة عن مجموعة من كيانات الإجراء ، والتي بدورها عبارة عن مجموعة من كيانات الأحداث.
TestCase
-> قائمة {Action}
-> قائمة {الحدث}عند بدء تشغيل TestCase ، يبدأ الإجراء بالتتابع (يُرجع كل إجراء نتيجة منطقية)
عند بدء الإجراء ، يبدأ الحدث بالتتابع (كل حدث بإرجاع نتيجة منطقية)
يتم حفظ نتيجة كل حدث.
وفقًا لذلك ، يتم الانتهاء من الاختبار إما عند اكتمال جميع الإجراءات بنجاح ، أو في حالة إرجاع الإجراء false.
* تحطم آلية
لأن نظامي قيد الاختبار قديم وقد اكتشف الأخطاء / الأخطاء التي ليست أخطاء ، وبعض الأحداث لا تعمل في المرة الأولى ، للنظام الأساسي آلية يمكنها الخروج عن المفهوم أعلاه لاختبار خطي صارم (ومع ذلك ، تتم كتابته بشدة). عند التقاط مثل هذه الأخطاء ، من الممكن تكرار الحالات أولاً وتنفيذ إجراءات إضافية لتكون قادرًا على تكرار الإجراءاتفي نهاية التطبيق ، يتم إنشاء تقرير ، يتم حفظه في دليل "\ Reports". في حالة وجود خطأ ، يتم التقاط لقطة شاشة ، يتم حفظها في "\ Reports \ ErrorScreens"
TestSuite ملء
لذلك ، وصف الاختبار. كما ذكرنا سابقًا ، فإن المعلمة الرئيسية اللازمة للتشغيل هي اسم الاختبار الذي سيتم تشغيله. يتم تخزين هذا الاسم في ملف xml في الدليل "/ TestSuite / TestCase". يتم تخزين جميع البرامج النصية اختبار في هذا الدليل. يمكن أن يكون هناك أي عدد منهم. لا يتم أخذ اسم حالة الاختبار من اسم الملف ، ولكن من علامة "testCaseName" داخل الملف.
يحدد TestCase ما سيتم تنفيذه بالضبط - على سبيل المثال الإجراءات. في الدليل "/ TestSuite / WebPackage" في ملفات xml ، يتم تخزين جميع المواقع. أي الكل في أفضل التقاليد - يتم تخزين الإجراءات بشكل منفصل ، وتحديد المواقع على شبكة الإنترنت بشكل منفصل.
يخزن TestCase أيضًا اسم WebPackage في علامة "webPackageName".
مجموع الصورة بالفعل هناك. للتشغيل ، يجب أن يكون لديك ملفين xml: TestCase و WebPackage. أنها تشكل حفنة. WebPackage مستقل - المعرف هو الاسم الموجود في العلامة "webPackageName". وفقًا لذلك ، إليك القاعدة الأولى - يجب أن تكون الأسماء TestCase و WebPackage فريدة. أي مرة أخرى - في الواقع ، فإن الاختبار الخاص بنا هو مجموعة من ملفات TestCase و WepPackage ، والتي تتصل باسم WebPackage ، المحدد في TestCase. في الممارسة العملية ، أقوم بأتمتة نظام واحد وأربط جميع حالات الاختبار الخاصة بي مع WebPackage واحد لدي مجموعة كبيرة من أوصاف جميع النماذج.
تعتمد الطبقة التالية من التحلل المنطقي على نموذج مثل كائن الصفحة.
كائن الصفحةكائن الصفحة هو أحد الحلول المعمارية الأكثر استخدامًا والأكثر استخدامًا في التشغيل الآلي. يساعد نمط التصميم هذا في تغليف العمل باستخدام عناصر صفحة فردية. كائن الصفحة ، كما كان ، يحاكي صفحات التطبيق قيد الاختبار ككائنات.
فصل المنطق والتنفيذ
هناك فرق كبير بين منطق الاختبار (ما يجب التحقق منه) وتنفيذه (كيفية التحقق). مثال على سيناريو الاختبار: "يقوم المستخدم بإدخال اسم مستخدم أو كلمة مرور غير صحيحة ، ويضغط على زر تسجيل الدخول ، ويتلقى رسالة خطأ." يصف هذا البرنامج النصي منطق الاختبار ، بينما يتضمن التنفيذ إجراءات مثل البحث عن حقول الإدخال في الصفحة ، وملءها ، والتحقق من الأخطاء ، إلخ. وإذا تغيرت طريقة عرض رسالة خطأ ، على سبيل المثال ، فلن يؤثر ذلك على البرنامج النصي للاختبار ، وستحتاج أيضًا إلى إدخال بيانات غير صحيحة والضغط على زر تسجيل الدخول والتحقق من الخطأ. ولكن هذا سيؤثر بشكل مباشر على تنفيذ الاختبار - سيكون من الضروري تغيير الطريقة التي تستقبل وتتعامل مع رسالة الخطأ. من خلال فصل منطق الاختبار عن تنفيذه ، تصبح الاختبارات التلقائية أكثر مرونة ، وكقاعدة عامة ، أسهل في الحفاظ عليها.
*! لا يمكن القول أن هذا النهج المعماري قد تم تطبيقه بالكامل. إنها فقط مسألة تحليل وصف صفحة البرنامج النصي للاختبار حسب الصفحة ، مما يساعد على كتابة الاختبارات بشكل أسرع وإضافة عمليات فحص تلقائي إضافية على جميع الصفحات ، ويحفز الوصف الصحيح لتحديد المواقع (بحيث لا يكون هو نفسه في صفحات مختلفة) ويبني بنية منطقية "جميلة" للاختبار. يتم تطبيق المنصة نفسها على مبادئ "العمارة النظيفة"
بعد ذلك ، سأحاول ألا أفصل بنية WebPackage و TestCase. بالنسبة لهم ، قمت بإنشاء مخطط DTD لـ WebPackage و XSD 1.1 لـ TestCase.
! هام
من خلال الحفاظ على مخططات DTD و XSD ، يتم تطبيق مفهوم الكتابة السريعة للاختبار.
عند كتابة WebPackage و TestCase مباشرة ، يجب عليك استخدام محرر xml مع وظائف التحقق من صحة DTD و XSD المضمنة في الوقت الحقيقي مع الإنشاء التلقائي للعلامات ، مما يجعل عملية كتابة اختبار تلقائي إلى حد كبير تلقائيًا (سيتم استبدال جميع العلامات المطلوبة تلقائيًا ، وسيتم عرض القوائم المنسدلة لقيم السمات القيم المحتملة ، وفقا لنوع الحدث وسيتم إنشاء علامات المقابلة) .
عندما يتم "فك" هذه المخططات في ملف xml نفسه ، يمكنك حينئذٍ أن تنسى صحة بنية ملف xml ، إذا كنت تستخدم بيئة خاصة. انخفض خياري على محرر xXygen. مرة أخرى - دون استخدام مثل هذا البرنامج ، فلن تفهم جوهر سرعة الكتابة. الفكرة ليست مناسبة جدا لهذا الغرض. لا يتعامل مع بنية XSD 1.1 "البديلة" ، وهو مفتاح TestCase.
WebPackage
WebPackaege - ملف xml يصف عناصر نماذج الويب ، الموجودة في الدليل "\ TestSuite \ WebPackage". (يمكن أن يكون هناك العديد من الملفات التي تريدها. يمكن أن يكون اسم الملفات أي شيء - يهم المحتوى فقط).
DTD (المدرج في بداية المستند):<!DOCTYPE webPackage [ <!ELEMENT webPackage (webPackageName, forms)> <!ELEMENT webPackageName (#PCDATA)> <!ELEMENT forms (form+)> <!ELEMENT form (formName, elements+)> <!ELEMENT formName (#PCDATA)> <!ELEMENT elements (element+)> <!ELEMENT element (name, locator)> <!ATTLIST element type (0|1|2|3|4|5|6|7) #REQUIRED> <!ATTLIST element alwaysVisible (0|1) #IMPLIED> <!ELEMENT name (#PCDATA)> <!ELEMENT locator (#PCDATA)> <!ATTLIST locator type (1|2) #IMPLIED> ]>
بشكل عام ، يبدو تقريبا <webPackage> <webPackageName>_</webPackageName> <forms> <form> <formName>______</formName> <elements> <element type="2" alwaysVisible="1"> <name>_</name> <locator type="2">.//div/form/div/div/form/table/tbody/tr/td[text()=""]/following-sibling::td/input</locator> </element> <element type="2"> <name>__</name> <locator>.//div/form/div/div/form/table/tbody/tr/td[text()=""]/following-sibling::td/input</locator> </element> ....... </elements> </form> ....... </forms> </webPackage>
كما ذكرنا من قبل ، بحيث لا تكون العناصر في كومة - كل شيء يتم تحليله وفقًا لنماذج الويب
الكيان الرئيسي هو
<element>
تحتوي علامة العنصر على سمتين:
سمة
الكتابة مطلوبة وتحدد نوع العنصر. في المنصة ، اضبط نوع البايت
في الوقت الحالي ، وبالتحديد لنفسه في المنصة ، قام بتنفيذ الأنواع التالية:
• 0 - ليس له معنى وظيفي ، وعادة ما يكون هناك نوع من الكتابة
• 1 - زر (زر)
• 2 - حقل الإدخال
• 3 - مربع الاختيار (checkbox)
• 4 - القائمة المنسدلة (حدد) - لم يتم تنفيذها فعليًا ، ولكنها تركت مكانًا لها
• 5 - للحصول على القائمة المنسدلة srm: اكتب الاسم ، انتظر حتى تظهر القيمة - اختر وفقًا لقالب xpath المحدد - النوع المحدد لنظامي
• 6 - srm select - يستخدم في الوظائف النموذجية مثل البحث ، إلخ. - اكتب خصيصا لنظامي
تُظهر السمة
alwaysVisible - اختيارية - ما إذا كان عنصر ما موجودًا دائمًا في النموذج ، يمكن استخدامه أثناء التحقق الأولي / النهائي من الإجراء (على سبيل المثال ، في الوضع التلقائي ، يمكنك التحقق من أنه عند فتح النموذج ، فإنه يحتوي على جميع العناصر الموجودة عليه دائمًا ، عند الإغلاق أشكال ، اختفت كل هذه العناصر)
القيم الممكنة:
- 0 - بشكل افتراضي (أو إذا لم يتم تعيين السمة) - قد لا يكون العنصر في الصفحة (لا يتم التحقق من الصحة)
- 1 - العنصر موجود دائمًا على الصفحة
يتم تطبيق سمة
نوع اختياري اختيارية مع علامة تحديد الموقع
القيم الممكنة:
- 1 - ابحث عن عنصر بمعرف (على التوالي ، حدد المعرف الوحيد في محدد الموقع)
- 2 - بشكل افتراضي (أو إذا لم يتم تعيين السمة) - البحث على xpath - يوصى باستخدام البحث فقط على xpath ، لأن تجمع هذه الطريقة بين جميع مزايا البقية وهي عالمية
TestCase
TestCase - يوجد ملف xml الذي يصف النص البرمجي للاختبار مباشرةً في الدليل "\ TestSuite \ TestCase" (يمكن أن يكون هناك عدد الملفات التي تريدها. يمكن أن يكون اسم الملفات أي شيء - يهم المحتوى فقط).
دارة XSD: <xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.1"> <xs:element name="testCase"> <xs:complexType> <xs:sequence> <xs:element name="testCaseName" type="xs:string"/> <xs:element name="webPackageName" type="xs:string"/> <xs:element name="actions" type="actionsType"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="actionsType"> <xs:sequence> <xs:element name="action" type="actionType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <xs:complexType name="actionType"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="orderNumber" type="xs:positiveInteger"/> <xs:element name="runConfiguration" type="runConfigurationType"/> </xs:sequence> </xs:complexType> <xs:complexType name="runConfigurationType"> <xs:sequence> <xs:element name="formName" type="xs:string"/> <xs:element name="repeatsOnError" type="xs:positiveInteger" minOccurs="0"/> <xs:element name="events" type="eventsType"/> <xs:element name="exceptionBlock" type="eventsType" minOccurs="0"/> </xs:sequence> <xs:attribute name="openValidation" use="required"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="closeValidation" use="required"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> <xs:complexType name="eventBaseType"> <xs:sequence> <xs:element name="orderNumber" type="xs:positiveInteger"/> </xs:sequence> <xs:attribute name="type" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="goToURL"/> <xs:enumeration value="checkElementsVisibility"/> <xs:enumeration value="checkElementsInVisibility"/> <xs:enumeration value="fillingFields"/> <xs:enumeration value="clickElement"/> <xs:enumeration value="dbUpdate"/> <xs:enumeration value="wait"/> <xs:enumeration value="scrollDown"/> <xs:enumeration value="userInput"/> <xs:enumeration value="checkInputValues"/> <xs:enumeration value="checkQueryResultWithUtilityValue"/> <xs:enumeration value="checkFieldsPresenceByQueryResult"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="invertResult" use="optional" default="0"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="hasExceptionBlock" use="optional" default="0"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="0"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> <xs:complexType name="eventsType"> <xs:sequence> <xs:element name="event" type="eventBaseType" maxOccurs="unbounded"> <xs:alternative test="@type='goToURL'" type="eventGoToURL"/> <xs:alternative test="@type='checkElementsVisibility'" type="eventCheckElementsVisibility"/> <xs:alternative test="@type='checkElementsInVisibility'" type="eventCheckElementsVisibility"/> <xs:alternative test="@type='fillingFields'" type="eventFillingFields"/> <xs:alternative test="@type='checkInputValues'" type="eventFillingFields"/> <xs:alternative test="@type='clickElement'" type="eventClickElement"/> <xs:alternative test="@TYPE='dbUpdate'" type="eventRequest"/> <xs:alternative test="@type='wait'" type="utilityValueInteger"/> <xs:alternative test="@type='scrollDown'" type="eventClickElement"/> <xs:alternative test="@type='userInput'" type="eventClickElement"/> <xs:alternative test="@type='checkQueryResultWithUtilityValue'" type="eventRequestWithValue"/> <xs:alternative test="@type='checkFieldsPresenceByQueryResult'" type="eventRequestWithValue"/> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="eventGoToURL"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="url" type="xs:anyURI"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventCheckElementsVisibility"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="fields" type="fieldType"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventFillingFields"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="fields" type="fieldTypeWithValue"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventClickElement"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="elementName" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventRequest"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="dbRequest" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="utilityValueInteger"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="utilityValue" type="xs:positiveInteger"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="eventRequestWithValue"> <xs:complexContent> <xs:extension base="eventBaseType"> <xs:sequence> <xs:element name="dbRequest" type="xs:string"/> <xs:element name="utilityValue" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType> <xs:complexType name="fieldType"> <xs:sequence> <xs:element name="field" maxOccurs="unbounded"> <xs:complexType> <xs:choice> <xs:element name="element" type="xs:string"/> <xs:element name="xpath" type="xs:string"/> </xs:choice> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="fieldTypeWithValue"> <xs:sequence> <xs:element name="field" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="element" type="xs:string"/> <xs:element name="value" type="valueType"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> <xs:complexType name="valueType"> <xs:complexContent> <xs:extension base="xs:anyType"> <xs:attribute name="type" use="optional" default="1"> <xs:simpleType> <xs:restriction base="xs:byte"> <xs:enumeration value="1"/> <xs:enumeration value="2"/> <xs:enumeration value="3"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:extension> </xs:complexContent> </xs:complexType> </xs:schema>
منظر عام: <!DOCTYPE testCase SYSTEM "./TestSuite/TestCase/entities.dtd" []> <testCase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="testShema.xsd"> <testCaseName>__testCase</testCaseName> <webPackageName>_webPackage__webPackageName</webPackageName> <actions> <action> <name> </name> <orderNumber>10</orderNumber> <runConfiguration openValidation="1" closeValidation="1"> <formName>______</formName> <events> <event type="goToURL"> <orderNumber>10</orderNumber> <url>&srmURL;</url> </event> ....... </events> </runConfiguration> </action> ....... </actions> </testCase>
هنا في هذا السطر ، يمكنك معرفة كيفية ربط نظام xsd بحيث يرى محرر XML ذلك:
<testCase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="testShema.xsd">
في TestCase ، أستخدم أيضًا كيانات DTD التي يتم تخزينها بشكل منفصل في نفس الدليل - ملف ذو ملحق .dtd. في ذلك ، أنا تخزين ما يقرب من جميع البيانات - الثوابت. لقد بنيت أيضًا المنطق بطريقة لإطلاق اختبار جديد ، وخلال الاختبار ، تم إنشاء كيانات فريدة جديدة ، وتم تسجيل مركبة فضائية جديدة ، وكان يكفي تغيير رقم واحد في هذا الملف.
هيكلها بسيط للغاية - سأقدم مثالاً: <?xml version="1.0" encoding="UTF-8"?> <!ENTITY mainNumber '040'> <!ENTITY mail '@mail.ru'> <!ENTITY srmURL 'https://srm-test.ru'>
يتم إدراج هذه الثوابت في قيمة العلامة كما يلي:
<url>&srmURL;</url>
- يمكن الجمع.
! التوصية - عند كتابة testCase ، يجب عليك تحديد هذه الكيانات DTD داخل المستند ، وبعد كل شيء يعمل بشكل مستقر ، نقله إلى ملف منفصل. يواجه محرر xml الخاص بي صعوبات في ذلك - لا يمكنه العثور على DTD ولا يأخذ XSD في الاعتبار ، لذلك أوصي بذلك
testCasetestCase - تحتوي العلامة الأصل على:
- testCaseName - اسم حالة الاختبار الخاصة بنا ، يتم تمرير هذه المعلمة إلى إدخال التطبيق
- webPackageName - اسم WebPackage ، المحدد في webPackageName (انظر الفقرة أعلاه على WebPackage)
- الإجراءات - حاوية كيان الإجراء
عمليحتوي على:
- الاسم - الاسم - يوصى بتحديد اسم النموذج والإجراءات الرئيسية - ماذا ولماذا
- orderNumber - الرقم التسلسلي - المعلمة ضرورية لإجراء الفرز (المقدمة بسبب حقيقة أنه عند تحليل xml في java باستخدام أدوات معينة ، يمكن تنفيذ التحليل في بيئة متعددة الخيوط ، وبالتالي يمكن الترتيب) - عند تحديد الإجراء التالي ، يمكنك القفز - على سبيل المثال عند الفرز ، يهم فقط "أكثر / أقل" ، وبالتالي فمن الممكن تنفيذ إجراء بين تلك الموصوفة بالفعل دون الحاجة إلى تغيير الترقيم بأكمله
- runConfiguration - الوصف الفعلي لما سيحدث كجزء من الإجراء
runConfigurationيحتوي على:
- سمة openValidation - اختياري ، الافتراضي هو "0"
- 0 - لا تقم بإجراء التحقق من صحة النموذج الأولي
- 1 - التحقق من صحة النموذج الأولي
- السمة closeValidation - اختياري ، الافتراضي هو "0"
- 0 - لا تقم بإجراء التحقق من صحة النموذج النهائي
- 1 - التحقق من صحة الشكل النهائي
- formName - اسم النموذج الذي سيتم تنفيذ الإجراءات من خلاله - قيمة formName من WebPackage
- يشير repeatsOnError - اختياري ، إلى عدد مرات التكرار التي ينبغي تنفيذها في حالة الفشل
- الأحداث - حاوية كيان الحدث
- ثناء استثناء - اختياري - حاوية كيانات الأحداث التي يتم تنفيذها في حالة حدوث خطأ
حدثالوحدة الهيكلية الدنيا - يعرض هذا الكيان الإجراءات التي يتم تنفيذها
كل حدث خاص ، ويمكن أن يكون له علامات وسمات فريدة.
النوع الأساسي يحتوي على:
- سمة الكتابة - تشير إلى نوع العنصر
- سمة hasExceptionBlock هي سمة اختيارية ، بشكل افتراضي "0" ضرورية لتنفيذ آلية الفشل - تشير السمة إلى أنه يمكننا توقع حدوث خطأ في هذا الحدث
- 0 - لا خطأ متوقع
- 1 - من المتوقع حدوث خطأ محتمل في الإجراء
- سمة invertResult - سمة اختيارية ، الإعدادات الافتراضية إلى "0" - تشير السمة إلى أنه من الضروري تغيير نتيجة الحدث
- 0 - اترك نتيجة الحدث
- 1 - تغيير نتيجة الحدث إلى عكس ذلك
*! آلية لوصف الخطأ المتوقعاسمحوا لي أن أقدم لكم مثالا تافها على المكان الذي استخدمته لأول مرة وما الذي يجب فعله لجعله يعمل.
الحالة: إدخال كلمة التحقق. في الوقت الحالي ، لم أستطع أتمتة ، إذا جاز التعبير ، التحقق من تعطل الروبوت - فهم لا يكتبون لي خدمة اختبار captcha (لكن من الأسهل بالنسبة لي أن أقوم بإنشاء شبكة عصبية للتعرف عليها))) لذا ، قد نخطئ عند الدخول. في هذه الحالة ، أقوم بإجراء عنصر تحكم أتحقق فيه من عدم وجود عنصر - إشعار حول رمز تحكم غير صحيح ، لقد وضعت سمة hasExceptionBlock عليه. في السابق ، طلبت إجراءً يمكن أن يكون لدينا العديد من التكرارات (5) وبعد كل شيء ، قمت بتسجيل استثناء ، حيث كتبت أنه يجب علي الضغط على زر الخروج للإشعار ، ثم تم تكرار الإجراء.
أمثلة من السياق الخاص بي.
إليك كيفية تسجيل الحدث:
<event type="checkElementsInVisibility" hasExceptionBlock="1"> <orderNumber>57</orderNumber> <fields> <field> <element>___</element> </field> </fields> </event>
وهنا استثناءالبلوك بعد الأحداث
<exceptionBlock> <event type="clickElement"> <orderNumber>10</orderNumber> <elementName>_____</elementName> </event> </exceptionBlock>
ونعم ، يمكن تقسيم الإجراءات في صفحة واحدة إلى عدة إجراءات.
+ الذين لاحظوا 2 المعلمات في التكوين: defaultTimeOutsForWebDriverWait ، lowTimeOutsForWebDriverWait. لهذا السبب هم كذلك. لأن لدي برنامج تشغيل الويب بالكامل في برنامج أحادي ، ولم أكن أرغب في إنشاء WebDriverWait جديد في كل مرة ، ثم لدي واحد سريع وهو في حالة حدوث خطأ (جيدًا ، أو إذا وضعت للتو hasExceptionBlock = "1" ، فسيكون ذلك غبيًا مع وقت أقل انتظار صريح) - حسنًا ، يجب عليك أن توافق ، انتظر دقيقة واحدة للتأكد من أن الرسالة لم تظهر بالوقت ، وكذلك إنشاء WebDriverWait جديد في كل مرة. حسنًا ، هذا الموقف على الجانب الذي لا يلتصق يتطلب عكازًا - قررت أن أفعل ذلك.
أنواع الأحداث
سأقدم هنا الحد الأدنى من الأحداث الخاصة بي ، مثل مجموعة الكشفية ، والتي يمكنني من خلالها اختبار كل شيء تقريبًا على نظامي.
والآن أصبح من السهل على الكود أن يفهم ماهية الحدث وكيف يتم بناؤه. ينفذ الكود في الأساس الإطار. لدي فصول 2 - DataBaseWrapper و SeleniumWrapper. في هذه الفئات ، يتم وصف التفاعل مع مكونات البنية الأساسية ، كما تنعكس ميزات النظام الأساسي. سأقدم الواجهة التي تنفذ SeleniumWrapper
package logic.selenium; import models.ElementWithStringValue; import models.webpackage.Element; import org.openqa.selenium.WebElement; public interface SeleniumService { void initialization(boolean webDriverWait); void nacigateTo(String url); void refreshPage(); boolean checkElementNotPresent(Element element); WebElement findSingleVisibleElement(Element element); WebElement findSingleElementInDOM(Element element); void enterSingleValuesToWebField(ElementWithStringValue element); void click(Element element); String getInputValue(Element element); Object jsReturnsValue(String jsFunction);
يصف جميع ميزات السيلينيوم وشرائح منصة التراكبات - حسنًا ، في الواقع فإن الشريحة الرئيسية هي طريقة "enterSingleValuesToWebField". تذكر أننا في WebPackage نحدد نوع العنصر. لذا ، كيف تتفاعل مع هذا النوع عند ملء الحقول المكتوبة هنا. نكتب 1 مرة وننسى. يجب تصحيح الطريقة المذكورة أعلاه لنفسك في المقام الأول. على سبيل المثال ، يعد النوعان 5 و 6 ، الساريان حاليًا ، مناسبين لنظامي فقط. إذا كان لديك شيء مثل عامل تصفية وتحتاج إلى تصفية الكثير ، وهو نموذجي (في تطبيق الويب الخاص بك) ، ولكن من أجل استخدامه يجب عليك أولاً تحريك الماوس فوق الحقل ، وانتظر ظهور بعض الحقول ، والانتقال إلى البعض ، والانتظار هناك شيء ، ثم اذهب إلى هناك وأدخل ... يصف بغباء آلية العمل 1 مرة ، وإعطاء نوعًا فريدًا لهذا كله في بنية التبديل - لا تهتم أكثر - احصل على طريقة متعددة الأشكال لجميع مرشحات التطبيقات المماثلة.
لذلك ، يوجد في الحزمة "package logic.testcase.events" فئة تجريدية تصف الإجراءات العامة للحدث. من أجل إنشاء حدث فريد خاص بك ، تحتاج إلى إنشاء فئة جديدة ، ورثت من هذه الفئة المجردة ، وكان لديك بالفعل DataBaseService و seleniumService في المجموعة - ثم تحدد البيانات التي تحتاجها وماذا تفعل به. شيء من هذا القبيل. حسنًا ووفقًا لذلك ، بعد إنشاء حدث جديد ، تحتاج إلى إنهاء فئة مصنع TestCaseActionFactory ، وإذا أمكن ، مخطط XSD. حسنًا ، إذا تمت إضافة سمة جديدة - قم بتعديل النموذج نفسه. في الواقع ، إنه سهل وسريع للغاية.
لذلك ، مجموعة الكشفية.
goToURL - عادةً الإجراء الأول - انقر على الرابط المحدد
مثال: <event type="goToURL"> <orderNumber>10</orderNumber> <url>testURL</url> </event>
fillFields - تعبئة العناصر المحددة
العلامات الخاصة:
- الحقول - حاوية حاوية الكيان
- الحقل - يحتوي على علامة العنصر
- العنصر - يشير إلى اسم العنصر من webPackage
- القيمة - ما هي القيمة التي يجب الإشارة إليها ، تحتوي على سمة كتابة اختيارية (إذا كان العنصر عبارة عن مربع اختيار ، تتم الإشارة إلى إحدى القيم: "تحديد" أو "إلغاء تحديد")
- السمة type - تشير إلى كيفية أخذ القيمة ، اختياري ، القيمة الافتراضية هي "1"
- 1 - القيمة المحددة مأخوذة
- 2 - في هذه الحالة ، يتم تنفيذ وظيفة JS المحددة من دليل "\ TestSuite \ JS"! هام - يشار إلى اسم ملف txt ، بدون ".txt" (وحتى الآن لقد وجدت تطبيقات لوظائف js حتى الآن فقط في هذا النموذج - أنا استخدامها في مكان واحد لإنشاء نزل عشوائي ، ولكن نطاق التطبيقات الممكنة واسع)
- 3 - في هذه الحالة ، يشار إلى الاستعلام في قاعدة البيانات باعتباره القيمة ، ويستبدل البرنامج النتيجة الأولى لهذا الاستعلام
مثال: <event type="fillingFields"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> <value>test</value> </field> </fields> </event>
checkElementsVisibility - يتحقق من وجود العناصر المحددة في النموذج (أي ، المرئي وليس فقط في DOM). في سمة الحقل ، يمكن تحديد عنصر من WebPackage أو xpath مباشرة
مثال: <event type="checkElementsVisibility"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> </field> <field> <xpath>test</xpath> </field> </fields> </event>
checkElementsInVisibility - تشبه checkElementsVisibility ، ولكن العكس
clickElement - انقر فوق العنصر المحدد
مثال: <event type="clickElement"> <orderNumber>10</orderNumber> <elementName>test</elementName> </event>
checkInputValues - تحقق من القيم المدخلة
مثال: <event type="checkInputValues"> <orderNumber>10</orderNumber> <fields> <field> <element>test</element> <value>test</value> </field> </fields> </event>
dbUpdate - إجراء تحديث في قاعدة البيانات (
يتفاعل oXygen بشكل غريب مع حدث واحد من نوع dbUpdate - لا أعرف ماذا أفعل به ولا أفهم لماذا )
مثال: <event type="dbUpdate"> <orderNumber>10</orderNumber> <dbRequest>update - </dbRequest> </event>
CheckQueryResultWithUtilityValue - تحقق من القيمة التي أدخلها المستخدم مع القيمة من قاعدة البيانات
مثال: <event type="checkQueryResultWithUtilityValue"> <orderNumber>10</orderNumber> <dbRequest>select ...</dbRequest> <utilityValue>test</utilityValue> </event>
checkFieldsPresenceByQueryResult - تحقق من وجود عناصر في النموذج بواسطة xpath حسب النمط. إذا لم يتم تحديد النمط المرغوب ، فسيحدث البحث وفقًا للنمط .//* [text () [يحتوي على (normalize-space (.)، "$")]] ، حيث بدلاً من "$" ستكون هناك قيمة من قاعدة البيانات. عند وصف النموذج الخاص بك ، يجب أن تشير إلى "$" في المكان الذي تريد وضع القيمة فيه من قاعدة البيانات. يوجد في نظامي ما يسمى بالشبكات التي توجد فيها قيم تتشكل عادةً من نوع ما. هذا الحدث لاختبار مثل هذه الشبكات
مثال: <event type="checkFieldsPresenceByQueryResult"> <orderNumber>10</orderNumber> <dbRequest>test</dbRequest> <utilityValue></utilityValue> </event>
انتظر - كل شيء بسيط - في انتظار العدد المحدد من المللي ثانية. لسوء الحظ ، على الرغم من أن هذا يعتبر عكازًا ، إلا أنني سأقول بالتأكيد - من المستحيل في بعض الأحيان الاستغناء عنه
مثال: <event type="wait"> <orderNumber>10</orderNumber> <utilityValue>1000</utilityValue> </event>
scrollDown - قم بالتمرير لأسفل من العنصر المحدد. يتم ذلك بهذه الطريقة: ينقر على العنصر المحدد ويضغط على مفتاح "PgDn". في حالاتي حيث اضطررت إلى التمرير لأسفل ، يعمل بشكل جيد:
مثال: <event type="scrollDown"> <orderNumber>10</orderNumber> <elementName>test</elementName> </event>
userInput - أدخل قيمة في العنصر المحدد. الجهاز شبه التلقائي الوحيد في التشغيل الآلي الخاص بي ، يُستخدم فقط في اختبار captcha. يشار إلى العنصر لإدخال القيمة فيه. يتم إدخال القيمة في مربع الحوار المنبثق.
مثال: <event type="userInput"> <orderNumber>10</orderNumber> <elementName>capch_input</elementName> </event>
عن الكود
لذا ، حاولت أن أصنع المنصة وفقًا لمبادئ العمارة النظيفة بوب.
حزم:
التطبيق - التهيئة والتشغيل + التكوينات والتقرير (لا تأنيب لفئة التقرير - وهذا ما يجعلها بأسرع وقت ممكن ، ثم بأسرع وقت ممكن)
المنطق - المنطق الرئيسي + خدمات السيلينيوم و DB. هناك أحداث.
الطرز - POJO في XML وجميع فئات الكائنات المساعدة
utils - المفردة للسيلينيوم وديسيبل
لتشغيل الكود ، تحتاج إلى تنزيل jdk 12 وتحديده في كل مكان حتى يتم تشغيل رقائقه. في Idea ، يتم ذلك من خلال بنية المشروع -> الوحدات والمشروع. أيضا لا تنسى عن عداء مخضرم. --enable-preview. .
, , JDK ojdbc “SprintAutoTest\src\lib”. , .. – , , ( , , )
ملخص
, , . 1,5 , 5 – 6 . 3700 830 ( 4800 ). , , , , . – -, - , -, , , ( – closeValidation . , , , action, ).
, xml, , (.. 2 – 1- xml , 2- TestCase).
– . , , . :
:
- + — « » + — ( – .. -)
- ( action — event c + )
- , , – -. , – . , . , – ,
- unit ( true false)
-, , , Cucumber BDD ( ):
- . أي , «When», , .
- . ( , ). , Given When – 99% , – , , .
, , :
- ,
- , , ,
- . git, , , , ,
- , , 1 , –
- , , - user friendly
- selenium server. , , CI, Team City .
- . github:
.
, .