دليل بسيط لكيفية كتابة اختبارات الوحدة للعقود الذكية

http://upstate.agency/smart-contracts

تعد اختبارات الوحدات الجيدة أمرًا بالغ الأهمية عندما يتعلق الأمر بالتطوير الذكي للعقد. في الواقع ، نظرًا لأن العقود الذكية غير قابلة للتغيير وغالبًا ما تكون مسؤولة عن إدارة مبالغ كبيرة من المال ، يمكن للمرء أن يجادل بأن تطوير اختبارات الوحدات الجيدة للعقود الذكية هو أكثر أهمية من تطبيقات الويب والهاتف المحمول التقليدية.

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

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

الحصول على فهم صحي للمنطق الأعمال

أول ما عليك فعله هو التأكد من أن لديك فهمًا شاملاً لمنطق أعمال التطبيق الموزع (dapp). من المفيد توثيق المشكلات التي سيحلها dapp وكيف سيتم حلها. ستحتاج أيضًا إلى تحديد القيود المختلفة التي لدى dapp. على سبيل المثال ، يضع معظم dapps شكلاً من أشكال التحكم في الوصول المستند إلى الأدوار أو التحكم في الوصول المستند إلى العقود. من المهم توثيق هذه القيود وتأثيرها على الاستخدام.

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

رسم خريطة لاختبارات وحدتك

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

للقيام بذلك ، نقوم بتجميع اختبارات وحدتنا معًا بحيث يتم تعيينها إلى الوظائف المقابلة في العقود. فيما يلي مثال لمواصفات حالة الاستخدام الواحد المرتبطة بوظيفة buyTokens (_amount) في عقد الرمز المميز:

// عند تنشيط العقد
   // عندما لا يتم إيقاف العقد مؤقتًا
      // عندما بدأت فترة الطرح
         // عندما لا تنتهي فترة الطرح
            / / يجب أن يعود صحيحا

على العكس ، إليك المواصفات الأخرى المرتبطة بوظيفة buyTokens (_amount) عندما تنتهي فترة العرض:

// عند تنشيط العقد
   // عندما لا يتم إيقاف العقد مؤقتًا
      // عندما بدأت فترة الطرح
         // عندما تنتهي فترة الطرح
             / / يجب أن تعود

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

قم بمعالجة المعدلات أولاً ، ثم اعمل على متطلباتك وإذا كانت العبارات متسلسلة

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

على سبيل المثال ، بالنسبة لوظيفة مثل ما يلي ، نود أولاً معالجة حالات الاستخدام المرتبطة بمعدلات atStage (Stages.AuctionStarted) و validBid () قبل معالجة عبارات if وخلاف ذلك التي تلي:

///dev أول من يقدم عرض أسعار في CurrentAskingPrice يمنح NFT للمستفيد
///dev إذا تغلب أحد المزايدين على NFT ، فسوف يفوز بالمزاد وسيعاد العمل الزائد
processBid ()
عامة
تدفع
atStage (Stages.AuctionStarted) // ابدأ هنا أولاً
validBid () // عالج حالات الاستخدام هذه ثانياً
بإرجاع (uint) // ثم قم بمعالجة عبارات "if" و "آخر" و "تتطلب" التي تلي
{
  bid = msg.value؛
  المزايد = msg.sender ؛
  
  getCurrentAskingPrice ()؛
  if (bid> currentAskingPrice)
  {
    overage = bid.sub (currentAskingPrice)؛
    bidder.transfer (الفائض)؛
    عرض التسعير = CurrentAskingPrice؛
    المرحلة = مراحل.
    payBeneficiary ()؛
    sendToBidder ()؛
  }
  آخر إذا (السعر == currentAskingPrice)
  {
    المرحلة = مراحل.
    payBeneficiary ()؛
    sendToBidder ()؛
  }
  آخر
  {
    رجوع ("عرض التسعير أقل من السعر الحالي") ؛
  }
}

لاختبار متى يجب أن تعود عقودك ، وجدنا أن مساعد تأكيدات OpenZeppelin مفيد للغاية. يمكنك استخدامه مثل هذا:

في انتظار assertRevert (tokenInstance.buy ({من: مستثمر ، القيمة: 500})) ؛

وظيفة الحمولة الزائدة والحاجة إلى المكالمات ذات المستوى المنخفض

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

على سبيل المثال ، لا يدعم إطار عمل Truffle - الذي يعد أداة ممتازة وربما الإطار الأكثر شعبية في مساحة Ethereum اليوم - التحميل الزائد للوظائف. وهذا يعني ، إذا كنت بحاجة إلى اختبار وظيفتين بنفس الاسم ، ولكن مع عناصر مختلفة ، فستحتاج إلى استخدام مكالمات منخفضة المستوى لاختبار الوظيفة الثانية في ABI الخاص بالعقد. إذا لم تقم بذلك ، فستتلقى خطأ: عدد غير صالح من الوسائط لدالة Solidity. دعونا نلقي نظرة على مثال سريع.

إذا كان لديك دالتين من buyTokens في العقد ، واحدة لا تأخذ أي وسيطة ويتم إدراجها أولاً في ABI وتقبل وسيطة (_amount) ويتم إدراجها ثانياً في ABI ، فستحتاج إلى استخدام مكالمة منخفضة المستوى لاختبار وظيفة buyTokens (_amount) باستخدام encodeCall كما هو موضح أدناه.

