يمكن استخدام
كائنات الصفحة كطريقة قوية لتجريد (عزل) اختباراتك من التنفيذ التقني. من المهم أن تتذكر أنه (كائنات الصفحة) يمكن استخدامها لزيادة ثبات الاختبارات والحفاظ على مبدأ
DRY (لا تكرر نفسك) - من خلال تغليف الوظيفة (موقع الويب) بطرق بسيطة.
وبعبارة أخرى
كائن الصفحة هو مثيل لفئة تلخص (تعزل) واجهة المستخدم عن بيئة الاختبار ، وتقدم طرقًا للتفاعل مع واجهة المستخدم ، وتستخرج المعلومات اللازمة.
مصطلحات
مصطلح "
كائن الصفحة " مفهوم عام جدًا. في تجربتي ، يتضمن
كائن الصفحة الأنواع الثلاثة التالية:
- تمثل كائنات المكونات مكونات أو عناصر واجهة تعامل مستخدم محددة في واجهة المستخدم. على سبيل المثال: الجداول والقوائم والمقالات والكتل الأخرى التي تحتوي على مجموعة من المكونات.
- يصف كائن الصفحة منطقة أو واجهة مستخدم معينة في تطبيق ويب. قد يتكون من عدة عناصر مكونة وقد يحتوي على طرق ملائمة للتفاعل مع التجريد الموجود في هذا الكائن.
- يتم استخدام الخبرة لتجميع الوظائف المعقدة ، والتي يتطلب اختبارها عدة خطوات ، أو التفاعل مع عدة صفحات. من تجربتي الخاصة ، استخدمت هذا المفهوم لتجريد السلوك المعقد على الصفحة (اختبار الصفحات التعليمية ، وإنشاء مستخدم جديد ، وما إلى ذلك)
أمثلة
ضع في
اعتبارك اختبار
RSpec Capybara البسيط ، الذي ينشئ مدونات ولا يستخدم كائنات الصفحة:
require 'feature_helper' feature 'Blog management', type: :feature do scenario 'Successfully creating a new blog' do visit '/' click_on 'Form Examples' expect(page).to have_content('Create Blog') fill_in 'blog_title', with: 'My Blog Title' fill_in 'blog_text', with: 'My new blog text' click_on 'Save Blog' expect(page).to have_selector('.blog--show') expect(page).to have_content('My Blog Title') expect(page).to have_content('My new blog text') end scenario 'Entering no data' do visit '/' click_on 'Form Examples' expect(page).to have_content('Create Blog') click_on 'Save Blog' expect(page).to have_content('4 errors stopped this form being submitted') expect(page).to have_content("Title can't be blank") expect(page).to have_content("Text can't be blank") expect(page).to have_content('Title is too short') expect(page).to have_content('Text is too short') end end
دعنا نلقي نظرة فاحصة على الكود ؛ لديه العديد من المشاكل. هناك الإجراءات التالية: التبديل إلى الصفحة المقابلة والتفاعل مع الصفحة والتحقق من المحتوى. يتم تكرار جزء من التعليمات البرمجية ، ولكن يمكن إصلاح ذلك من خلال الالتزام بمبدأ
DRY .
من المهم أن نفهم أن هذا الرمز يصعب الحفاظ عليه إذا كانت هناك تغييرات في التطبيق قيد الاختبار. على سبيل المثال ، قد تتغير فئات العناصر والأسماء والمعرفات ، مما يتطلب تحديثات منتظمة لرمز الاختبار.
أيضًا في هذا الرمز ، لا يوجد "سياق دلالي" ، من الصعب فهم أي سطور من التعليمات البرمجية يتم تجميعها بشكل منطقي.
مقدمة إلى كائنات الصفحة
كما نوقش في قسم المصطلحات ، يمكن استخدام
كائنات الصفحة لتمثيل التجريدات على مستوى العرض.
بأخذ المثال السابق واستخدام
كائن الصفحة لإنشاء مدونات جديدة وعرض المدونات ، يمكننا مسح رمز المثال السابق.
بعد التخلص من معلومات محددة حول التنفيذ الفني ، يجب أن تكون النتيجة النهائية (الكود) قابلة للقراءة ويجب ألا تحتوي على معلومات محددة حول واجهة المستخدم (id ، css classes ، وما إلى ذلك).
require 'feature_helper' require_relative '../pages/new_blog' require_relative '../pages/view_blog' feature 'Blog management', type: :feature do let(:new_blog_page) { ::Pages::NewBlog.new } let(:view_blog_page) { ::Pages::ViewBlog.new } before :each do new_blog_page.visit_location end scenario 'Successfully creating a new blog' do new_blog_page.create title: 'My Blog Title', text: 'My new blog text' expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text' end scenario 'Entering no data' do new_blog_page.create title: '', text: '' expect(view_blog_page).to_not have_loaded expect(new_blog_page).to have_errors "Title can't be blank", "Text can't be blank", "Title is too short", "Text is too short" end end
إنشاء كائنات الصفحةتتمثل الخطوة الأولى في إنشاء
كائنات الصفحة في إنشاء بنية
أساسية لفئة الصفحة :
module Pages class NewBlog include RSpec::Matchers include Capybara::DSL
توصيل (تمكين)
Capybara :: DSL للسماح لمثيلات
كائنات الصفحة باستخدام الطرق المتاحة في
Capybara has_css? '.foo' has_content? 'hello world' find('.foo').click
بالإضافة إلى ذلك ، اعتدت
تشمل RSpec :: Matchers
في الأمثلة أعلاه لاستخدام مكتبة RSpec التوقعية.
لا تنتهك الاتفاقية ، يجب ألا تشتمل
كائنات الصفحة على
توقع (توقعات) . ومع ذلك ، عند الاقتضاء ، أفضّل أن يعتمد هذا النهج على آليات
Capybara المضمنة لمعالجة الظروف.
على سبيل المثال ،
ستتوقع شفرة
Capybara التالية وجود
"foo" داخل
كائنات الصفحة (في هذه الحالة ،
ذاتي ):
expect(self).to have_content 'foo'
ومع ذلك ، في التعليمات البرمجية التالية:
expect(page_object.content).to match 'foo'
من المحتمل حدوث أخطاء غير متوقعة (قد يحدث اختبار عائم) ، نظرًا لأن
page_object.content يتم فحصه على الفور للتأكد من توافقه مع الشرط ، وربما لم يتم الإعلان عنه بعد. للحصول على المزيد من الأمثلة ، أوصي بقراءة
اختبارات التكامل غير المتزامنة التي يمكن الاعتماد عليها في thinkbot مع Capybara .
طريقة إنشاء
يمكننا تلخيص (وصف) المكان (المنطقة) الذي نريد منه الحصول على البيانات ، في إطار طريقة واحدة:
def visit_location visit '/blogs/new'
من المهم اختيار الأسماء
الصحيحة بشكل شبه صحيح لأساليب
كائنات الصفحة الخاصة بك
def create(title:, text:)
بشكل عام ، من المهم اتباع مبدأ الأساليب المتكاملة وظيفياً والالتزام ، حيثما أمكن ، بمبدأ المسؤولية الفردية (مبدأ المسؤولية الفردية).
كائنات المكون
في مثالنا ، نستخدم فئة NewBlog ، لكن لا يوجد تطبيق لإنشاء.
نظرًا لأننا نتفاعل مع النموذج ، يمكننا إضافة فصل لتمثيل هذا المكون:
حيث يمكن إخفاء تطبيق أساليب BlogForm:
module Components class BlogForm include RSpec::Matchers include Capybara::DSL def create(title:, text:) within blog_form do fill_in 'blog_title', with: title fill_in 'blog_text', with: text click_on 'Save Blog' end end private def blog_form find('.blog--new') end end end
كل ذلك معا
باستخدام الفئات المذكورة أعلاه ، يمكنك الآن الاستعلام عن
كائنات الصفحة في صفحتك والبدء في إنشاء جزء منها كجزء من وصف الكائن.
require 'feature_helper' require_relative '../pages/new_blog' require_relative '../pages/view_blog' feature 'Blog management', type: :feature do let(:new_blog_page) { ::Pages::NewBlog.new } let(:view_blog_page) { ::Pages::ViewBlog.new }
ملاحظة: لقد قمتُ بإنشاء كائن صفحة عن قصد يدويًا في الجزء العلوي من ملف الكائن. في بعض اختبارات RSpec ، قد يكون من المناسب تنزيل جميع ملفات الدعم تلقائيًا وإتاحة الوصول إليها في ملفات الكائنات ، ومع ذلك ، يمكن أن يؤدي ذلك إلى عبء عمل كبير عند استخدام أجزاء كبيرة من التعليمات البرمجية. على وجه الخصوص ، سيؤدي ذلك إلى بدء التشغيل البطيء والتبعيات الدورية غير المقصودة المحتملة.
استدعاء كائنات الصفحة
الآن في كل سيناريو ، سيكون لدينا وصول إلى مثيلات
new_blog_page و
view_blog_page :
scenario 'Successfully creating a new blog' do new_blog_page.create title: 'My Blog Title', text: 'My new blog text' expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text' end
اصطلاحات التسمية / طرق المسند
كما هو الحال مع معظم الأشياء في Rails / Ruby ، هناك اصطلاحات قد تبدو غير مهمة (غير ملزمة) تمامًا في لمحة.
في اختباراتنا ، تفاعلنا مع كائن الصفحة باستخدام
have_loaded و
have_blog :
expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text'
ومع ذلك ، فإن أسماء طريقة كائن
صفحتنا بالفعل
has_loaded؟ و
has_blog؟ :
def has_loaded?
هذا تمييز دقيق يحتاج إلى اهتمام. لمزيد من المعلومات حول هذه الاتفاقية ، أوصي بقراءة الرابط التالي
للمطابقة التقريبية .
بوابة ، شفرة المصدر المستخدمة في الأمثلةالأصلي