How to mock سوليديتي smart contracts for testing
الكائنات الوهمية (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;23import "@openzeppelin/contracts/token/ERC20/ERC20.sol";4import "@openzeppelin/contracts/access/Ownable.sol";56contract PrivateERC20 is ERC20, Ownable {7 mapping (address => bool) public isPrivateUser;8 uint256 private publicAfterTime;910 constructor(uint256 privateERC20timeInSec) ERC20("PrivateERC20", "PRIV") public {11 publicAfterTime = now + privateERC20timeInSec;12 }1314 function addUser(address user) external onlyOwner {15 isPrivateUser[user] = true;16 }1718 function isPublic() public view returns (bool) {19 return now >= publicAfterTime;20 }2122 function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {23 super._beforeTokenTransfer(from, to, amount);2425 require(_validRecipient(to), "PrivateERC20: مستلم غير صالح");26 }2728 function _validRecipient(address to) private view returns (bool) {29 if (isPublic()) {30 return true;31 }3233 return isPrivateUser[to];34 }35}إظهار الكلAnd now let's mock it.
1pragma solidity ^0.6.0;2import "../PrivateERC20.sol";34contract PrivateERC20Mock is PrivateERC20 {5 bool isPublicConfig;67 constructor() public PrivateERC20(0) {}89 function setIsPublic(bool isPublic) external {10 isPublicConfig = isPublic;11 }1213 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