data = encodeCall ("buyTokens" ، [int uint]] ، [100]) ؛ // 100 هي قيمة الوسيطة `_amount`
contractInstance.sendTransaction ({data ، من: مستثمر ، القيمة: 500})

يدرك مجتمع Truffle هذه المشكلة وسيتم تناولها في إصدار قادم. ومع ذلك ، فإن السيناريوهات الغريبة مثل هذه ليست غير نمطية عندما تقوم بتطوير عقود ذكية واختبارات الوحدات المقابلة لها. يجب أن تحصل على ماكرة (حتى الآن كن حذرًا للغاية) مع الحلول التي تستدعيها من وقت لآخر.

كيفية اختبار الوظائف الداخلية

يمكن أن يكون للوظائف المعطاة في Solidity رؤى مختلفة (أي العامة والخاصة والداخلية والخارجية) ، تجدر الإشارة إلى أن تطوير اختبارات الوحدات للوظائف الداخلية يختلف قليلاً عن تطويرها للوظائف العامة. وذلك لأن الوظائف الداخلية غير مدرجة في ABI للعقد الذكي بعد تجميعها. لذلك ، لاختبار الوظائف الداخلية لديك خياران:

  1. يمكنك إنشاء عقد آخر يرث العقد الذي تختبره من أجل اختبار وظيفة داخلية
  2. أو يمكنك اختبار منطق الوظيفة الداخلية من داخل الوظائف الأخرى في العقد التي تستدعي الوظيفة الداخلية

كلا الأسلوبين سينجز المهمة ، على الرغم من أن البعض قد يجادل بأن اختبار منطق الوظيفة الداخلية من داخل الوظائف الأخرى في العقد هو أكثر سهولة.

مفيد Ganache النصائح والحيل

كما ذكرت سابقًا ، نحن نستخدم إطار عمل Truffle بشكل أساسي لإنشاء dapps لعملائنا. يستخدم Truffle أداة تسمى Ganache والتي تمكنك من إطلاق سلسلة Ethereum blockchain الشخصية الخاصة بك والتي يمكنك استخدامها لإجراء الاختبارات وتنفيذ الأوامر وفحص الحالة أثناء التحكم في كيفية عمل السلسلة. إنه مفيد للغاية.

ما هو لطيف حقًا في Ganache هو كيف يمكن تخصيصه بسهولة لتلبية احتياجات dapp الفريدة الخاصة بك. على سبيل المثال ، لقد طورنا اختبارات وحدة لـ dapps تتطلب منا اختبار حالات الاستخدام لعشرات وعشرات المستخدمين. من خلال أمر واحد بسيط ، فإن Ganache يجعل من السهل علينا اختبار أكبر عدد ممكن من الحسابات:

ganache-cli -a 40 // حيث تشير علامة `a` إلى بدء تشغيل Ganache مع 40 حساب تجريبي

بالإضافة إلى ذلك ، يمكننا تعيين أرصدة هذه الحسابات لتبدأ مع ETH بقدر ما هو ضروري لاختباراتنا. بشكل افتراضي ، يقوم Truffle بتعيين الأرصدة عند 100 ETH. يمكننا بسهولة زيادة أو تقليل هذه القيمة وفقًا لمتطلباتنا الفريدة:

ganache -e 10000` // حيث تشير علامة `e` إلى مقدار ETH الذي يجب أن يبدأ به كل حساب افتراضيًا

Ganache هي أداة مفيدة للغاية عندما يتعلق الأمر بتطوير العقود الذكية لـ Ethereum. إنها تساعد على تبسيط عملية التطوير وتخفيف المخاطر وتحسين موثوقية dapp.

ربط كل ذلك معا

يشبه اختبار الوحدة في حيز التعاقد الذكي بشكل أساسي اختبار الوحدة في عالم تطبيقات الويب والجوال. الفرق الأكبر هو أن العقود الذكية اليوم غير قابلة للتغيير ولا تقدم عمومًا طرقًا بسيطة للتكرار عليها بمرور الوقت [1]. لذلك ، يعد تطوير اختبارات الوحدة الجيدة أمرًا بالغ الأهمية بالنظر إلى ثبات العقد الذكي وحقيقة أنها غالبًا ما تكون مسؤولة عن إدارة مبالغ كبيرة من المال. الطريقة الوحيدة للثقة بأن العقود الذكية الخاصة بك آمنة وموثوق بها هي اختبارها بدقة.

من أجل التبني الجماعي للعقود الذكية ، من الأهمية بمكان أن يعمل مجتمعنا معًا من أجل النهوض بالمساحة وتسهيل قيام المطورين بكتابة تطبيقات معقدة على رأس المجموعات العامة.

نأمل أن تكون هذه الدروس المستفادة من تجاربنا في تطوير اختبارات الوحدات مفيدة وتدعم جهودك لبناء عقود ذكية آمنة وموثوقة. لا تتردد في التواصل إذا كانت لديك أسئلة أو أفكار إضافية ترغب في مناقشتها مع فريقنا - [email protected]

في الأيام المقبلة ، سننشر مقالة متابعة لهذا ، ما تعلمته من اختبار العقود الذكية المعقدة.

[١] قام فريق Zeppelin مؤخرًا بإطلاق ZeppelinOS والذي يوفر مجموعة من المكتبات القياسية القابلة للترقية على السلسلة ويسمح بترقية العقود الذكية باستخدام أنماط الوكيل.

☞ للبقاء على اطلاع دائم بعملنا مع تطوير العقود الذكية ، تابعنا على "متوسط" و "تويتر".