هذا دليل شامل لتوفير الموثوقية في JavaScript و Node.js. هنا يتم جمع العشرات من أفضل الوظائف والكتب والأدوات.
أولاً ، تعامل مع طرق الاختبار المقبولة عمومًا والتي تقوم عليها أي تطبيق. وبعد ذلك يمكنك الخوض في المجال الذي تهمك: الواجهة الأمامية والواجهات ، الواجهة الخلفية ، CI أو كل ما سبق.
محتوى
القسم 0. القاعدة الذهبية
0. القاعدة الذهبية: التمسك اختبار العجاف
ما يجب القيام به. يختلف رمز الاختبار عما يدخل حيز التنفيذ. اجعلها بسيطة بقدر الإمكان ، قصيرة ، خالية من التجريدات ، فردية ، رائعة في العمل وثريتي. يجب أن ينظر شخص آخر إلى الاختبار ويفهم ما يفعله على الفور.
رؤوسنا مشغولة برمز الإنتاج ، وليس لديهم مساحة حرة لتعقيد إضافي. إذا دفعنا جزءًا جديدًا من التعليمات البرمجية المعقدة إلى أذهاننا السيئة ، فسيؤدي ذلك إلى إبطاء عمل الفريق بأكمله في المهمة ، من أجل اختبارها. في الواقع ، وبسبب هذا ، العديد من الفرق ببساطة تجنب الاختبارات.
الاختبارات - هذه فرصة للحصول على مساعد ودود ومبتسم ، من الجيد للغاية معه العمل والذي يعطي عائدًا كبيرًا على الاستثمارات الصغيرة. يعتقد العلماء أنه يوجد في دماغنا نظامان: نظام للأفعال التي لا تتطلب مجهودًا ، مثل القيادة على طريق فارغ ، والثاني للعمليات المعقدة التي تتطلب الوعي ، مثل حل المعادلات الرياضية. قم بإنشاء اختباراتك للنظام الأول ، بحيث عندما تنظر إلى الشفرة ، ستشعر بالبساطة مقارنة بتحرير مستند HTML ، وليس باستخدام حل
2X(17 × 24)
.
يمكن تحقيق ذلك من خلال الاختيار الدقيق للطرق والأدوات والأهداف للاختبار ، بحيث تكون اقتصادية وتعطي عائد استثمار كبير. اختبار فقط بقدر الضرورة ، حاول أن تكون مرنة. في بعض الأحيان يكون من المفيد حتى تجاهل بعض الاختبارات والتضحية بالموثوقية من أجل السرعة والبساطة.

