تخطٍ إلى المحتوى الرئيسي

How to mock سوليديتي smart contracts for testing

Solidity
العقود الذكيه
الاختبار
mocking
المستوى المتوسط
ماركوس واس
2 مايو 2020
4 دقيقة قراءة

الكائنات الوهمية (opens in a new tab) هي نمط تصميم شائع في البرمجة كائنية التوجه. Coming from the old French word 'mocquer' with the meaning of 'making fun of', it evolved to 'imitating something real' which is actually what we are doing in programming. Please only make fun of your smart contracts if you want to, but mock them whenever you can. It makes your life easier.

اختبار الوحدات للعقود باستخدام الكائنات الوهمية

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

يمكنك كتابة منطق إعداد اختبار معقد في كل مرة يجلب العقد في الحالة المطلوبة أو يمكنك كتابة Mock. محاكاة العقد سهلة باستخدام الوراثة (Inheritance). ببساطة أنشئ عقد Mock ثانياً يرث من العقد الأصلي. يمكنك الآن تجاوز الوظائف (Override) في الـ Mock الخاص بك. دعنا نرى ذلك من خلال مثال.

مثال: PrivateERC20

نحن نستخدم مثال لعقد ERC-20 له فترة خصوصية أولية. يمكن للمالك إدارة المستخدمين الخاصين والذين سيُسمح لهم فقط بتلقي التوكنات في البداية. بمجرد مرور وقت معين، سيُسمح للجميع باستخدام التوكنات. إذا كنت مهتماً، فنحن نستخدم Hook الـ _beforeTokenTransfer (opens in a new tab) من عقود OpenZeppelin الجديدة الإصدار 3.

1pragma solidity ^0.6.0;
2
3import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4import "@openzeppelin/contracts/access/Ownable.sol";
5
6contract PrivateERC20 is ERC20, Ownable {
7 mapping (address => bool) public isPrivateUser;
8 uint256 private publicAfterTime;
9
10 constructor(uint256 privateERC20timeInSec) ERC20("PrivateERC20", "PRIV") public {
11 publicAfterTime = now + privateERC20timeInSec;
12 }
13
14 function addUser(address user) external onlyOwner {
15 isPrivateUser[user] = true;
16 }
17
18 function isPublic() public view returns (bool) {
19 return now >= publicAfterTime;
20 }
21
22 function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
23 super._beforeTokenTransfer(from, to, amount);
24
25 require(_validRecipient(to), "PrivateERC20: مستلم غير صالح");
26 }
27
28 function _validRecipient(address to) private view returns (bool) {
29 if (isPublic()) {
30 return true;
31 }
32
33 return isPrivateUser[to];
34 }
35}
إظهار الكل

And now let's mock it.

1pragma solidity ^0.6.0;
2import "../PrivateERC20.sol";
3
4contract PrivateERC20Mock is PrivateERC20 {
5 bool isPublicConfig;
6
7 constructor() public PrivateERC20(0) {}
8
9 function setIsPublic(bool isPublic) external {
10 isPublicConfig = isPublic;
11 }
12
13 function isPublic() public view returns (bool) {
14 return isPublicConfig;
15 }
16}
إظهار الكل

ستتلقى إحدى رسائل الخطأ التالية:

  • PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.
  • PrivateERC20.sol: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?.

بما أننا نستخدم إصدار سوليديتي 0.6 الجديد، علينا إضافة الكلمة المفتاحية virtual للدوال التي يمكن تجاوزها و override للدالة التي تقوم بالتجاوز. لذا دعنا نضيفهما إلى كلتا الدالتين isPublic.

الآن في اختبارات الوحدات الخاصة بك، يمكنك استخدام PrivateERC20Mock بدلاً من ذلك. عندما تريد اختبار السلوك أثناء وقت الاستخدام الخاص، استخدم setIsPublic(false)، وبالمثل استخدم setIsPublic(true) لاختبار وقت الاستخدام العام. بالطبع في مثالنا، يمكننا ببساطة استخدام مساعدات الوقت (opens in a new tab) لتغيير الأوقات وفقًا لذلك أيضًا. But the idea of mocking should be clear now and you can imagine scenarios where it is not as easy as simply advancing the time.

محاكاة العديد من العقود

يمكن أن يصبح الأمر فوضوياً إذا اضطررت لإنشاء عقد آخر لكل Mock مفرد. إذا كان هذا يزعجك، فيمكنك إلقاء نظرة على مكتبة MockContract (opens in a new tab). إنها تسمح لك بتجاوز وتغيير سلوكيات العقود بسرعة. ومع ذلك، فهي تعمل فقط لمحاكاة الاستدعاءات لعقد آخر، لذا لن تعمل في مثالنا.

يمكن أن تكون المحاكاة أكثر قوة

The powers of mocking do not end there.

  • Adding functions: Not only overriding a specific function is useful, but also just adding additional functions. من الأمثلة الجيدة للرموز وجود دالة mint إضافية للسماح لأي مستخدم بالحصول على رموز جديدة مجانًا.
  • الاستخدام في شبكات الاختبار (Testnets): عندما تنشر وتختبر عقودك على شبكات الاختبار مع تطبيقك اللامركزي، فكر في استخدام نسخة Mock. تجنب تجاوز الوظائف ما لم تضطر حقاً لذلك. فأنت تريد اختبار المنطق الحقيقي في النهاية. ولكن إضافة دالة إعادة تعيين (Reset) مثلاً يمكن أن يكون مفيداً بحيث يعيد تعيين حالة العقد ببساطة إلى البداية، دون الحاجة إلى نشر جديد. من الواضح أنك لن ترغب في وجود ذلك في عقد على الشبكة الرئيسية (Mainnet).

آخر تحديث للصفحة: 25 أغسطس 2025

هل كانت تعليمات الاستخدام هذه مفيدة؟