> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# أدوات ضمان الجودة QA: أيها يناسب فريقك؟
- URL: https://arabroot.io/articles/ادوات-ضمان-الجودة-qa-واختبار-البرمجيات/
- Published: 2026-10-13T01:30:00.000Z
- Updated: 2026-10-12T20:57:02.000Z
- Description: خريطة لأدوات الاختبار وإدارة حالاته بتراخيصها وأسعارها، مع جدول يساعدك على اختيار ما يحتاجه فريقك فعلاً.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, DevOps والتشغيل, DevOps وCI/CD, الاستضافة الذاتية

في [مقال ضمان الجودة QA](https://arabroot.io/articles/%D9%85%D8%A7-%D9%87%D9%88-%D8%B6%D9%85%D8%A7%D9%86-%D8%A7%D9%84%D8%AC%D9%88%D8%AF%D8%A9-qa-%D9%88%D9%83%D9%8A%D9%81-%D9%8A%D8%B9%D9%85%D9%84-%D9%81%D8%B1%D9%8A%D9%82-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1/) صممنا حالات الاختبار Test Cases لميزة استرداد التذاكر، ورأينا أن القيم الحدية Boundary Values وجدول القرار Decision Table وجدا خطأين لم تجدهما الاختبارات العادية. والسؤال الذي يأتي بعد ذلك عملي: بأي أداة تشغل هذه الحالات؟ فإذا بحثت عن «أدوات الاختبار» سوف تجد مئات الأسماء، بعضها يتنافس فعلاً، وبعضها يختبر شيئاً مختلفاً تماماً، وبعضها مجاني على سيرفرك وبعضها اشتراك شهري لكل مستخدم.

وسوف نناقش في هذا المقال ما يلي:

- خريطة الأدوات حسب ما تختبره: الوحدة والمتصفح وال API والأداء وإدارة حالات الاختبار Test Management وأسلوب BDD والاختبار المرئي والعقود والطفرات وسهولة الوصول Accessibility والجوال.
- التراخيص وآخر الإصدارات والأسعار كما هي في صفحات الشركات في 12 أكتوبر 2026.
- تجربة حقيقية لأربع أدوات على تطبيق الاسترداد نفسه: أداة Bruno لاختبار ال API وCucumber للسلوك وأداة axe لسهولة الوصول Accessibility وأداة Stryker للطفرات.
- جدول قرار يساعدك على الاختيار حسب نوع فريقك.

وبعض هذه العائلات شرحناها بالتفصيل في مقالات أخرى، فمقارنة Playwright وCypress وSelenium في مقال أنواع الاختبارات، ومقارنة k6 وLocust وJMeter وGatling بقياسات حقيقية في مقال اختبار الأحمال، وأدوات جودة الكود في مقالها الخاص، ولن نكرر ذلك هنا:

[![](https://arabroot.io/content/images/2026/10/qa-intro-cover-cover.webp)دليلما هو ضمان الجودة QA وكيف يعمل فريق الاختبار؟كيف تفرق بين QA وQC والاختبار، وتصمم حالات الاختبار Test Cases التي تجد الأخطاء قبل أن يجدها عملاؤك.](https://arabroot.io/articles/%D9%85%D8%A7-%D9%87%D9%88-%D8%B6%D9%85%D8%A7%D9%86-%D8%A7%D9%84%D8%AC%D9%88%D8%AF%D8%A9-qa-%D9%88%D9%83%D9%8A%D9%81-%D9%8A%D8%B9%D9%85%D9%84-%D9%81%D8%B1%D9%8A%D9%82-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1/)[![](https://arabroot.io/content/images/2026/10/testing-types-cover-cover.webp)دليلأنواع الاختبارات للمبتدئين: من Unit إلى E2E مع Playwrightما الذي يكشفه كل نوع من الاختبارات وما الذي يفوته، وكم اختباراً تكتب من كل نوع، وأين يعمل كل منها في خط CI.](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-unit-%D9%88-integration-%D9%88-e2e/)[![](https://arabroot.io/content/images/2026/10/load-testing-cover-cover.webp)دليلاختبار الأحمال والضغط Load Testing للمبتدئينلماذا يعمل تطبيقك مع خمسة مستخدمين وينهار مع خمسمئة، وكيف تكتشف ذلك وتصلحه قبل يوم الإطلاق.](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D8%AD%D9%85%D8%A7%D9%84-%D9%88%D8%A7%D9%84%D8%B6%D8%BA%D8%B7-load-%D9%88-stress-testing-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)[![](https://arabroot.io/content/images/2026/10/code-quality-tools-cover-cover.webp)دليلأدوات جودة الكود: أيها تختار لفريقك؟مقارنة عملية بين مدققات كل لغة والأدوات الجامعة ومنصات الجودة، بتراخيصها وأسعارها ونتائجها على مستودع واحد.](https://arabroot.io/articles/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A7%D8%AF%D9%88%D8%A7%D8%AA-%D8%AC%D9%88%D8%AF%D8%A9-%D8%A7%D9%84%D9%83%D9%88%D8%AF/)

## خريطة الأدوات: كل عائلة تختبر طبقة

أكثر خطأ نراه عند اختيار الأدوات أن الفريق يقارن أداة بأداة لا تنافسها، مثل Postman وPlaywright، أو يظن أن أداة إدارة الاختبارات مثل TestRail تختبر شيئاً بنفسها. والصورة التالية تبين العائلات وأين تنظر كل واحدة في التطبيق:

![خريطة حول مربع التطبيق بواجهته وال API وقاعدة البيانات: اختبار الوحدة pytest وJUnit وJest وVitest، واختبار الطفرات Stryker وPIT وmutmut، وإدارة الاختبارات Kiwi TCMS وTestLink وQase وTestRail، والمتصفح Playwright وCypress وSelenium، وال API والعقود Bruno وPostman وHoppscotch وKarate وPact، والأداء k6 وJMeter وGatling وLocust، و BDD والاختبار المرئي Cucumber وBackstopJS، وسهولة الوصول axe وLighthouse، والجوال Appium وMaestro](https://arabroot.io/content/images/2026/10/qa-tools-01-tools-map.webp)

كل عائلة ترى طبقة مختلفة من التطبيق، وأدوات الإدارة تجمع النتائج ولا تختبر

والقاعدة العملية أن فريقاً صغيراً يحتاج من ثلاث إلى أربع عائلات فقط: إطار وحدة للغته، وأداة API، وأداة متصفح لمسارين أو ثلاثة مهمة، وCI يشغلها كلها، ثم تضاف العائلات الأخرى عندما يظهر سبب حقيقي لها، مثل عميل حكومي يشترط سهولة الوصول Accessibility أو خدمات كثيرة تتواصل عبر ال API.

## أطر اختبار الوحدة Unit لكل لغة

هذه الطبقة لا تحتاج قراراً كبيراً، فلكل لغة إطار شائع تستخدمه، وقد عرضنا أشهرها في [جدول الأدوات في مقال أنواع الاختبارات](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-unit-%D9%88-integration-%D9%88-e2e/#tools). والجدول التالي يضيف الإصدارات الحالية ولغات أخرى، والتراخيص من ملف الترخيص في مستودع كل أداة:

| اللغة                      | الإطار                                                                                                                           | الترخيص                   | آخر إصدار في أكتوبر 2026                     |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | -------------------------------------------- |
| لغة Python                 | [أداة pytest](https://github.com/pytest-dev/pytest?ref=arabroot.io)                                                              | رخصة MIT                  | الإصدار 9.1.1                                |
| لغة Java                   | [إطار JUnit](https://github.com/junit-team/junit-framework?ref=arabroot.io)                                                      | رخصة EPL-2.0              | الإصدار 6.1.3                                |
| لغة Kotlin                 | [إطار Kotest](https://github.com/kotest/kotest?ref=arabroot.io)                                                                  | رخصة Apache-2.0           | الإصدار 6.2.5                                |
| لغة JavaScript وTypeScript | [أداة Vitest](https://github.com/vitest-dev/vitest?ref=arabroot.io) و[أداة Jest](https://github.com/jestjs/jest?ref=arabroot.io) | رخصة MIT للاثنتين         | الإصدار 5.0.4 والإصدار 30.5.2                |
| لغة Go                     | [الحزمة testing](https://pkg.go.dev/testing?ref=arabroot.io)                                                                     | رخصة BSD-3-Clause         | جزء من Go نفسها                              |
| لغة C# و.NET               | [إطار xUnit.net](https://github.com/xunit/xunit?ref=arabroot.io) و[إطار NUnit](https://github.com/nunit/nunit?ref=arabroot.io)   | رخصة Apache-2.0 ورخصة MIT | النسخة v3 من الأول، والإصدار 5.0.0 من الثاني |
| لغة PHP                    | [إطار PHPUnit](https://github.com/sebastianbergmann/phpunit?ref=arabroot.io)                                                     | رخصة BSD-3-Clause         | الإصدار 13.4.2                               |
| لغة Ruby                   | [إطار RSpec](https://rubygems.org/gems/rspec?ref=arabroot.io)                                                                    | رخصة MIT                  | الإصدار 3.13.2                               |

والنصيحة الوحيدة هنا: استخدم ما يستخدمه مجتمع لغتك وإطار عملك، والسبب أن الإضافات والأمثلة وقدرة الوكلاء الأذكياء على كتابة الاختبارات كلها تتبع الإطار الأشهر.

## اختبار المتصفح E2E مع Playwright وCypress وSelenium

قارنا الثلاثة بنقاط القوة والضعف في [مقال أنواع الاختبارات](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-unit-%D9%88-integration-%D9%88-e2e/#compare-e2e)، والجديد هنا الإصدارات والتكلفة:

- **أداة** [**Playwright**](https://github.com/microsoft/playwright?ref=arabroot.io)**:** بترخيص Apache-2.0، وآخر إصدار 1.64.0، ولا توجد خدمة مدفوعة إلزامية، فالتوازي والتقارير وعارض التتبع كلها داخل الأداة.
- **أداة** [**Cypress**](https://github.com/cypress-io/cypress?ref=arabroot.io)**:** بترخيص MIT، وآخر إصدار 16.1.1، أما خدمة [Cypress Cloud](https://www.cypress.io/pricing?ref=arabroot.io) للتسجيلات والتوازي الموزع فمجانية حتى 500 نتيجة اختبار في الشهر، وخطة Team بسعر 67 دولاراً في الشهر بالدفع السنوي 799 دولاراً، وخطة Business بسعر 267 دولاراً في الشهر بالدفع السنوي 3199 دولاراً.
- **أداة** [**Selenium**](https://github.com/SeleniumHQ/selenium?ref=arabroot.io)**:** بترخيص Apache-2.0، وآخر إصدار 4.51.0، ومجانية بالكامل بما فيها Selenium Grid الذي تستضيفه بنفسك.

ولمشروع جديد في 2026 نختار Playwright للأسباب التي شرحناها هناك، ونبقى على Selenium عندما يكون عند الفريق مكتبة اختبارات كبيرة مكتوبة به، أو يحتاج متصفحات حقيقية مثبتة عبر WebDriver.

## أدوات اختبار ال API مثل Bruno وPostman وHoppscotch وKarate

أدوات ال API تبدأ عادة كعميل HTTP بواجهة رسومية يجرب فيه المطور الطلبات، ثم تصبح أداة اختبار عندما تضيف إليها التحققات Assertions وتشغلها من سطر الأوامر في CI. والفرق الأهم بينها ليس الواجهة، وإنما أين تحفظ الطلبات: في ملفات نصية داخل المستودع تراجعها مع الكود، أم في سحابة الشركة.

### أداة Bruno

أداة [Bruno](https://github.com/usebruno/bruno?ref=arabroot.io) بترخيص MIT، وآخر إصدار 4.2.1، وتحفظ كل طلب في ملف نصي بامتداد `.bru` داخل مستودعك، فتراجع تغيير الطلبات في طلب الدمج كما تراجع الكود، وأداة سطر الأوامر `@usebruno/cli` تشغل المجموعة في CI. وتذكر [صفحة الأسعار](https://www.usebruno.com/pricing?ref=arabroot.io) أن النسخة المفتوحة مجانية، وأن خطة Pro بسعر 6 دولارات للمستخدم شهرياً، وخطة Ultimate بسعر 11 دولاراً، وكلاهما بالدفع السنوي فقط، وتضيفان مثلاً مزودي Git الخاصين وتسجيل الدخول الموحد SSO.

**نقاط القوة:** الملفات في Git، ولا يوجد حساب إجباري، وأداة سطر الأوامر مجانية.

**نقاط الضعف:** أحدث من Postman، فالأمثلة والتكامل مع الأدوات الأخرى أقل، وبعض ميزات الفرق في الخطط المدفوعة.

### أداة Postman

تطبيق Postman مغلق المصدر، وأداة تشغيل مجموعاته من سطر الأوامر [Newman](https://github.com/postmanlabs/newman?ref=arabroot.io) بترخيص Apache-2.0\. ولاحظ أن [صفحة الأسعار](https://www.postman.com/pricing/?ref=arabroot.io) تغيرت، فالخطط اليوم هي Free لمستخدم واحد، وSolo بسعر 9 دولارات في الشهر بالدفع السنوي، وTeam بسعر 19 دولاراً للمستخدم في الشهر بالدفع السنوي، وEnterprise بسعر خاص، والصفحة نفسها تقول إن خطتي Basic وProfessional لم تعودا متاحتين للعملاء الجدد، فإذا قرأت مقارنة قديمة تذكرهما فأسعارها لم تعد صحيحة.

**نقاط القوة:** الأشهر، وفيه مراقبة Monitors وتوثيق ومحاكاة Mock Servers في مكان واحد.

**نقاط الضعف:** الطلبات تحفظ في سحابة الشركة وليس في مستودعك، والخطة المجانية لمستخدم واحد فقط، فالفريق يدفع من أول يوم.

### أداة Hoppscotch

أداة [Hoppscotch](https://github.com/hoppscotch/hoppscotch?ref=arabroot.io) بترخيص MIT، وآخر إصدار 2026.9.0، وهي واجهة ويب يمكنك استضافتها على سيرفرك، ومعها أداة سطر أوامر. وتذكر [صفحة الأسعار](https://hoppscotch.com/pricing?ref=arabroot.io) خطة مجانية، وخطة Business بسعر 6 دولارات للمستخدم شهرياً، وEnterprise بسعر 11 دولاراً، بالدفع السنوي.

**نقاط القوة:** تعمل من المتصفح دون تثبيت، وتستضاف على سيرفرك، وتدعم REST وGraphQL وWebSocket.

**نقاط الضعف:** التشغيل في CI أقل نضجاً من Bruno وNewman.

### إطار Karate

إطار [Karate](https://github.com/karatelabs/karate?ref=arabroot.io) بترخيص MIT، وآخر إصدار 2.1.3، ويختلف عن الثلاثة السابقة بأنه إطار اختبار وليس عميل HTTP، فتكتب الاختبارات بلغة قريبة من Gherkin، ويعمل على JVM، ويجمع اختبار ال API والأداء عبر Gatling. وشركة Karate Labs تبيع إضافات مثل IDE Plus بسعر 100 دولار للمستخدم سنوياً وPro بسعر 640 دولاراً كما في [صفحة الأسعار](https://www.karatelabs.io/pricing?ref=arabroot.io).

**نقاط القوة:** اختبارات API مقروءة دون كود Java، وتحققات قوية على JSON.

**نقاط الضعف:** يحتاج JVM، ولغته الخاصة شيء جديد يتعلمه الفريق.

## أدوات الأداء: k6 وJMeter وGatling وLocust

شغلنا الأربعة على السيناريو نفسه وقارنا نتائجها في [مقال اختبار الأحمال](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D8%AD%D9%85%D8%A7%D9%84-%D9%88%D8%A7%D9%84%D8%B6%D8%BA%D8%B7-load-%D9%88-stress-testing-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/#lab-tools-compare)، وهنا التراخيص والتكلفة فقط:

| الأداة                                                             | الترخيص         | آخر إصدار                   | ملاحظة                                                                                                                                                         |
| ------------------------------------------------------------------ | --------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| أداة [k6](https://github.com/grafana/k6?ref=arabroot.io)           | رخصة AGPL-3.0   | الإصدار 2.3.0               | خدمة [Grafana Cloud k6](https://grafana.com/pricing/?ref=arabroot.io) مجانية حتى 500 ساعة مستخدم افتراضي VUh في الشهر، ثم تبدأ من 0.15 دولار للساعة في خطة Pro |
| أداة [Apache JMeter](https://jmeter.apache.org/?ref=arabroot.io)   | رخصة Apache-2.0 | الإصدار 5.6.3 من يناير 2024 | تعمل على Java، ولها واجهة رسومية لبناء الاختبار                                                                                                                |
| أداة [Gatling](https://github.com/gatling/gatling?ref=arabroot.io) | رخصة Apache-2.0 | الإصدار 3.16.0              | خدمة [Gatling Enterprise](https://gatling.io/pricing?ref=arabroot.io) تبدأ من 99 يورو في الشهر                                                                 |
| أداة [Locust](https://github.com/locustio/locust?ref=arabroot.io)  | رخصة MIT        | الإصدار 2.46.7              | تكتب السيناريو بلغة Python                                                                                                                                     |

ولاحظ أن ترخيص AGPL الخاص بأداة k6 لا يؤثر عليك إذا شغلتها لاختبار تطبيقك، وإنما يهم من يعدلها ويقدمها كخدمة للآخرين.

## إدارة حالات الاختبار Test Management

هذه العائلة لا تختبر شيئاً، وإنما تحفظ حالات الاختبار Test Cases وخطط الاختبار وجولات التشغيل Test Runs ونتائجها، وتربط كل حالة بالمتطلب وبالخطأ الذي كشفته، وتعطي المدير تقرير «ماذا اختبرنا في هذا الإصدار؟». وتحتاجها عندما يكون عندك اختبار يدوي كثير، أو عميل أو جهة رقابية تطلب دليلاً على ما اختبر، أما الفريق الذي كل اختباراته آلية في CI فتكفيه غالباً تقارير CI نفسها.

| الأداة                                                                                   | الترخيص والاستضافة                                  | السعر في أكتوبر 2026                                                                                                                               |
| ---------------------------------------------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| أداة [Kiwi TCMS](https://github.com/kiwitcms/Kiwi?ref=arabroot.io)                       | رخصة GPL-2.0، وتستضيفها على سيرفرك، وآخر إصدار 16.6 | مجانية على سيرفرك، ولها [اشتراكات دعم](https://kiwitcms.org/?ref=arabroot.io) تبدأ من 35 دولاراً في الشهر، واستضافة مدارة بسعر 2000 دولار في الشهر |
| أداة [TestLink](https://github.com/TestLinkOpenSourceTRMS/testlink-code?ref=arabroot.io) | رخصة GPL-2.0، وتستضيفها على سيرفرك                  | مجانية، ولكن آخر تغيير في مستودعها كان في ديسمبر 2025                                                                                              |
| خدمة [Qase](https://qase.io/pricing?ref=arabroot.io)                                     | سحابية                                              | مجانية حتى 4 مستخدمين ومشروعين، وخطة Teams بسعر 35 دولاراً للمستخدم شهرياً بالدفع السنوي أو 42 دولاراً بالدفع الشهري                               |
| خدمة [TestRail](https://www.testrail.com/pricing/?ref=arabroot.io)                       | سحابية، أو على سيرفرك من 10 مستخدمين                | خطة Essentials بسعر 22 دولاراً للمستخدم شهرياً، وProfessional بسعر 38 دولاراً، وEnterprise بسعر 78 دولاراً، بالدفع السنوي                          |

**نقاط القوة:** تاريخ كامل لما اختبر ومن اختبره، وربط بالمتطلبات والأخطاء، واستيراد نتائج الاختبارات الآلية بصيغة JUnit XML.

**نقاط الضعف:** الحالات المكتوبة يدوياً تتقادم بسرعة إذا لم يحدثها أحد، والأدوات السحابية تحسب لكل مستخدم، فتصبح مكلفة عندما يحتاج المطورون كلهم حسابات. وإذا كنت تبدأ اليوم على سيرفرك فنفضل Kiwi TCMS على TestLink، والسبب أن الأولى أصدرت نسختها 16.6 في أكتوبر 2026، والثانية لم يتغير مستودعها منذ ديسمبر 2025.

## أسلوب BDD مع Cucumber

أسلوب التطوير بالسلوك Behavior-Driven Development، واختصاراً BDD، يكتب القواعد كأمثلة بلغة يقرؤها مالك المنتج، بصيغة Gherkin المعروفة: Given وWhen وThen، ثم يربط كل سطر منها بدالة في الكود. والأداة الأشهر [Cucumber](https://github.com/cucumber/cucumber-js?ref=arabroot.io) بترخيص MIT، ولها نسخة JavaScript بالإصدار 13.3.0 ونسخة [Java](https://github.com/cucumber/cucumber-jvm?ref=arabroot.io)، وفي Python تستخدم غالباً [behave](https://github.com/behave/behave?ref=arabroot.io) بترخيص BSD-2-Clause. ولغة Gherkin تدعم الكلمات المفتاحية العربية مثل «خاصية» و«سيناريو» و«بفرض» و«عندما» و«ثم» كما في [قائمة اللغات الرسمية](https://cucumber.io/docs/gherkin/languages/?ref=arabroot.io).

**نقاط القوة:** القواعد تصبح وثيقة حية تختبر نفسها، وجدول الأمثلة Examples مكان طبيعي للقيم الحدية Boundary Values.

**نقاط الضعف:** إذا لم يقرأ مالك المنتج ملفات Gherkin فعلاً فهي طبقة إضافية بلا فائدة فوق اختبارات عادية، وهذا ما يحدث في كثير من الفرق.

## الاختبار المرئي Visual Regression

شرحنا فكرته في [مقال أنواع الاختبارات](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-unit-%D9%88-integration-%D9%88-e2e/#visual)، وأسهل بداية هي التحقق [toHaveScreenshot()](https://playwright.dev/docs/test-snapshots?ref=arabroot.io) المدمج في Playwright. أما الأدوات المفتوحة المخصصة فأغلبها تباطأ، فآخر إصدار من [BackstopJS](https://github.com/garris/BackstopJS?ref=arabroot.io) على npm كان في سبتمبر 2024، وآخر إصدار من [Lost Pixel](https://github.com/lost-pixel/lost-pixel?ref=arabroot.io) في نوفمبر 2024، وكلاهما بترخيص MIT. والخدمات السحابية تقارن الصور على سيرفراتها وتعرض الفروق للمراجعة:

- **خدمة** [**Percy**](https://www.browserstack.com/pricing?product=percy&ref=arabroot.io) **من BrowserStack:** مجانية حتى 5000 لقطة في الشهر، وخطة Desktop بسعر 199 دولاراً في الشهر بالدفع السنوي.
- **خدمة** [**Chromatic**](https://www.chromatic.com/pricing?ref=arabroot.io)**:** مجانية حتى 5000 لقطة، وخطة Starter بسعر 179 دولاراً في الشهر.
- **خدمة** [**Applitools**](https://applitools.com/pricing/?ref=arabroot.io)**:** خطة Starter بسعر 667 دولاراً في الشهر بالدفع السنوي، والخطط الأكبر بسعر خاص.

## اختبار العقود Contract Testing مع Pact

شرحنا الفكرة في [قسم اختبار العقد](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-unit-%D9%88-integration-%D9%88-e2e/#contract). ومكتبات Pact مفتوحة: [pact-js](https://github.com/pact-foundation/pact-js?ref=arabroot.io) بترخيص MIT بالإصدار 17.1.4، و[pact-jvm](https://github.com/pact-foundation/pact-jvm?ref=arabroot.io) بترخيص Apache-2.0، و[Pact Broker](https://github.com/pact-foundation/pact%5Fbroker?ref=arabroot.io) الذي يحفظ العقود بترخيص MIT وتستضيفه بنفسك. أما خدمة [PactFlow](https://pactflow.io/pricing/?ref=arabroot.io) من SmartBear فمجانية حتى تكاملين، وخطة Team بسعر 127 دولاراً في الشهر أو 1385 دولاراً في السنة. ولا تحتاج هذه العائلة إلا عندما تملك فرق مختلفة خدمات تتواصل عبر ال API.

## اختبار الطفرات Mutation Testing

أداة الطفرات تغير الكود عمداً، مثل `>=` إلى `>`، ثم تشغل الاختبارات، فإذا نجحت الاختبارات مع الكود المغير فهي لا تتحقق من هذا السطر فعلاً، وقد استخدمنا mutmut في [مقال الاختبارات بالذكاء الاصطناعي](https://arabroot.io/articles/%D8%A7%D8%AA%D9%85%D8%AA%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/#mutation). والأدوات حسب اللغة:

- **أداة** [**StrykerJS**](https://github.com/stryker-mutator/stryker-js?ref=arabroot.io) بترخيص Apache-2.0 والإصدار 10.0.0 للغة JavaScript وTypeScript، ومعها Stryker.NET بالإصدار 5.0.0.
- **أداة** [**PIT**](https://github.com/hcoles/pitest?ref=arabroot.io) بترخيص Apache-2.0 والإصدار 1.30.0 للغة Java، ولها إضافات تجارية من [Arcmutate](https://www.arcmutate.com/?ref=arabroot.io) لا تنشر الشركة أسعارها.
- **أداة** [**mutmut**](https://github.com/boxed/mutmut?ref=arabroot.io) بترخيص BSD-3-Clause والإصدار 3.8.0 للغة Python.

## سهولة الوصول Accessibility

المكتبة الأساسية هي [axe-core](https://github.com/dequelabs/axe-core?ref=arabroot.io) بترخيص MPL-2.0 والإصدار 4.14.0، وتفحص الصفحة حسب قواعد WCAG، وتستخدمها أدوات كثيرة من داخلها، ومنها الحزمة `@axe-core/playwright` التي سوف نستخدمها في التجربة. ومعها [Lighthouse](https://github.com/GoogleChrome/lighthouse?ref=arabroot.io) بترخيص Apache-2.0 الذي يعطي درجة لسهولة الوصول Accessibility مع الأداء، و[Pa11y](https://github.com/pa11y/pa11y?ref=arabroot.io) بترخيص LGPL-3.0 لفحص قائمة صفحات من سطر الأوامر. أما [axe DevTools](https://www.deque.com/axe/devtools/pricing/?ref=arabroot.io) التجاري فإضافة المتصفح منه مجانية، والنسخ المدفوعة بلا سعر منشور.

ولاحظ أن الأدوات الآلية تجد جزءاً فقط من مشاكل سهولة الوصول Accessibility، وسوف ترى مثالاً على ذلك في التجربة.

## اختبار تطبيقات الجوال

- **أداة** [**Appium**](https://github.com/appium/appium?ref=arabroot.io)**:** بترخيص Apache-2.0 والإصدار 3.8.0، وتتحكم في تطبيقات Android وiOS عبر بروتوكول WebDriver نفسه الذي يستخدمه Selenium، فتكتب الاختبارات بأي لغة.
- **أداة** [**Maestro**](https://github.com/mobile-dev-inc/maestro?ref=arabroot.io)**:** بترخيص Apache-2.0 والإصدار 2.11.0، وتكتب الاختبار بملف YAML قصير، وهي أسهل بداية، وخدمة [Maestro Cloud](https://maestro.dev/pricing?ref=arabroot.io) لتشغيلها على أجهزة سحابية بسعر 250 دولاراً للجهاز في الشهر.
- **أطر المنصة نفسها:** [Espresso](https://developer.android.com/training/testing/espresso?ref=arabroot.io) من Google لنظام Android و[XCTest](https://developer.apple.com/documentation/xctest?ref=arabroot.io) من Apple لنظام iOS، وهي الأسرع والأكثر استقراراً، ولكن كل واحد لمنصة واحدة وبلغتها.

## تجربة عملية: أربع أدوات على تطبيق واحد

نأخذ قواعد الاسترداد من [تجربة مقال ضمان الجودة](https://arabroot.io/articles/%D9%85%D8%A7-%D9%87%D9%88-%D8%B6%D9%85%D8%A7%D9%86-%D8%A7%D9%84%D8%AC%D9%88%D8%AF%D8%A9-qa-%D9%88%D9%83%D9%8A%D9%81-%D9%8A%D8%B9%D9%85%D9%84-%D9%81%D8%B1%D9%8A%D9%82-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1/#lab) بعد إصلاحها، ونكتبها بلغة JavaScript في تطبيق Node.js صغير له صفحة ويب و API، ثم نشغل عليه أربع أدوات، كل واحدة في Container منفصل على شبكة `lab-qa-net`، وكل Container يبدأ اسمه بالبادئة `lab-qa-`. ووضعنا في التطبيق عن قصد خطأ في ال API ومشكلتين في الصفحة، والصورة التالية تبين ما تنظر إليه كل أداة:

![تطبيق lab-qa-app في الوسط بصفحة ويب و API لحساب الاسترداد والملف refund.js، وحوله أربع أدوات: Bruno CLI يفحص ال API ووجد رمز حالة 200 بدل 400، وCucumber يشغل القواعد كأمثلة ونجحت ستة سيناريوهات، وaxe مع Playwright يفحص الصفحة ووجد ضعف التباين وقائمة بلا اسم، وStryker يغير الملف refund.js ورفع درجة الطفرات من 87.80% إلى 100%](https://arabroot.io/content/images/2026/10/qa-tools-02-lab.webp)

كل أداة رأت طبقة مختلفة، ولم تجد أي واحدة منها ما وجدته الأخرى

### التطبيق

شكل المجلد كما يلي:

```text
lab/
├── compose.yaml
├── app/
│   ├── package.json
│   ├── refund.js
│   ├── server.js
│   ├── stryker.config.json
│   └── test/refund.test.js
├── bruno/refunds/
│   ├── bruno.json
│   ├── environments/local.bru
│   ├── vip-under-24h.bru
│   ├── cancelled-promo.bru
│   └── unknown-ticket.bru
├── bdd/
│   ├── package.json
│   └── features/
│       ├── refund.feature
│       └── step_definitions/steps.mjs
└── a11y/
    ├── package.json
    ├── playwright.config.ts
    └── tests/a11y.spec.ts
```

الملف `app/refund.js` هو القواعد نفسها بعد الإصلاح:

```javascript
// Ticket refund policy, same rules as the qa-intro article (fixed version).
export const TYPES = ['standard', 'vip', 'promo'];
const HOUR = 3600 * 1000;

export function refundPercent(ticketType, eventStart, requestedAt, cancelled = false) {
  if (!TYPES.includes(ticketType)) throw new Error(`unknown ticket type: ${ticketType}`);
  if (cancelled) return 100;
  if (ticketType === 'promo') return 0;
  const left = eventStart - requestedAt; // milliseconds
  if (left <= 0) return 0;
  if (ticketType === 'vip') return left >= 24 * HOUR ? 100 : 50;
  if (left >= 72 * HOUR) return 100;
  if (left >= 24 * HOUR) return 50;
  return 0;
}
```

والملف `app/server.js` يقدم الصفحة والمسار `POST /api/refunds/quote` دون أي مكتبة خارجية، وهذا الجزء المهم منه، ولاحظ أن الخطأ يرجع برمز الحالة 200:

```javascript
  if (req.method === 'POST' && req.url === '/api/refunds/quote') {
    let raw = '';
    req.on('data', (c) => (raw += c));
    req.on('end', () => {
      try {
        const { ticket, hoursLeft, cancelled = false } = JSON.parse(raw);
        const now = Date.now();
        const percent = refundPercent(ticket, now + hoursLeft * 3600 * 1000, now, cancelled);
        json(res, 200, { ticket, hoursLeft, cancelled, percent });
      } catch (err) {
        json(res, 200, { error: err.message });
      }
    });
    return;
  }
```

وفي الصفحة نفسها نص إرشادي بلون رمادي فاتح `#b9b9b9`، وقائمة اختيار نوع التذكرة وحقل الساعات دون عنصر `label`، والحقل له نص `placeholder` فقط:

```html
  <p class="hint">Enter how many hours are left before the event.</p>
  <form id="quote">
    <select id="ticket"><option>standard</option><option>vip</option><option>promo</option></select>
    <input id="hours" type="number" placeholder="Hours left">
    <button type="submit">Quote</button>
  </form>
```

ونشغل التطبيق بملف `compose.yaml` على Docker Image الرسمية لـ Node.js دون بناء:

```yaml
name: lab-qa
services:
  app:
    image: node:24-alpine
    container_name: lab-qa-app
    working_dir: /app
    volumes:
      - ./app:/app:ro
    command: ["node", "server.js"]
    ports:
      - "127.0.0.1:19380:3000"
networks:
  default:
    name: lab-qa-net
```

```bash
docker compose up -d
curl -s -XPOST 127.0.0.1:19380/api/refunds/quote -d '{"ticket":"vip","hoursLeft":10}'
```

```text
{"ticket":"vip","hoursLeft":10,"cancelled":false,"percent":50}
```

### الأداة الأولى: أداة Bruno تختبر ال API

كل طلب في ملف، وهذا الملف `unknown-ticket.bru` الذي يرسل نوع تذكرة غير موجود، ويتوقع الرمز 400 ورسالة واضحة:

```text
meta {
  name: Unknown ticket type is rejected
  type: http
  seq: 3
}

post {
  url: {{baseUrl}}/api/refunds/quote
  body: json
  auth: none
}

body:json {
  { "ticket": "gold", "hoursLeft": 10 }
}

assert {
  res.status: eq 400
  res.body.error: contains unknown ticket type
}
```

والملفان الآخران بالشكل نفسه، الأول لتذكرة مميزة بقي لها 10 ساعات ويتوقع 50%، والثاني لتذكرة ترويجية في فعالية ملغاة ويتوقع 100%، والمتغير `baseUrl` في الملف `environments/local.bru` قيمته `http://lab-qa-app:3000`. نشغل المجموعة بأداة سطر الأوامر:

```bash
docker run --rm --name lab-qa-bruno --network lab-qa-net \
  -v "$PWD/bruno/refunds:/refunds" -w /refunds node:24-alpine \
  sh -c "npm i -g @usebruno/cli@4.2.1 >/dev/null 2>&1; bru run --env local"
```

```text
vip-under-24h (200 OK) - 24 ms
Assertions
   ✓ res.status: eq 200
   ✓ res.body.percent: eq 50
cancelled-promo (200 OK) - 4 ms
Assertions
   ✓ res.status: eq 200
   ✓ res.body.percent: eq 100
unknown-ticket (200 OK) - 3 ms
Assertions
   ✕ res.status: eq 400
      expected 200 to equal 400
   ✓ res.body.error: contains unknown ticket type

📊 Execution Summary
┌───────────────┬────────────────────────┐
│ Metric        │         Result         │
├───────────────┼────────────────────────┤
│ Status        │         ✗ FAIL         │
├───────────────┼────────────────────────┤
│ Requests      │ 3 (2 Passed, 1 Failed) │
├───────────────┼────────────────────────┤
│ Tests         │          0/0           │
├───────────────┼────────────────────────┤
│ Assertions    │          5/6           │
├───────────────┼────────────────────────┤
│ Duration (ms) │          263           │
└───────────────┴────────────────────────┘
```

الرسالة صحيحة، ولكن الرمز 200 يعني لأي برنامج يستدعي ال API أن الطلب نجح، فالتطبيق الذي يقرأ `percent` سوف يجده فارغاً. وهذا الخطأ لا تراه اختبارات الوحدة لأن الدالة نفسها ترمي الاستثناء بشكل صحيح، ولا يراه من يجرب الصفحة لأنها تعرض الرسالة. بعد تغيير الرمز في `server.js` إلى 400 وإعادة تشغيل التطبيق، وهذا آخر المخرج:

```text
unknown-ticket (400 Bad Request) - 5 ms
Assertions
   ✓ res.status: eq 400
   ✓ res.body.error: contains unknown ticket type

📊 Execution Summary
┌───────────────┬──────────────┐
│ Metric        │    Result    │
├───────────────┼──────────────┤
│ Status        │    ✓ PASS    │
├───────────────┼──────────────┤
│ Requests      │ 3 (3 Passed) │
├───────────────┼──────────────┤
│ Tests         │     0/0      │
├───────────────┼──────────────┤
│ Assertions    │     6/6      │
├───────────────┼──────────────┤
│ Duration (ms) │     249      │
└───────────────┴──────────────┘
```

### الأداة الثانية: أداة Cucumber تشغل القواعد كأمثلة

الملف `features/refund.feature` يكتب القواعد بلغة يقرؤها مالك المنتج، وجدول الأمثلة Examples فيه القيم الحدية Boundary Values من المقال السابق، فالقيمة 71.99 ساعة هي 71 ساعة و59 دقيقة تقريباً:

```gherkin
Feature: Ticket refund quote
  The customer asks how much of the ticket price comes back
  before deciding to cancel.

  Scenario Outline: Refund depends on the ticket type and the time left
    Given a "<ticket>" ticket
    And the event starts in <hours> hours
    When the customer asks for a refund quote
    Then the refund is <percent>%

    Examples:
      | ticket   | hours | percent |
      | standard | 72    | 100     |
      | standard | 71.99 | 50      |
      | standard | 23.99 | 0       |
      | vip      | 24    | 100     |
      | vip      | 1     | 50      |

  Scenario: The organiser cancels the event
    Given a "promo" ticket
    And the event starts in 5 hours
    And the organiser has cancelled the event
    When the customer asks for a refund quote
    Then the refund is 100%
```

وكل سطر مربوط بدالة في `features/step_definitions/steps.mjs`، والدوال ترسل طلباً حقيقياً إلى ال API:

```javascript
import assert from 'node:assert/strict';
import { Given, When, Then } from '@cucumber/cucumber';

const BASE = process.env.BASE_URL ?? 'http://lab-qa-app:3000';

Given('a {string} ticket', function (ticket) {
  this.request = { ticket, cancelled: false };
});

Given('the event starts in {float} hours', function (hours) {
  this.request.hoursLeft = hours;
});

Given('the organiser has cancelled the event', function () {
  this.request.cancelled = true;
});

When('the customer asks for a refund quote', async function () {
  const res = await fetch(`${BASE}/api/refunds/quote`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify(this.request),
  });
  this.response = await res.json();
});

Then('the refund is {int}%', function (percent) {
  assert.equal(this.response.percent, percent);
});
```

```bash
docker run --rm --name lab-qa-bdd --network lab-qa-net -v "$PWD/bdd:/bdd" -w /bdd node:24-alpine \
  sh -c "npm install --silent >/dev/null 2>&1; npx cucumber-js --import 'features/**/*.mjs' --format progress"
```

```text
.........................

6 scenarios (6 passed)
25 steps (25 passed)
0m 0.202s (0m 0.148s executing your code)
```

والآن نكسرها: لنفرض أن مالك المنتج أضاف قاعدة جديدة في ملف `gift-card.feature` قبل أن يكتب المطور كودها:

```gherkin
Feature: Gift card refunds

  Scenario: A ticket paid with a gift card
    Given a "standard" ticket
    And the event starts in 100 hours
    And the customer paid with a gift card
    When the customer asks for a refund quote
    Then the refund goes back to the gift card
```

```text
..U-U

Undefined scenarios:
  1) A ticket paid with a gift card # features/gift-card.feature:3
       And the customer paid with a gift card
       Then the refund goes back to the gift card

1 scenario (1 undefined)
5 steps (2 passed, 1 skipped, 2 undefined)
0m 0.043s (0m 0.001s executing your code)

You can implement missing steps with the snippets below:

Given('the customer paid with a gift card', function () {
  // Write code here that turns the phrase above into concrete actions
  return 'pending';
});

Then('the refund goes back to the gift card', function () {
  // Write code here that turns the phrase above into concrete actions
  return 'pending';
});
```

الأداة لا تفشل بصمت، وإنما تقول إن سطرين لا يعرف أحد معناهما، وتكتب للمطور هيكل الدالتين، ورمز الخروج 1 يوقف خط CI، وبهذا تصبح القاعدة الجديدة مهمة مفتوحة لا يمكن نسيانها.

### الأداة الثالثة: أداة axe تفحص سهولة الوصول Accessibility

نشغل axe من داخل اختبار Playwright، ونطلب قواعد WCAG 2.0 و2.1 بمستويي A وAA، والاختبار يطبع كل مشكلة والعنصر الذي فيه:

```typescript
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('refund page has no WCAG A/AA violations', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
    .analyze();

  for (const v of results.violations) {
    console.log(`${v.id} [${v.impact}] ${v.help}`);
    for (const node of v.nodes) console.log(`   ${node.target.join(' ')}`);
  }
  expect(results.violations.map((v) => v.id)).toEqual([]);
});
```

الملف `package.json` يثبت `@playwright/test` بالإصدار 1.64.0 و`@axe-core/playwright` بالإصدار 4.13.0، ونشغل الاختبار في Docker Image الرسمية لـ Playwright بالإصدار نفسه:

```bash
docker run --rm --name lab-qa-a11y --init --ipc=host --network lab-qa-net \
  -v "$PWD/a11y:/a11y" -w /a11y mcr.microsoft.com/playwright:v1.64.0-noble \
  sh -c "npm install --silent >/dev/null 2>&1; npx playwright test"
```

```text
Running 1 test using 1 worker

color-contrast [serious] Elements must meet minimum color contrast ratio thresholds
   .hint
select-name [critical] Select element must have an accessible name
   #ticket
  ✘  1 tests/a11y.spec.ts:4:5 › refund page has no WCAG A/AA violations (737ms)

  1) tests/a11y.spec.ts:4:5 › refund page has no WCAG A/AA violations ──────────────────────────────

    Error: expect(received).toEqual(expected) // deep equality

    - Expected  - 1
    + Received  + 4

    - Array []
    + Array [
    +   "color-contrast",
    +   "select-name",
    + ]
```

في المخرج أعلاه لاحظ التالي:

- النص الرمادي تباينه مع الخلفية البيضاء 1.96 كما ذكرت axe في التفاصيل الكاملة، والمطلوب في WCAG بمستوى AA هو 4.5 للنص العادي.
- قائمة نوع التذكرة بلا اسم، فقارئ الشاشة Screen Reader سوف يقول «قائمة» فقط، ولا يعرف المستخدم الكفيف ماذا يختار.
- حقل الساعات **لم يظهر** في النتائج رغم أنه بلا `label`، والسبب أن axe يقبل نص `placeholder` كاسم للحقل، مع أن هذا النص يختفي بمجرد أن يبدأ المستخدم الكتابة. وهذا مثال على ما قلناه أعلاه: الأداة الآلية تجد جزءاً من المشاكل، والباقي يحتاج مراجعة إنسان.

بعد إضافة عنصري `label` للقائمة والحقل، وتغيير اللون إلى `#595959`:

```text
Running 1 test using 1 worker

  ✓  1 tests/a11y.spec.ts:4:5 › refund page has no WCAG A/AA violations (1.4s)

  1 passed (3.5s)
```

### الأداة الرابعة: أداة Stryker تختبر الاختبارات

في `app/test/refund.test.js` كتبنا بأداة Vitest نفس مجموعة الفئات المتكافئة Equivalence Partitions من المقال السابق، أي قيمة واحدة من كل فئة مع اختبار الإلغاء والنوع غير الصالح، وعشرة اختبارات ناجحة. وملف الإعداد `stryker.config.json` يحدد الملف الذي نغيره:

```json
{
  "testRunner": "vitest",
  "mutate": ["refund.js"],
  "reporters": ["clear-text", "progress"],
  "coverageAnalysis": "perTest"
}
```

```bash
docker run --rm --name lab-qa-mutation -v "$PWD/app:/app" -w /app node:24-alpine \
  sh -c "npm install --silent >/dev/null 2>&1; npx stryker run --reporters clear-text"
```

وهنا واجهنا مشكلة تستحق أن تعرفها: مع Vitest بالإصدار 5.0.4 وStryker بالإصدار 10.0.0 خرجت النتيجة 12.20%، وفي قائمة الطفرات الناجية طفرات واضح أنها يجب أن تموت، مثل تغيير `if (left >= 24 * HOUR) return 50;` إلى `if (true) return 50;` مع أن اختبار العشر ساعات يتوقع 0%. والإصدار 5.0.0 من Vitest صدر في 3 سبتمبر 2026، أي بعد Stryker 10.0.0 الذي صدر في 14 أغسطس، فعدنا إلى Vitest 4.1.11 وخرجت النتيجة الصحيحة:

```text
[Survived] StringLiteral
refund.js:6:52
-     if (!TYPES.includes(ticketType)) throw new Error(`unknown ticket type: ${ticketType}`);
+     if (!TYPES.includes(ticketType)) throw new Error(``);
--
[Survived] EqualityOperator
refund.js:10:7
-     if (left <= 0) return 0;
+     if (left < 0) return 0;
--
[Survived] EqualityOperator
refund.js:11:36
-     if (ticketType === 'vip') return left >= 24 * HOUR ? 100 : 50;
+     if (ticketType === 'vip') return left > 24 * HOUR ? 100 : 50;
--
[Survived] EqualityOperator
refund.js:12:7
-     if (left >= 72 * HOUR) return 100;
+     if (left > 72 * HOUR) return 100;
--
[Survived] EqualityOperator
refund.js:13:7
-     if (left >= 24 * HOUR) return 50;
+     if (left > 24 * HOUR) return 50;

-----------|------------------|----------|-----------|------------|----------|----------|
           | % Mutation score |          |           |            |          |          |
File       |  total | covered | # killed | # timeout | # survived | # no cov | # errors |
-----------|--------|---------|----------|-----------|------------|----------|----------|
All files  |  87.80 |   87.80 |       36 |         0 |          5 |        0 |        0 |
 refund.js |  87.80 |   87.80 |       36 |         0 |          5 |        0 |        0 |
-----------|--------|---------|----------|-----------|------------|----------|----------|
```

أربع من الطفرات الخمس الناجية هي تغيير `>=` إلى `>` أو `<=` إلى `<`، أي أن Stryker وجد بنفسه، دون أن يعرف شيئاً عن القواعد، أن الاختبارات لا تلمس الحدود، وهي النتيجة نفسها التي وصلنا إليها في المقال السابق بتصميم القيم الحدية Boundary Values يدوياً. والطفرة الخامسة تقول إن أحداً لا يتحقق من نص رسالة الخطأ. فنضيف مجموعة ثانية:

```javascript
// Boundary values: exactly on each edge.
describe('refundPercent: boundaries', () => {
  it.each([
    ['standard', 72, 100],
    ['standard', 24, 50],
    ['vip', 24, 100],
    ['vip', 0, 0],
  ])('%s, exactly %d hours left -> %d%%', (ticket, hours, expected) => {
    expect(refundPercent(ticket, START, before(hours))).toBe(expected);
  });

  it('names the unknown ticket type in the error', () => {
    expect(() => refundPercent('gold', START, before(10))).toThrow(/unknown ticket type: gold/);
  });
});
```

```text
-----------|------------------|----------|-----------|------------|----------|----------|
           | % Mutation score |          |           |            |          |          |
File       |  total | covered | # killed | # timeout | # survived | # no cov | # errors |
-----------|--------|---------|----------|-----------|------------|----------|----------|
All files  | 100.00 |  100.00 |       41 |         0 |          0 |        0 |        0 |
 refund.js | 100.00 |  100.00 |       41 |         0 |          0 |        0 |        0 |
-----------|--------|---------|----------|-----------|------------|----------|----------|
```

ولاحظ أن تشغيل Stryker استغرق بين 43 و55 ثانية على دالة من 15 سطراً، لأنه يشغل الاختبارات مرة لكل طفرة، لذلك يناسبه أن يعمل على الملفات المهمة أو على الكود الجديد في طلب الدمج، وليس على المستودع كله في كل Commit.

### ماذا وجدت كل أداة؟

| الأداة        | ما وجدته                                  | ما لا تستطيع أن تراه                                 |
| ------------- | ----------------------------------------- | ---------------------------------------------------- |
| أداة Bruno    | رمز الحالة 200 لطلب خطأ                   | الصفحة ومنطق الدالة الداخلي                          |
| أداة Cucumber | نجحت القواعد، ثم كشفت قاعدة جديدة بلا كود | أي قاعدة لم يكتبها أحد في ملف                        |
| أداة axe      | ضعف التباين وقائمة بلا اسم                | حقل له placeholder بلا label، وكل ما يحتاج حكم إنسان |
| أداة Stryker  | اختبارات لا تلمس الحدود ولا رسالة الخطأ   | ال API والصفحة، فهو يقيس الاختبارات وليس التطبيق     |

وفي النهاية نحذف ما أنشأناه فقط: `docker compose down` يحذف `lab-qa-app` والشبكة، والأدوات الأخرى عملت بالخيار `--rm`، ثم نحذف مجلدات `node_modules`.

## كيف تختار؟ جدول قرار حسب نوع فريقك

| نوع الفريق                        | ابدأ بهذا                                                                               | أضف لاحقاً عند الحاجة                                                                   |
| --------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| مطور واحد أو فريق صغير بلا مختبر  | إطار الوحدة للغتك، وBruno، وPlaywright لمسارين مهمين، وكلها في CI                       | فحص axe داخل اختبارات Playwright نفسها                                                  |
| فريق فيه مختبرون يدويون           | ما سبق، مع Kiwi TCMS على سيرفرك للحالات اليدوية والجولات                                | أداة Cucumber إذا كان مالك المنتج سيقرأ الأمثلة فعلاً                                   |
| شركة كبيرة أو قطاع منظم يطلب أدلة | أداة إدارة تحفظ التاريخ وتربط الحالات بالمتطلبات، مثل TestRail أو Kiwi TCMS باشتراك دعم | تقارير سهولة الوصول Accessibility، واختبار الطفرات Mutation Testing على الوحدات الحساسة |
| خدمات كثيرة تملكها فرق مختلفة     | اختبارات API في CI لكل خدمة، وPact مع Pact Broker على سيرفرك                            | أداة k6 لاختبار الأحمال على المسارات المشتركة                                           |
| تطبيق جوال                        | أداة Maestro لأنها أسهل بداية، أو أطر المنصة إذا كان التطبيق أصلياً لمنصة واحدة         | أداة Appium عندما تحتاج لغة برمجة كاملة أو تكاملاً مع Selenium Grid                     |
| مشروع مفتوح المصدر                | أدوات مفتوحة فقط تعمل في CI عام: إطار الوحدة وPlaywright وBruno                         | الخطط المجانية من Percy أو Chromatic للصور                                              |

وإذا كان عندك قيد على مكان البيانات، كجهة حكومية أو عميل يمنع خروج الكود والطلبات، فاستبعد من البداية كل أداة تحفظ الطلبات أو الحالات في سحابة الشركة، وابق على ما يعمل من مستودعك أو على سيرفرك.

## أخطاء شائعة

- **شراء أداة إدارة قبل وجود حالات تديرها:** ابدأ بالحالات والأتمتة، وأضف الإدارة عندما يصبح السؤال «ماذا اختبرنا؟» صعب الإجابة.
- **مقارنة أدوات لا تتنافس:** أداة Postman لا تغني عن Playwright، وأداة TestRail لا تختبر شيئاً.
- **الثقة بنتيجة أداة بعد تحديث مكتبة:** كما رأيت مع Stryker وVitest 5، فإذا تغيرت النتيجة فجأة بعد تحديث فتحقق من توافق الإصدارات قبل أن تصدق الرقم.
- **اعتبار نجاح axe دليلاً على سهولة الوصول Accessibility:** هو يجد جزءاً من المشاكل، والحقل بلا `label` مثال على ما يفوته.
- **ملفات Gherkin لا يقرؤها أحد:** إذا كان المطور وحده يكتب ويقرأ ملفات Cucumber فاكتب اختبارات عادية واختصر الطبقة.
- **الأسعار من مقالات قديمة:** خطط Postman تغيرت، وأدوات المرئيات المفتوحة توقفت، فراجع صفحة الشركة يوم القرار.
- **إصدارات غير مثبتة:** ثبت إصدار كل أداة في CI كما فعلنا في التجربة، حتى لا يتغير السلوك دون أن يتغير الكود.

## أين تكمل بعد هذه الخريطة؟

لكل فئة في الخريطة مقال عملي في مرحلة ضمان الجودة QA يشرح أداة واحدة منها بالتفصيل مع تجربة حقيقية:

- [إدارة حالات الاختبار Test Management مع Kiwi TCMS](https://arabroot.io/articles/%D8%A7%D8%AF%D8%A7%D8%B1%D8%A9-%D8%AD%D8%A7%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D9%85%D8%B9-kiwi-tcms/)
- [اختبارات الواجهة E2E مع Playwright](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-e2e-%D9%85%D8%B9-playwright-%D8%A8%D8%A7%D9%84%D8%AA%D9%81%D8%B5%D9%8A%D9%84/)
- [اختبار ال API مع Bruno وPostman](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-api-%D9%85%D8%B9-bruno-%D9%88-postman/)
- [اختبار العقود Contract Testing مع Pact](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%82%D9%88%D8%AF-contract-testing-%D9%85%D8%B9-pact/)
- [اختبار الطفرات Mutation Testing](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%B7%D9%81%D8%B1%D8%A7%D8%AA-mutation-testing/)
- [اختبار سهولة الوصول Accessibility لموقع عربي](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D9%84%D9%85%D9%88%D9%82%D8%B9-%D8%B9%D8%B1%D8%A8%D9%8A/)
- [اختبار تطبيقات الجوال مع Appium وMaestro](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-%D9%85%D8%B9-appium-%D9%88-maestro/)

## الخلاصة

- الأدوات عائلات تختبر طبقات مختلفة، والفريق الصغير يحتاج ثلاثاً أو أربعاً منها فقط: الوحدة وال API والمتصفح وCI.
- أغلب أدوات الاختبار نفسها مفتوحة المصدر ومجانية، والتكلفة في الخدمات السحابية حولها: Cypress Cloud وPostman Team وGrafana Cloud k6 وPercy وPactFlow، ثم في أدوات الإدارة التي تحسب لكل مستخدم.
- أداة Bruno تحفظ طلبات ال API كملفات في مستودعك، وPostman في سحابته، وخطط Postman القديمة لم تعد متاحة للعملاء الجدد.
- أداة Kiwi TCMS هي الخيار المفتوح النشط لإدارة الحالات على سيرفرك، وTestLink شبه متوقف، وQase وTestRail سحابيان بسعر لكل مستخدم.
- في التجربة وجد Bruno رمز حالة خاطئاً في ال API، ووجد axe مشكلتين في الصفحة وفاته الحقل بلا `label`، وكشف Cucumber قاعدة بلا كود، ووجد Stryker أن الاختبارات لا تلمس الحدود فارتفعت الدرجة من 87.80% إلى 100% بعد إضافة القيم الحدية Boundary Values.
- تحقق من توافق الإصدارات قبل أن تصدق رقماً غريباً، فقد أعطى Stryker 12.20% خاطئة مع Vitest 5.0.4.

وفي مقالات قادمة من هذا القسم بإذن الله سوف نتعمق في كل عائلة، من إدارة حالات الاختبار Test Management على سيرفرك إلى اختبار المتصفح وال API والعقود والطفرات وسهولة الوصول Accessibility والجوال. وهذه مقالات مرتبطة بالموضوع:

[![](https://arabroot.io/content/images/2026/10/qa-intro-cover-cover.webp)دليلما هو ضمان الجودة QA وكيف يعمل فريق الاختبار؟كيف تفرق بين QA وQC والاختبار، وتصمم حالات الاختبار Test Cases التي تجد الأخطاء قبل أن يجدها عملاؤك.](https://arabroot.io/articles/%D9%85%D8%A7-%D9%87%D9%88-%D8%B6%D9%85%D8%A7%D9%86-%D8%A7%D9%84%D8%AC%D9%88%D8%AF%D8%A9-qa-%D9%88%D9%83%D9%8A%D9%81-%D9%8A%D8%B9%D9%85%D9%84-%D9%81%D8%B1%D9%8A%D9%82-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1/)[![](https://arabroot.io/content/images/2026/10/testing-types-cover-cover.webp)دليلأنواع الاختبارات للمبتدئين: من Unit إلى E2E مع Playwrightما الذي يكشفه كل نوع من الاختبارات وما الذي يفوته، وكم اختباراً تكتب من كل نوع، وأين يعمل كل منها في خط CI.](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-unit-%D9%88-integration-%D9%88-e2e/)[![](https://arabroot.io/content/images/2026/10/ai-testing-cover-cover.webp)دليلاختبارات يكتبها الذكاء الاصطناعي: متى تثق بها؟أين يساعدك الذكاء الاصطناعي في كتابة الاختبارات وأين يخدعك، ولماذا يبقى قرار ما هو الصحيح بيد الإنسان.](https://arabroot.io/articles/%D8%A7%D8%AA%D9%85%D8%AA%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/)[![](https://arabroot.io/content/images/2026/10/load-testing-cover-cover.webp)دليلاختبار الأحمال والضغط Load Testing للمبتدئينلماذا يعمل تطبيقك مع خمسة مستخدمين وينهار مع خمسمئة، وكيف تكتشف ذلك وتصلحه قبل يوم الإطلاق.](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D8%AD%D9%85%D8%A7%D9%84-%D9%88%D8%A7%D9%84%D8%B6%D8%BA%D8%B7-load-%D9%88-stress-testing-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)[![](https://arabroot.io/content/images/2026/10/dast-cover-cover.webp)دليلDAST: فحص تطبيقك من الخارج وهو يعملكيف يفحص ZAP تطبيقك من الخارج وهو يعمل، ومتى يحق لك أن تشغله، وكيف تحول نتائجه إلى بوابة في خط CI.](https://arabroot.io/articles/%D9%81%D8%AD%D8%B5-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D8%AB%D9%86%D8%A7%D8%A1-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-dast-%D9%85%D8%B9-zap/)[![](https://arabroot.io/content/images/2026/10/sonarqube-cover-cover.webp)دليلSonarQube عملياً: بوابة جودة لكود الذكاء الاصطناعيكيف تثبت SonarQube مع PostgreSQL وتضبط قواعده وبوابته، ثم تجعل الوكيل الذكي يقرأ نتيجتها ويصلح كوده حتى تنجح.](https://arabroot.io/articles/sonarqube-%D8%B9%D9%85%D9%84%D9%8A%D8%A7-%D8%A8%D9%88%D8%A7%D8%A8%D8%A9-%D8%AC%D9%88%D8%AF%D8%A9-%D9%84%D9%83%D9%88%D8%AF-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/)

## سجل التحديثات

- أكتوبر 2026: كتابة المقال اعتماداً على مستودعات الأدوات وصفحات الأسعار الرسمية كما هي في 12 أكتوبر 2026، مع تجربة حقيقية على Node.js 24.21.0 باستخدام Bruno CLI 4.2.1 وCucumber 13.3.0 وPlaywright 1.64.0 مع @axe-core/playwright 4.13.0 وaxe-core 4.13.0 وStryker 10.0.0 مع Vitest 4.1.11.
- أكتوبر 2026: إضافة روابط المقالات العملية لكل فئة من أدوات الاختبار.