معظم التوصيات الواردة أدناه مستمدة من هذا المبدأ.
هل انت جاهزالقسم 1. تشريح الاختبار
1.1 يجب أن يتكون اسم كل اختبار من ثلاثة أجزاء
ما يجب القيام به. يجب أن يشير تقرير الاختبار إلى ما إذا كانت المراجعة الحالية للتطبيق تفي بمتطلبات أولئك الأشخاص الذين ليسوا على دراية بالكود: المختبرين المشاركين في نشر مهندسي DevOps ، وكذلك نفسك في غضون عامين. سيكون من الأفضل إذا أبلغت الاختبارات عن المعلومات بلغة المتطلبات ، وتتكون أسمائها من ثلاثة أجزاء:
- ما الذي يتم اختباره بالضبط؟ على سبيل المثال ، أسلوب
ProductsService.addNewProduct
. - تحت أي ظروف وسيناريوهات؟ على سبيل المثال ، لا يتم تمرير السعر إلى الطريقة.
- ما هي النتيجة المتوقعة؟ على سبيل المثال ، لم يتم اعتماد منتج جديد.
على خلاف ذلك. فشل النشر ، فشل الاختبار المسمى "إضافة منتج". هل تفهم بالضبط ما الخطأ الذي يعمل؟
المذكرة. يحتوي كل فصل على رمز مثال وأحيانًا شكل توضيحي. انظر المفسدين.
أمثلة التعليمات البرمجيةكيف نفعل ذلك بشكل صحيح. يتكون اسم الاختبار من ثلاثة أجزاء.
1.2 هيكلة الاختبارات وفقا لنمط AAA
ما يجب القيام به. يجب أن يتكون كل اختبار من ثلاثة أقسام منفصلة بوضوح: الترتيب (التحضير) ، الفعل (الإجراء) والتأكيد (النتيجة). يضمن الالتزام بهذه البنية أن قارئ الكود الخاص بك لا يحتاج إلى استخدام معالج الدماغ لفهم خطة الاختبار:
الترتيب: كل الشفرة التي تجلب النظام إلى الحالة وفقًا لسيناريو الاختبار. قد يتضمن ذلك إنشاء مثيل للوحدة النمطية في مصمم الاختبار ، وإضافة سجلات إلى قاعدة البيانات ، وإنشاء أجزاء بديلة بدلاً من الكائنات ، وأي تعليمات برمجية أخرى تعد النظام لتشغيل الاختبار.
الفعل: تنفيذ التعليمات البرمجية كجزء من الاختبار. عادة سطر واحد فقط.
تأكيد: تأكد من أن القيمة التي تم الحصول عليها تلبي التوقعات. عادة سطر واحد فقط.
على خلاف ذلك. لن تقضي ساعات طويلة فقط في العمل باستخدام الكود الرئيسي ، ولكن سوف يتضخم عقلك أيضًا مما يجب أن يكون مهمة بسيطة - من الاختبار.
أمثلة التعليمات البرمجيةكيف نفعل ذلك بشكل صحيح. اختبار منظم وفقا لنمط AAA.
describe.skip('Customer classifier', () => { test('When customer spent more than 500$, should be classified as premium', () => {
مثال على antipattern. لا فصل ، في قطعة واحدة ، أكثر صعوبة في التفسير.
test('Should be classified as premium', () => { const customerToClassify = {spent:505, joined: new Date(), id:1} const DBStub = sinon.stub(dataAccess, "getCustomer") .reply({id:1, classification: 'regular'}); const receivedClassification = customerClassifier.classifyCustomer(customerToClassify); expect(receivedClassification).toMatch('premium'); });
1.3 وصف التوقعات بلغة المنتج: اذكر نمط BDD
ما يجب القيام به. تسمح اختبارات البرمجة بأسلوب تعريفي للمستخدم بفهم الجوهر على الفور دون قضاء دورة واحدة من معالج الدماغ. عندما تكتب رمزًا إلزاميًا معبأًا بالمنطق الشرطي ، يتعين على القارئ بذل الكثير من الجهد. من وجهة النظر هذه ، تحتاج إلى وصف التوقعات بلغة شبيهة بالإنسان بأسلوب BDD التعريفي باستخدام توقع / ينبغي وعدم استخدام رمز مخصص. إذا لم يكن هناك تأكيد ضروري في Chai and Jest ، والذي يتكرر غالبًا ، فيمكنك
توسيع Matcher Jest أو كتابة
البرنامج المساعد الخاص بك لـ Chai .
على خلاف ذلك. سيقوم الفريق بكتابة عدد أقل من الاختبارات وتزيين الاختبارات المزعجة
with .skip()
.
أمثلة التعليمات البرمجيةمثال باستخدام موكا .
مثال على antipattern. لفهم جوهر الاختبار ، يضطر المستخدم للحصول على رمز إلزامي طويل إلى حد ما.
it("When asking for an admin, ensure only ordered admins in results" , ()={
كيف نفعل ذلك بشكل صحيح. قراءة هذا الاختبار التعريفي واضحة.
it("When asking for an admin, ensure only ordered admins in results" , ()={
1.4 الالتزام باختبار الصندوق الأسود: اختبار الطرق العامة فقط
ما يجب القيام به. اختبار الدواخل سيؤدي إلى نفقات ضخمة ولن تسفر عن شيء تقريبًا. إذا كانت الشفرة أو واجهة برمجة التطبيقات (API) توفر النتائج الصحيحة ، فهل يستحق ذلك قضاء ثلاث ساعات في اختبار كيفية عمله داخليًا ثم دعم هذه الاختبارات الهشة؟ عند التحقق من السلوك العام ، في نفس الوقت ، تحقق التطبيق نفسه ضمنيًا ، ستفشل اختباراتك فقط في حالة وجود مشكلة محددة (على سبيل المثال ، الإخراج غير الصحيح). هذا النهج يسمى أيضا اختبار السلوكية. من ناحية أخرى ، إذا كنت تختبر الأجزاء الداخلية (طريقة "المربع الأبيض") ، فبدلاً من التخطيط لإخراج المكونات ، ستركز على التفاصيل الصغيرة ، وقد تنكسر اختباراتك بسبب التعديلات الطفيفة في الكود ، حتى لو كانت النتائج صحيحة ، ولكن سوف مرافقة تأخذ المزيد من الموارد.
على خلاف ذلك. سوف تتصرف الاختبارات الخاصة بك مثل
صبي يهتف "الذئب!" : قم بالإبلاغ بصوت عالٍ عن الإيجابيات الخاطئة (على سبيل المثال ، فشل الاختبار بسبب تغيير اسم المتغير الخاص). ليس من المستغرب أن يبدأ الناس قريبًا في تجاهل إشعارات CI ، وفي يوم من الأيام سيفتقدون خطأ حقيقي ...
أمثلة التعليمات البرمجيةمثال على antipattern. اختبار الدواخل دون سبب وجيه.
مثال باستخدام موكا .
class ProductService{
1.5 اختيار التنفيذ المحاكى الصحيح: تجنب الأشياء المزيفة لصالح الرهانات والجواسيس
ما يجب القيام به. تعتبر عمليات التنفيذ المحاكية (مضاعفات الاختبار) شرًا ضروريًا لأنها مرتبطة مع الأجزاء الداخلية للتطبيق ، وبعضها له قيمة كبيرة (قم
بتحديث ذاكرة التطبيقات المقلدة: كائنات وهمية (مقلات) ، وعقاب (أجزاء رديئة) وعناصر تجسس (جواسيس) ). ومع ذلك ، ليست كل التقنيات متكافئة. تم تصميم الجواسيس والكعوب لاختبار المتطلبات ، ولكن لها تأثير جانبي لا مفر منه - كما أنها تؤثر بشكل طفيف على الدواخل. والكائنات المزيفة مصممة لاختبار الدواخل ، مما يؤدي إلى حمل ضخم ، كما هو موضح في الفصل 1.4.
قبل استخدام تطبيقات المحاكاة ، اسأل نفسك أبسط سؤال: "هل يمكنني استخدام هذا لاختبار الوظائف التي ظهرت أو قد تظهر في الوثائق مع المتطلبات؟" إذا لم يكن كذلك ، فإنه ينم عن اختبار الصندوق الأبيض.
على سبيل المثال ، إذا كنت ترغب في معرفة ما إذا كان التطبيق يتصرف كما ينبغي عندما تكون خدمة الدفع غير متوفرة ، يمكنك إجراء كعب بدلاً من ذلك وإرجاع "لا إجابة" للتحقق مما إذا كانت الوحدة النمطية قيد الاختبار تُرجع القيمة الصحيحة. بحيث يمكنك التحقق من سلوك / استجابة / إخراج التطبيق تحت سيناريوهات معينة. يمكنك أيضًا التأكيد بمساعدة من جاسوس أنه عندما تكون الخدمة غير متوفرة ، تم إرسال الرسالة ، كما أنها اختبار سلوكي ، وهو ما ينعكس بشكل أفضل في الوثائق مع المتطلبات ("إرسال بريد إلكتروني إذا تعذر حفظ معلومات الدفع"). في الوقت نفسه ، إذا قمت بإجراء خدمة دفع وهمية وتأكدت من استدعائها باستخدام أنواع JS الصحيحة ، فإن الاختبار الخاص بك يستهدف الأجزاء الداخلية التي لا تتعلق بوظيفة التطبيق والتي من المحتمل أن تتغير كثيرًا.
على خلاف ذلك. يتضمن أي إعادة بيع التعليمات البرمجية العثور على وتحديث كافة الكائنات وهمية في التعليمات البرمجية. الاختبارات من صديق مساعد تتحول إلى عبء.
أمثلة التعليمات البرمجيةمثال على antipattern. الكائنات وهمية هي الشجاعة.
مثال باستخدام سينون .
it("When a valid product is about to be deleted, ensure data access DAL was called once, with the right product and right config", async () => {
كيف نفعل ذلك بشكل صحيح. تم تصميم الجواسيس لاختبار المتطلبات ، ولكن هناك تأثير جانبي - فهي تؤثر حتما على الدواخل.
it("When a valid product is about to be deleted, ensure an email is sent", async () => {
1.6 لا تستخدم "فو" ، استخدم مدخلات واقعية
ما يجب القيام به. غالبًا ما تحدث أخطاء الإنتاج مع بيانات إدخال محددة ومثيرة للدهشة. كلما كانت البيانات أكثر واقعية أثناء الاختبار ، زاد احتمال اصطياد الأخطاء في الوقت المحدد. لإنشاء بيانات حقيقية مزيفة تحاكي تنوع بيانات الإنتاج ونوعها ، استخدم مكتبات خاصة ، على سبيل المثال ،
Faker . يمكن لهذه المكتبات إنشاء أرقام هواتف واقعية وأسماء مستعارة للمستخدمين وبطاقات مصرفية وأسماء شركات ، وحتى النص "lorem ipsum". يمكنك إنشاء اختبارات (أعلى اختبارات الوحدات ، وليس بدلاً منها) التي تقوم بتوزيع عشوائي للبيانات المزيفة لتناسب الوحدة في اختبار ، أو حتى استيراد بيانات حقيقية من بيئة الإنتاج. تريد أن تذهب أبعد من ذلك؟ اقرأ الفصل التالي (حول الاختبار القائم على الملكية).
على خلاف ذلك. سيبدو اختبار التطوير الخاص بك ناجحًا باستخدام المدخلات الاصطناعية مثل "Foo" ، وقد تتعطل بيانات الإنتاج عندما
@3e2ddsf . ##' 1 fdsfds . fds432 AAAA
المتطفلين خطًا صعبًا مثل
@3e2ddsf . ##' 1 fdsfds . fds432 AAAA
@3e2ddsf . ##' 1 fdsfds . fds432 AAAA
@3e2ddsf . ##' 1 fdsfds . fds432 AAAA
.
أمثلة التعليمات البرمجيةمثال على antipattern. مجموعة اختبار تعمل بنجاح بسبب استخدام بيانات غير واقعية.
مثال باستخدام Jest .
const addProduct = (name, price) =>{ const productNameRegexNoSpace = /^\S*$/;
كيف نفعل ذلك بشكل صحيح. العشوائية مدخلات واقعية.
it("Better: When adding new valid product, get successful confirmation", async () => { const addProductResult = addProduct(faker.commerce.productName(), faker.random.number());
1.7 استخدم الاختبار المستند إلى خاصية للتحقق من صحة مجموعات الإدخال المتعددة
ما يجب القيام به. عادة لكل اختبار نختار عدة عينات من بيانات الإدخال. حتى إذا كان تنسيق الإدخال مشابهًا للبيانات الحقيقية (انظر الفصل "لا تستخدم" foo ") ، فإننا لا نغطي سوى مجموعات قليلة من بيانات الإدخال (الطريقة
('', true, 1)
، الطريقة
("string" , false" , 0)
). ولكن في العملية ، يمكن استدعاء واجهة برمجة التطبيقات (API) التي تسمى بـ 5 معلمات بآلاف من المجموعات المختلفة ، يمكن أن تؤدي إحداها إلى
تعطل العملية (
الدمج ). ماذا لو كان بإمكانك كتابة اختبار واحد يقوم تلقائيًا بإرسال 1000 مجموعة من بيانات الإدخال و تحديد ، في أي مجموعات رمز لا ترجع الإجابة الصحيحة؟ نفس الشيء نفعله مع م todike الاختبار على أساس الخصائص: عن طريق إرسال كل مزيج ممكن من إدخال البيانات في وحدة اختبار نزيد من فرصة للكشف عن الخلل، على سبيل المثال، لدينا أسلوب.
addNewProduct(id, name, isDiscount)
دعم مكتبته نسمي هذا الأسلوب مع عدد من المجموعات.
(, , )
، على سبيل المثال ،
(1, "iPhone", false)
،
(2, "Galaxy", true)
، وما إلى ذلك. يمكنك الاختبار بناءً على الخصائص باستخدام عداء الاختبار المفضل لديك (Mocha، Jest وما إلى ذلك) والمكتبات مثل
js- check أو
testcheck (لديها وثائق أفضل بكثير). يمكنك أيضًا
تجربة المكتبة السريعة الفحص ، والتي توفر ميزات إضافية ويرافقها الكاتب بفعالية.
على خلاف ذلك. أنت بصدد اختيار بيانات الإدخال للاختبار ، والتي تغطي مسارات تنفيذ التعليمات البرمجية التي تعمل بشكل جيد فقط. لسوء الحظ ، هذا يقلل من فعالية الاختبار كوسيلة للكشف عن الأخطاء.
أمثلة التعليمات البرمجيةكيف نفعل ذلك بشكل صحيح. اختبار العديد من المجموعات مع المختبر testcheck.
require('mocha-testcheck').install(); const {expect} = require('chai'); const faker = require('faker'); describe('Product service', () => { describe('Adding new', () => {
1.8 إذا لزم الأمر ، استخدم اللقطات القصيرة والمضمنة فقط.
ما يجب القيام به. عندما تحتاج إلى
الاختبار استنادًا إلى اللقطات ، استخدم لقطات قصيرة فقط دون كل العناصر الإضافية (على سبيل المثال ، في الأسطر 3 إلى 7) ، بما في ذلك كجزء من الاختبار (
لقطة مضمنة ) ، وليس كملفات خارجية. باتباع هذه التوصية سوف تبقي اختباراتك بديهية وأكثر موثوقية.
من ناحية أخرى ، تستفزنا أدلة وأدوات "اللقطة الكلاسيكية" لتخزين الملفات الكبيرة (على سبيل المثال ، الترميز لتقديم المكونات أو نتائج JSON API) على الوسائط الخارجية ومقارنة النتائج بالإصدار المحفوظ في كل مرة تقوم فيها بإجراء الاختبار. يمكن ، على سبيل المثال ، ربط اختبارنا ضمنيًا بـ 1000 سطر يحتوي على 3000 قيمة لم يراها مؤلف الاختبار مطلقًا ولم يكن يتوقعها. لماذا هذا سيء؟ لأن هناك 1000 سبب للاختبار. حتى سطر واحد يمكن أن يبطل لقطة ، وهذا يمكن أن يحدث في كثير من الأحيان. كم بعد كل مساحة أو تعليق أو تغيير بسيط في CSS أو HTML. بالإضافة إلى ذلك ، لن يخبرك اسم الاختبار بالفشل ، لأنه يتحقق فقط من أن 1000 سطر لم يتغير ، كما يشجع مؤلف الاختبار على أن يأخذ ما دامت وثيقة طويلة لا يستطيع تحليلها والتحقق منها. كل هذه الأعراض عبارة عن اختبار غامض ومتسرع ليس له مهمة واضحة ويحاول تحقيق الكثير.
تجدر الإشارة إلى أن هناك العديد من المواقف التي يكون من المقبول فيها استخدام الصور الطويلة والخارجية ، على سبيل المثال ، عند تأكيد المخطط ، وليس البيانات (استخراج القيم والتركيز على الحقول) ، أو عندما نادراً ما تتغير المستندات المستلمة.
على خلاف ذلك. اختبارات واجهة المستخدم تفشل. يبدو الرمز جيدًا ، ويتم عرض وحدات البكسل المثالية على الشاشة ، فما الذي يحدث؟ كشفت الاختبارات التي أجريتها مع لقطات الفرق بين المستند الأصلي وحرف المسافة الذي تم تلقيه للتو - تمت إضافة حرف واحد إلى العلامة ...
أمثلة التعليمات البرمجيةمثال على antipattern. ربط اختبار مع بعض الأسطر 2000 غير المعروفة من التعليمات البرمجية.
it('TestJavaScript.com is renderd correctly', () => {
كيف نفعل ذلك بشكل صحيح. التوقعات واضحة وفي دائرة الضوء.
it('When visiting TestJavaScript.com home page, a menu is displayed', () => {
1.9 تجنب مقاعد الاختبار العالمية والبيانات الأولية ، أضف البيانات إلى كل اختبار على حدة
ما يجب القيام به. وفقًا للقاعدة الذهبية (الفصل 0) ، يجب أن يضيف كل اختبار ويعمل ضمن مجموعة الصفوف الخاصة به في قاعدة البيانات لتجنب الارتباطات ، وكان من الأسهل على المستخدمين فهم الاختبار. في الواقع ، غالبًا ما ينتهك القائمون على الاختبار هذه القاعدة ، قبل إجراء الاختبارات التي تملأ قاعدة البيانات بالبيانات الأولية (البذور) (
وتسمى أيضًا "منصة الاختبار" ) من أجل زيادة الإنتاجية.
على الرغم من أن الأداء يمثل مهمة مهمة حقًا ، إلا أنه يمكن أن ينقص (انظر الفصل "مكونات الاختبار") ، ومع ذلك ، فإن تعقيد الاختبارات أكثر ضررًا وينبغي أن يدير قراراتنا في أغلب الأحيان. يجب أن تضيف كل حالة اختبار تقريبًا السجلات اللازمة إلى قاعدة البيانات وأن تعمل معها فقط. إذا كان الأداء بالغ الأهمية ، كحل وسط ، يمكنك فقط ملء البيانات الأولية بتلك الاختبارات التي لا تغير المعلومات (على سبيل المثال ، الاستعلامات).على خلاف ذلك. فشلت عدة اختبارات ، تم إحباط النشر ، والآن سيقضي الفريق وقتًا ثمينًا ، هل لدينا خطأ؟ دعونا ننظر ، لعنة ، يبدو أن اختبارين تغيير نفس البيانات الأولية.أمثلة التعليمات البرمجيةمثال على antipattern. الاختبارات ليست مستقلة وتستخدم نوعًا من الخطاف العام للحصول على بيانات عمومية من قاعدة البيانات. before(() => {
كيف نفعل ذلك بشكل صحيح. يمكنك البقاء داخل الاختبار ، كل اختبار يعمل فقط مع البيانات الخاصة به. it("When updating site name, get successful confirmation", async () => {
1.10 لا تصاب بالأخطاء ، لكن توقعها
. , - , try-catch-finally , . ( ), .
Chai:
expect(method).to.throw
( Jest:
expect(method).toThrow()
). , , . , , .
. (, CI-) , .
مثال على antipattern. حالة اختبار طويلة تحاول اكتشاف خطأ باستخدام try-catch. /it("When no product name, it throws error 400", async() => { let errorWeExceptFor = null; try { const result = await addNewProduct({name:'nest'});} catch (error) { expect(error.code).to.equal('InvalidInput'); errorWeExceptFor = error; } expect(errorWeExceptFor).not.to.be.null;
. , , , QA .
it.only("When no product name, it throws error 400", async() => { expect(addNewProduct)).to.eventually.throw(AppError).with.property('code', "InvalidInput"); });
1.11
. :
- smoke-,
- IO-less,
- , , ,
- , pull request', .
, , , #cold #api #sanity. . , Mocha :
mocha — grep 'sanity'
.
. , , , , , , , .
. '#cold-test' (Cold=== , - , ).
1.12
. , Node.js . , Node.
TDD . , , , .
-- , - . , , . , . , , , , (, ..).
. , .
2:
️2.1 :
. 10 , . . , 10 (, , ), , ,
? ?
: 2019- , TDD , , , . , , ,
. IoT-, Kafka RabbitMQ, , - . , , ? (, , ), , - .
( ) , , (« API, , !» (consumer-driven contracts)). , : , , , .
: TDD - . TDD , . , .
. ROI, Fuzz, , 10 .
. Cindy Sridharan 'Testing Microservices — the sane way'

مثال:
2.2
. , . , . , , ? — . : TDD-, .
«», API, , (, , in-memory ), , , . , , « », .
. , , 20 %.
. Express API ( ).

2.3 , API
. , ( ). - , ! — , , . «
-22 » : , , .
(consumer-driven contracts) PACT : , … ! PACT — «», . PACT- — . , API CI, .
. — .
.
2.4
. , Express-. . , , , JS- {req,res}. , (,
Sinon ) {req,res}, , .
node-mock-http {req,res} . , , HTTP-, res- (. ).
. Express- === .
2.5
. . CI- , . (, ), (, ), .
Sonarqube (2600+
)
Code Climate (1500+
). ::
Keith Holliday. , .
. CodeClimate, :

2.6 , Node
. , . ( ) . , - , ? ? , API 50 % ? , Netflix - (
Chaos Engineering ). : , . , Netflix,
chaos monkey , , , , - ( Kubernetes
kube-monkey , ). , . , , Node- , , v8 1,7 , UX , ?
node-chaos (-), , Node.
. , production .
. Node-chaos , Node.js, .

2.7 ,
. ( 0), , , . , (seeds) (
« » ) . , (. « »), , . . , , (, ).
. , , , ? , , , .
. - .
before(() => {
. , .
it("When updating site name, get successful confirmation", async () => {
3:
3.1. UI
. , , , . , , ( HTML CSS) . , (, , , ), , , .
. 10 , 500 (100 = 1 ) - - .
. .
test('When users-list is flagged to show only VIP, should display only VIP members', () => {
. UI . test('When flagging to show only VIP, should display only VIP members', () => {
3.2 HTML- ,
. HTML- , . , , CSS-. , 'test-id-submit-button'. . , , .
. , , . — , , Ajax . . , CSS 'thick-border' 'thin-border'
. , .
. CSS-.
<!-- the markup code (part of React component) --> <span id="metric" className="d-flex-column">{value}</span> <!-- what if the designer changes the classs? -->
3.3
. , , . , , . , — - , (.
« » ). (, ) , .
, : , . ( ) . , .
. , . ?
. .
class Calendar extends React.Component { static defaultProps = {showFilters: false} render() { return ( <div> A filters panel with a button to hide/show filters <FiltersPanel showFilter={showFilters} title='Choose Filters'/> </div> ) } } //Examples use React & Enzyme test('Realistic approach: When clicked to show filters, filters are displayed', () => { // Arrange const wrapper = mount(<Calendar showFilters={false} />) // Act wrapper.find('button').simulate('click'); // Assert expect(wrapper.text().includes('Choose Filter')); // This is how the user will approach this element: by text })
. .
test('Shallow/mocked approach: When clicked to show filters, filters are displayed', () => {
3.4 .
. (, ). (,
setTimeOut
) , . (,
Cypress cy.request('url') ), API,
wait(expect(element)) @testing-library/DOM . , API, , . , ,
hurry-up the clock . — , , ( ). , , - npm- , ,
wait-for-expect .
. , . , . .
. E2E API (Cypress).
. , DOM- (@testing-library/dom).
. .
test('movie title appears', async () => {
3.5.
. - , . , , . :
pingdom , AWS CloudWatch
gcp StackDriver , , SLA. , , (,
lighthouse ,
pagespeed ), . — , :
,
(TTI) . , , , , , DOM, SSL . , CI, 247 CDN.
. , , , , - CDN.
. Lighthouse .

3.6 API
. ( 2), , , ( ). API (,
Sinon ,
Test doubles ), API. . API , ( ). API, . , , API . , : .
. , API 100 , 20 .
3.7 ,
. E2E (end-to-end, ) UI (. 3.6). , , . , , - . , — (, ), . - , , UI-
Cypress Pupeteer . , : 50 , , . 10 . , , , — . , .
. UI , , ( , UI) .
3.8
. , API , , . (before-all), - . , : . , . - API- . , . (, ), , , . , : , API (. 3.6).
. , 200 , 100 , 20 .
. (before-all), (before-each) (, Cypress).
Cypress .
let authenticationToken;
3.9 smoke-,
. production- , , . , , , , . smoke- . production, , , . , smoke- , .
. , , production . /Payment.
. Smoke- .
it('When doing smoke testing over all page, should load them all successfully', () => {
3.10
. , . «» , , , , . , ( ) , , -, , , . « », .
. ,
Cucumber JavaScript .
StoryBook UI- , (, , , ..) , . , , .
. , .
. cucumber-js.
. Storybook , .

3.11
. , . , . , . , - . , . , , , . , - . UI « ». , (,
wraith , PhantomCSS), . (,
Applitools ,
Perci.io ) , , « » (, ), DOM/CSS, .
. , ( ) , ?
. : , .

. wraith UI.
# Add as many domains as necessary. Key will act as a label domains: english: "http://www.mysite.com" # Type screen widths below, here are a couple of examples screen_widths: - 600 - 768 - 1024 - 1280 # Type page URL paths below, here are a couple of examples paths: about: path: /about selector: '.about' subscribe: selector: '.subscribe' path: /subscribe
4:
4.1 (~80 %),
. — , . , . — (, ), . ? , 10-30 % . 100 % , . . , : Airbus, ; , 50 % . , , 80 % (
Fowler: «in the upper 80s or 90s» ), , , .
: (CI), (
Jest ) , . , . , ( ) — . , — , , . , .
. . , , . .
.
. ( Jest).

4.2 ,
. , . , , , , . , , - , .
PricingCalculator
, , , 10 000 … , , . , . 80- , . : , , , . , - .
. , , , .
. ? , QA . : , - . , - API .

4.3
. : 100 %, . كيف ذلك؟ , , , . . - : , , , .
, . JavaScript-
Stryker :
- « ». ,
newOrder.price===0
newOrder.price!=0
. «» .
- , , : , . , , .
, , , .
. , 85- 85 % .
. 100 %, 0 %.
function addNewOrder(newOrder) { logger.log(`Adding new order ${newOrder}`); DB.save(newOrder); Mailer.sendMail(newOrder.assignee, `A new order was places ${newOrder}`); return {approved: true}; } it("Test addNewOrder, don't use such test names", () => { addNewOrder({asignee: "John@mailer.com",price: 120}); });
. Stryker reports, , ().

4.4 -
. ESLint. ,
eslint-plugin-mocha , (
describe()
),
, .
eslint-plugin-jest , ( ).
. 90- , , , . , .
. , , .
describe("Too short description", () => { const userToken = userService.getDefaultToken()
5: CI
5.1 ,
. — . , . , ( !). , . (
ESLint standard Airbnb ), . ,
eslint-plugin-chai-expect , .
Eslint-plugin-promise ( ).
Eslint-plugin-security , DOS-.
eslint-plugin-you-dont-need-lodash-underscore , , V8, ,
Lodash._map(…)
.
. , , . ما الذي يحدث؟ , , . . , , .
. , . , ESLint production-.

5.2
. CI , , ..? ,
. لماذا؟ : (1) -> (2) -> (3) . , , .
, , , , - .
CI- ( ,
CircleCI local CLI ) . ,
wallaby , ( ) . npm- package.json, , (, , , ). (non-zero exit code)
concurrently . — ,
npm run quality
. githook (
husky ).
. , .
. Npm-, , , .
"scripts": { "inspect:sanity-testing": "mocha **/**--test.js --grep \"sanity\"", "inspect:lint": "eslint .", "inspect:vulnerabilities": "npm audit", "inspect:license": "license-checker --failOn GPLv2", "inspect:complexity": "plato .", "inspect:all": "concurrently -c \"bgBlue.bold,bgMagenta.bold,yellow\" \"npm:inspect:quick-testing\" \"npm:inspect:lint\" \"npm:inspect:vulnerabilities\" \"npm:inspect:license\"" }, "husky": { "hooks": { "precommit": "npm run inspect:all", "prepush": "npm run inspect:all" } }
5.3 production-
. — CI-. . —
Docker-compose . (, ) production-.
AWS Local AWS-.
, serverless
AWS SAM Faas-.
Kubernetes CI-, . , « Kubernetes»
Minikube MicroK8s , , . « Kubernetes»: CI- (,
Codefresh ) Kubernetes-, CI- ; .
. .
: CI-, Kubernetes-
(Dynamic-environments Kubernetes )
deploy: stage: deploy image: registry.gitlab.com/gitlab-examples/kubernetes-deploy script: - ./configureCluster.sh $KUBE_CA_PEM_FILE $KUBE_URL $KUBE_TOKEN - kubectl create ns $NAMESPACE - kubectl create secret -n $NAMESPACE docker-registry gitlab-registry --docker-server="$CI_REGISTRY" --docker-username="$CI_REGISTRY_USER" --docker-password="$CI_REGISTRY_PASSWORD" --docker-email="$GITLAB_USER_EMAIL" - mkdir .generated - echo "$CI_BUILD_REF_NAME-$CI_BUILD_REF" - sed -e "s/TAG/$CI_BUILD_REF_NAME-$CI_BUILD_REF/g" templates/deals.yaml | tee ".generated/deals.yaml" - kubectl apply --namespace $NAMESPACE -f .generated/deals.yaml - kubectl apply --namespace $NAMESPACE -f templates/my-sock-shop.yaml environment: name: test-for-ci
5.4
. , , . , 500 , , . , CI- (
Jest ,
AVA Mocha ) , . CI- (!), . , CLI , , .
. — , .
5.5
. , . 10 ? CI- npm-
license check plagiarism check ( ), , , Stackoveflow .
. , , .
.

5.6
. , Express, .
npm audit ,
snyk ( ). CI .
. . .
: NPM Audit

5.7
. package-lock.json Yarn npm ( ): .
npm install
npm update
, . , — . , package.json
ncu .
, . :
- CI , , npm outdated npm-check-updates (ncu). .
- , pull request' .
: ? , ( ,
eslint-scope ). « »:
latest , , (, 1.3.1, — 1.3.8).
. , .
5.8 CI-, Node
. , Node . , Node.
- . , Jenkins .
- Docker.
- , . . smoke-, (, , ) .
- , , , , , .
- , . , -. - ( ).
- . .
- , , .
- (, Docker-).
- , .
node_modules
.
. , .
5.9 : CI-, Node
. , , . Node, CI . , MySQL, Postgres. CI- «», MySQl, Postgres Node. , - (, ). CI, , .
. - ?
: Travis ( CI) Node.
language: node_js node_js: - "7" - "6" - "5" - "4" install: - npm install script: - npm run test