في هذه المقالة ، سنتحدث عن المشكلات التي تحلها العقود التي يحركها المستهلك ، ونعرض كيفية تطبيقها باستخدام مثال Pact مع Node.js و Spring Boot. وتحدث عن حدود هذا النهج.
مشاكل
عند اختبار المنتجات ، غالبًا ما تستخدم اختبارات السيناريو التي يتم فيها التحقق من تكامل المكونات المختلفة للنظام في بيئة محددة بشكل خاص. مثل هذه الاختبارات على الخدمات الحية تعطي النتيجة الأكثر موثوقية (وليس عد الاختبارات في المعركة). ولكن في الوقت نفسه ، فهي واحدة من أغلى.
- غالبًا ما يُعتقد خطأً أن بيئة التكامل يجب ألا تكون متسامحة مع الخطأ. نادراً ما يتم التحدث عن ضمانات جيش تحرير السودان ، مثل هذه البيئات ، ولكن إذا لم تكن متوفرة ، يتعين على الفرق إما تأخير النشرات أو الأمل في الأفضل والخوض في معركة دون اختبارات. على الرغم من أن الجميع يعلم أن الأمل ليس استراتيجية . وتقنيات البنية التحتية الجديدة أحدثت تعقيد العمل مع بيئات التكامل.
- ألم آخر يعمل مع بيانات الاختبار . تتطلب العديد من السيناريوهات حالة معينة من النظام والتركيبات. ما مدى قربهم من مكافحة البيانات؟ كيف يتم تحديثها قبل الاختبار وتنظيفها بعد الانتهاء؟
- الاختبارات غير مستقرة للغاية . وليس فقط بسبب البنية التحتية التي ذكرناها في الفقرة الأولى. قد يفشل الاختبار لأن فريقًا مجاورًا قد بدأ عمليات الفحص الخاصة به التي كسرت الحالة المتوقعة للنظام! العديد من الفحوصات السلبية الخاطئة والاختبارات
@Ignored
تنهي حياتهم في @Ignored
أيضا ، قد يتم دعم أجزاء مختلفة من التكامل من قبل فرق مختلفة. لقد طرحوا مرشح إصدار جديد به أخطاء - لقد كسروا جميع المستهلكين. شخص يحل هذه المشكلة مع حلقات اختبار مخصصة. ولكن على حساب ضرب تكلفة الدعم. - مثل هذه الاختبارات تستغرق الكثير من الوقت . حتى مع وضع الأتمتة في الاعتبار ، يمكن توقع النتائج لساعات.
- وللتغلب على ذلك ، إذا انخفض الاختبار حقًا ، فمن غير الممكن دائمًا العثور على سبب المشكلة على الفور. يمكن أن تختبئ عميق وراء طبقات التكامل. أو قد يكون نتيجة لمجموعة غير متوقعة من حالات العديد من مكونات النظام.
تتطلب الاختبارات المستقرة في بيئة تكامل استثمارًا كبيرًا من QA و dev وحتى ops. لا عجب أنهم في قمة
هرم الاختبار . هذه الاختبارات مفيدة ، لكن اقتصاد الموارد لا يسمح لهم بالتحقق من كل شيء. المصدر الرئيسي لقيمة هذه البيئة.
يوجد أسفل الهرم نفسه اختبارات أخرى نتبادل فيها الثقة لصداع دعم أصغر - باستخدام اختبارات العزل. الحبيبية ، أصغر حجم الاختبار ، وأقل الاعتماد على البيئة الخارجية. في أسفل الهرم توجد اختبارات وحدة. نتحقق من الوظائف الفردية ، والطبقات ، ونحن لا نعمل كثيرا مع دلالات الأعمال كما هو الحال مع المنشآت لتنفيذ معين. هذه الاختبارات تعطي ردود فعل سريعة.
ولكن بمجرد أن نسقط الهرم ، يتعين علينا استبدال البيئة بشيء ما. تظهر الدعامات - كخدمات كاملة ، وكيانات فردية في لغة البرمجة. وبمساعدة المقابس ، يمكننا اختبار المكونات بمعزل عن غيرها. لكنها تقلل أيضا من صحة الشيكات. كيفية التأكد من أن كعب الروتين يقوم بإرجاع البيانات الصحيحة؟ كيفية ضمان جودتها؟
يمكن أن يكون الحل وثائق شاملة تصف سيناريوهات متعددة والحالات المحتملة لمكونات النظام. لكن أي تركيبات لا تزال تترك حرية التفسير. لذلك ، فإن التوثيق الجيد هو قطعة أثرية حية تتحسن باستمرار حيث أن الفريق يدرك مجال المشكلة. كيف بعد ذلك لضمان الامتثال مع وثائق الرذيلة؟
في العديد من المشاريع ، يمكنك ملاحظة موقف حيث تتم كتابة الروايات بواسطة نفس الأشخاص الذين طوروا قطعة أثر الاختبار. على سبيل المثال ، يصنع مطورو تطبيقات الأجهزة المحمولة بذرة في اختباراتهم بأنفسهم. نتيجةً لذلك ، يمكن للمبرمجين فهم الوثائق بطريقتهم الخاصة (وهو أمر طبيعي تمامًا) ، فهم يصنعون كعب الروتين بالسلوك المتوقع الخاطئ ، ويكتبون الكود وفقًا له (مع الاختبارات الخضراء) ، وتحدث الأخطاء أثناء التكامل الحقيقي.
علاوة على ذلك ، عادةً ما تنتقل الوثائق إلى المصب - يستخدم العملاء مواصفات الخدمات (في هذه الحالة ، يمكن أن تكون خدمة أخرى عميلًا للخدمة). لا تعبر عن
كيفية استخدام المستهلكين للبيانات ، وما هي البيانات المطلوبة على الإطلاق ، وما هي الافتراضات التي يقدمونها لتلك البيانات. نتيجة هذا الجهل هو
قانون Hyrum .
يعمل Hyrum Wright على تطوير أدوات عامة داخل Google لفترة طويلة ولاحظ كيف أن أصغر التغييرات يمكن أن تسبب أعطالًا للعملاء الذين استخدموا الميزات الضمنية (غير الموثقة) في مكتباته. مثل هذه الاتصالات الخفية تعقد تطور API.
يمكن حل هذه المشكلات إلى حد ما باستخدام عقود يحركها المستهلك. مثل أي نهج وأداة ، فإنه يحتوي على مجموعة من قابلية التطبيق والتكلفة ، والتي سننظر فيها أيضًا. وصلت تطبيقات هذا النهج إلى مستوى كافٍ من النضج لمحاولة تنفيذ مشاريعهم.
ما هو مركز السيطرة على الأمراض؟
ثلاثة عناصر رئيسية:
- العقد . موصوف باستخدام بعض DSL ، يعتمد التنفيذ. يحتوي على وصف لواجهة برمجة التطبيقات (API) في شكل سيناريوهات تفاعل: إذا وصل طلب معين ، فيجب أن يتلقى العميل استجابة محددة.
- اختبارات العملاء . علاوة على ذلك ، يستخدمون كعب ، والذي يتم إنشاؤه تلقائيًا من العقد.
- اختبارات ل API . يتم إنشاؤها أيضا من العقد.
وبالتالي ، فإن العقد قابل للتنفيذ. والميزة الرئيسية لهذا النهج هي أن متطلبات سلوك واجهة برمجة التطبيقات (API) تتجه إلى الأعلى ، من العميل إلى الخادم.
يركز العقد على السلوك الذي يهم المستهلك
حقًا . يجعل افتراضاته حول API صريحة.
الهدف الرئيسي من مركز السيطرة على الأمراض هو تحقيق فهم لسلوك API لمطوريها ومطوري عملائها. يتم دمج هذا النهج بشكل جيد مع BDD ، في
اجتماعات ثلاثة أميغو يمكنك رسم الفراغات للعقد. في النهاية ، يعمل هذا العقد أيضًا على تحسين الاتصالات ؛ تبادل الفهم المشترك لمنطقة المشكلة وتنفيذ الحل داخل وبين الفرق.
اتفاق
فكر في استخدام CDC كمثال من Pact ، أحد تطبيقاته. لنفترض أننا نقوم بإنشاء تطبيق ويب للمشاركين في المؤتمر. في التكرار التالي ، يطور الفريق جدول عرض تقديمي - حتى الآن دون أي قصص مثل التصويت أو الملاحظات ، فقط إخراج شبكة التقارير. الكود المصدري للمثال موجود
هنا .
في اجتماع من
ثلاثة أربعة أميغو ، يجتمع منتج ، واختبار ، ومطورو الواجهة الخلفية وتطبيقات الهاتف المحمول. يقولون ذلك
- سيتم عرض قائمة بالنص في واجهة المستخدم: عنوان التقرير + المتحدثون + التاريخ والوقت.
- للقيام بذلك ، يجب على الواجهة الخلفية إرجاع البيانات كما في المثال أدناه.
{ "talks":[ { "title":" ", "speakers":[ { "name":" " } ], "time":"2019-05-27T12:00:00+03:00" } ] }
بعد ذلك يذهب مطور الواجهة الأمامية لكتابة رمز العميل (الواجهة الخلفية للواجهة الأمامية). قام بتثبيت مكتبة عقود الاتفاق في المشروع:
yarn add --dev @pact-foundation/pact
ويبدأ في كتابة اختبار. يقوم بتكوين خادم كعب الروتين المحلي ، والذي سيحاكي الخدمة بجداول التقارير:
const provider = new Pact({
العقد عبارة عن ملف JSON يصف سيناريوهات العميل التي تتفاعل مع الخدمة. لكنك لست بحاجة إلى وصفه يدويًا ، حيث يتم تشكيله من إعدادات كعب الروتين في الكود. يصف المطور قبل الاختبار السلوك التالي.
provider.setup().then(() => provider .addInteraction({ uponReceiving: "a request for schedule", withRequest: { method: "GET", path: "/schedule" }, willRespondWith: { status: 200, headers: { "Content-Type": "application/json;charset=UTF-8" }, body: { talks: [ { title: " ", speakers: [ { name: " " } ], time: "2019-05-27T12:00:00+03:00" } ] } } }) .then(() => done()) );
هنا ، في المثال ، حددنا طلب الخدمة المتوقع المحدد ، لكن pact-js تدعم أيضًا
عدة طرق لتحديد التطابقات .
أخيرًا ، يكتب المبرمج اختبارًا لهذا الجزء من التعليمات البرمجية الذي يستخدم هذا كعب الروتين. في المثال التالي ، سوف نسميها مباشرة للبساطة.
it("fetches schedule", done => { fetch(`http://localhost:${pactServerPort}/schedule`) .then(response => response.json()) .then(json => expect(json).toStrictEqual({ talks: [ { title: " ", speakers: [ { name: " " } ], time: "2019-05-27T12:00:00+03:00" } ] })) .then(() => done()); });
في مشروع حقيقي ، يمكن أن يكون هذا إما اختبارًا سريعًا للوحدة لوظيفة تفسير استجابة منفصلة أو اختبار واجهة مستخدم بطيئًا لعرض البيانات المستلمة من الخدمة.
أثناء التشغيل التجريبي ، يتحقق pact من استلام كعب الروتين للطلب المحدد في الاختبارات. يمكن اعتبار التناقضات فرقًا في ملف pact.log.
E, [2019-05-21T01:01:55.810194 #78394] ERROR -- : Diff with interaction: "a request for schedule" Diff -------------------------------------- Key: - is expected + is actual Matching keys and values are not shown { "headers": { - "Accept": "application/json" + "Accept": "*/*" } } Description of differences -------------------------------------- * Expected "application/json" but got "*/*" at $.headers.Accept
إذا نجح الاختبار ، يتم إنشاء العقد بتنسيق JSON. وهو يصف السلوك المتوقع لواجهة برمجة التطبيقات.
{ "consumer": { "name": "schedule-consumer" }, "provider": { "name": "schedule-producer" }, "interactions": [ { "description": "a request for schedule", "request": { "method": "GET", "path": "/schedule", "headers": { "Accept": "application/json" } }, "response": { "status": 200, "headers": { "Content-Type": "application/json;charset=UTF-8" }, "body": { "talks":[ { "title":" ", "speakers":[ { "name":" " } ], "time":"2019-05-27T12:00:00+03:00" } ] }}} ], "metadata": { "pactSpecification": { "version": "2.0.0" } } }
انه يعطي هذا العقد لمطور الخلفية. دعنا نقول أن API موجود على Spring Boot. يحتوي Pact على مكتبة
pact-jvm-provider-spring يمكنها العمل مع MockMVC. لكننا سنلقي نظرة على عقد Spring Cloud ، الذي ينفذ CDC في نظام Spring. يستخدم تنسيق العقود الخاص به ، ولكن لديه أيضًا نقطة امتداد لتوصيل المحولات من التنسيقات الأخرى. لا يتم دعم تنسيق العقد الأصلي إلا من خلال Spring Cloud Contract نفسها - على عكس Pact ، التي تحتوي على مكتبات لـ JVM و Ruby و JS و Go و Python وما إلى ذلك.
لنفترض ، في مثالنا ، أن مطور الواجهة الخلفية يستخدم Gradle لإنشاء الخدمة. يربط التبعيات التالية:
buildscript { // ... dependencies { classpath "org.springframework.cloud:spring-cloud-contract-pact:2.1.1.RELEASE" } } plugins { id "org.springframework.cloud.contract" version "2.1.1.RELEASE" // ... } // ... dependencies { // ... testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier' }
ويضع عقد Pact الوارد من frotender في دليل
src/test/resources/contracts
.
منه ، افتراضيًا ، يقوم المكون الإضافي للعقد الربيعي بطرح العقود. أثناء التجميع ، يتم تنفيذ مهمة gradle createContractTests ، والتي تنشئ الاختبار التالي في دليل build / born-test-sources.
public class ContractVerifierTest extends ContractsBaseTest { @Test public void validate_aggregator_client_aggregator_service() throws Exception {
عند بدء هذا الاختبار ، سنرى خطأ:
java.lang.IllegalStateException: You haven't configured a MockMVC instance. You can do this statically
نظرًا لأنه يمكننا استخدام أدوات مختلفة للاختبار ، نحتاج إلى معرفة المكون الإضافي الذي قمنا بتكوينه. يتم ذلك من خلال الفئة الأساسية ، والتي سوف ترث الاختبارات الناتجة عن العقود.
public abstract class ContractsBaseTest { private ScheduleController scheduleController = new ScheduleController(); @Before public void setup() { RestAssuredMockMvc.standaloneSetup(scheduleController); } }
لاستخدام هذه الفئة الأساسية أثناء التوليد ، تحتاج إلى تكوين المكون الإضافي للعقد الربيعي السحابي.
contracts { baseClassForTests = 'ru.example.schedule.ContractsBaseTest' }
الآن لدينا اختبار التالية ولدت:
public class ContractVerifierTest extends ContractsBaseTest { @Test public void validate_aggregator_client_aggregator_service() throws Exception {
يبدأ الاختبار بنجاح ، ولكنه فشل مع وجود خطأ في التحقق - لم يكتب المطور بعد تنفيذ الخدمة. لكن الآن يمكنه أن يفعل ذلك بناءً على عقد. يمكنه التأكد من أنه قادر على معالجة طلب العميل وإرجاع الاستجابة المتوقعة.
يعرف مطور الخدمة ما الذي يتعين عليه القيام به من خلال العقد ، وما السلوك الذي يجب تنفيذه.
يمكن دمج الميثاق بشكل أعمق في عملية التطوير. يمكنك نشر وسيط Pact الذي يقوم بتجميع مثل هذه العقود ، ويدعم إصدارها ، ويمكن عرض رسم بياني تبعية.

يمكن أن يتم تحميل عقد جديد تم إنشاؤه إلى الوسيط في الخطوة CI عند إنشاء العميل. وفي رمز الخادم تشير إلى التحميل الديناميكي للعقد عن طريق URL. ربيع الغيمة العقد كما يدعم هذا.
مركز السيطرة على الأمراض
ما هي حدود العقود التي يحركها المستهلك؟
لاستخدام هذا النهج ،
عليك الدفع باستخدام أدوات إضافية مثل pact. العقود في حد ذاتها هي قطعة أثرية إضافية ، تجريد آخر يجب الحفاظ عليه بعناية وتطبيق الممارسات الهندسية بوعي عليه.
فهي لا تحل محل اختبارات e2e ، حيث لا تزال الدعامات روتينًا - نماذج لمكونات النظام الحقيقي ، والتي قد تكون قليلاً ، ولكنها لا تتوافق مع الواقع. من خلالها ، لا يمكن التحقق من السيناريوهات المعقدة.
أيضا ،
لا تحل CDCs محل اختبارات API الوظيفية . أنها أكثر تكلفة لدعم من "عادي الوحدة القديمة الاختبارات". يوصي مطورو Pact باستخدام الأساليب البحثية التالية - إذا قمت بإزالة العقد وهذا لا يسبب أخطاء أو سوء تفسير من قبل العميل ، فلا داعي لذلك. على سبيل المثال ، ليس من الضروري وصف جميع رموز أخطاء واجهة برمجة التطبيقات (API) تمامًا من خلال عقد إذا قام العميل بمعالجتها بنفس الطريقة. بمعنى آخر ، يصف العقد للخدمة
فقط ما هو مهم لعميلها . لا أكثر ولا أقل.
هناك الكثير من العقود التي تؤدي أيضًا إلى تعقيد تطور واجهة برمجة التطبيقات.
كل عقد إضافي هو مناسبة للاختبارات الحمراء . من الضروري تصميم CDC بطريقة تجعل كل اختبار فاشل يحمل عبءًا مفيدًا يفوق تكلفة دعمه. على سبيل المثال ، إذا كان العقد يحدد الحد الأدنى لطول حقل نص معين غير مبال بالمستهلك (يستخدم
أسلوب قارئ Toleran ) ، فكل تغيير في هذه القيمة الدنيا سوف يؤدي إلى كسر العقد وأعصاب المحيطين به. يجب نقل هذا الفحص إلى مستوى واجهة برمجة التطبيقات (API) نفسها وتنفيذها وفقًا لمصدر القيود.
استنتاج
يحسن CDC جودة المنتج من خلال وصف سلوك التكامل بوضوح. يساعد العملاء ومطوري الخدمات على الوصول إلى تفاهم مشترك ، ويسمح لك بالتحدث عبر الكود. ولكن هذا يحدث على حساب إضافة الأدوات ، وإدخال التجريدات الجديدة والإجراءات الإضافية لأعضاء الفريق.
في الوقت نفسه ، يجري تطوير أدوات وإطارات عمل مراكز السيطرة على الأمراض بفعالية وقد وصلت بالفعل إلى مرحلة النضج لاختبار مشاريعك. اختبار :)
في مؤتمر QualityConf الذي سيعقد يومي 27 و 28 مايو ، سيتحدث Andrei Markelov عن تقنيات الاختبار على prod ، وسيتحدث Arthur Khineltsev عن مراقبة الواجهة الأمامية المحملة للغاية ، عندما يكون سعر الخطأ البسيط هو عشرات الآلاف من المستخدمين المحزنين.
تعال دردشة لجودة!