سالیدیتی (Solidity) یک زبان برنامه نویسی حرفهای است که برای کار با فناوری بلاکچین و نوشتن قراردادهای هوشمند در شبکه اتریوم طراحی شده است. سالیدیتی بر اساس سایر زبانهای برنامهنویسی از جمله، ++C، پایتون و جاوا اسکریپت است. اگر با این زبانهای برنامه نویسی آشنایی دارید، یادگیری زبان برنامه نویسی بلاکچین Solidity برای شما راحتتر خواهد بود.
درمورد سالیدیتی (Solidity) کامل میتوانید در مقاله زبان برنامه نویسی سالیدیتی (Solidity) چیست؟ مطالعه داشته باشید.
در نگاه اول به نظر میرسد که چارچوب ترافل (Truffle)تماما برای نوشتن، اجرا و آزمایش قراردادها ایجاد شده است و این کارها را نیز به خوبی انجام میدهد. هرچند موارد دیگری نیز وجود دارد که با استفاده از چند نکته میتوانید از ترافل (Truffle) برای نوشتن و اجرای آزمایشی بهره ببرید.
در این مقاله به سه نکته خاص خواهیم پرداخت:
۱. استفاده از صبر (await) و ناهمزمانی (async) در مقابل وعده ها (promise)
۲. ایجاد محیط مشترک برای انجام چندین آزمایش
۳. ضمیمه کردن آزمایش ها در سایر آزمایش ها
استفاده از این نکات میتواند به کاهش مقدار بسیار زیادی کپی کردن کد در آزمایش شما کمک کند و آزمایش های خواندنی تری بنویسید.
استفاده از صبر (await) و ناهمزمانی (async) در مقابل وعده ها (promise) در Solidity
ترافل (Truffle) همراه با چند آزمایش ارائه میشود و هر آزمایش شامل چندین سرفصل تکرارشونده است و از وعده ها استفاده میکند.
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰
۱۱
۱۲
۱۳
۱۴
۱۵
۱۶
۱۷
۱۸
۱۹
۲۰
۲۱
۲۲
۲۳
|
it(“should call a function
that depends on a linked
library”, function() {
var meta;
var metaCoinBalance;
var metaCoinEthBalance;
return
MetaCoin.deployed().then(function(instance){
meta = instance;
return
meta.getBalance.call(accounts [0]);
}).then(function(outCoinBalance) {
metaCoinBalance = outCoinBalance.toNumber();
return
meta.getBalanceInEth.call(accounts[0]);
}).then(function(outCoinBalan ceEth) {
metaCoinEthBalance = outCoinBalanceEth.coNumber();
)).then(function() {
assert.equal(metaCoinEthBalance,
۲ * metaCoinBalance,
“Library function returned unexpected function, linkage may be broken”):
});
});
|
این مجموعه آزمایش ها اساسا کرافت (cruft یا کد بد طراحی شده، به صورت غیرضروری پیچیده یا ناخواسته) میباشند. در ادامه به این موضوع میپردازیم که اگر آزمایش با ناهمزمانی و صبر مجدد نوشته شود چگونه خواهد بود.
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰
۱۱
۱۲
۱۳
۱۴
۱۵
۱۶
|
it(“should call a function
that depends on a linked
library”, async function() {
var meta = await
MetaCoin.deployed();
var metaCoinBalance =
(await
meta.getBalance.call(accounts[0])).toNumber();
var metaCoinEthBalance = (await
meta.getBalanceInEth.call(accounts[0])). toNumber();
assert.equal(metaCoinEthBalan ce, 2 *
metaCoinBalance,
“Library function returned
unexpected function, linkage may be broken”); ;
});
|
میتوانید مشاهده کنید که با تبدیل تابع آزمایش به تابع ناهمزمانی و سپس صبر برای تمام مقادیر مورد نیاز تا چه حد میتوان در کدنویسی صرفه جویی کرد. بنابراین نیازی به نوشتن توابع برای مدیریت هر وعده قرارداد ترافل (Truffle) نداریم.
صرفه جویی دیگری که میتوان انجام داد، جمع بندی پاسخ های صبر در براکت ها و تبدیل مستقیم آنها از شی نوع BigNumber به اعداد جاوااسکریپت است. این امر با استفاده از دستور زیر انجام میشود:
۱
۲
|
(await
meta.getBalance.call(accounts[0])).toNumber();
|
هر uint256 بازگشته از سالیدیتی به جاوا اسکریپت یک شی از نوع BigNumber است و قابل تبدیل به عدد یا رشته میباشد.
ایجاد محیط مشترک برای انجام چندین آزمایش
ترفند مفید دیگر ، ایجاد محیط مشترکی از متغیرهایی است که میخواهید در چندین آزمایش در چندین قرارداد استفاده کنید.
نحوه تنظیمات اولیه ترافل (Truffle) این موضوع را بلافاصله مشخص نمیکند و به بررسی دقیق تر برای یافتن رویکرد صحیح نیاز است.
ترافل (Truffle) اینگونه آزمایش را ایجاد میکند:
۱
۲
۳
|
contract(‘MetaCoin”,
function(accounts) { …
tests …}
|
اکنون اگر میخواهید این آزمایش را در آزمایش دیگری به کار بگیرید، کار دشوار میشود. چگونه بسته ای از متغیرهایی که در این آزمایش ایجاد شده اند را به آزمایش دیگری انتقال میدهید.
در دستور زیر ، اصلاح بر روی اجرای آزمایشی را مشاهده میکنیم که دارای اکسپورت یا انتقال میباشد و محیط JSON را در فایل جدیدی به اسم shared.js قرار میدهد:
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰
۱۱
۱۲
۱۳
|
const run = exports.run = async(accounts) =>{
const meta =await MetaCoin.deployed();
it(“should have MetaCoin
deployed”, () =>
{ assert(meta !==
undefined, “MetaCoin is
deployed”);
});
return { meta }
};
contract(‘Shared’, run);
|
اکنون تابع آزمایش بسیار خوبی داریم که ناهمزمان است و شامل شی محیطی با مثال قرارداد ما میباشد. این تابع اجرای قرارداد ترافل (Truffle) ما را به تاخیر میاندازد و میتوانیم مثال مورد استفاده را داخل سایر قراردادها قرار دهیم:
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰
۱۱
۱۲
۱۳
۱۴
۱۵
۱۶
۱۷
۱۸
۱۹
۲۰
۲۱
|
const shared = require(‘./shared.js’); contract(‘MetaCoinSale’,
(accounts) => {
let meta; //explained in a moment it(“should have the shared context”, async() => {
context= await shared.run(accounts);
meta = context.meta;
//or…
({ meta,…}=
context);
//make sure not undefined assert(meta !==
undefined, ‘has MetaCoin instance’);
});
… tests can use MetaCoin
instance now…
});
|
۱
۲
۳
|
//shared.js
const run = exports.run= async(accounts) => {…}
|
هم چنین میتوانید چندین متغیر دیگر در فایل shared.js قرار دهید. در آزمایش های قرارداد شما، صرفا از واگذاری برای متغیرها استفاده کنید. هم چنین برای آسانتر شدن کارها، متغیرها را در حوزه تابع اصلی اجرا کننده آزمایش حفظ کردیم تا تمام آزمایش ها به آن دسترسی داشته باشند.
چرا ورود متغیرهایی نظیر meta به قراردادهای MetaCoin در آزمایش های بدین گونه است؟ این امر، هر آزمایش را به شدت ساده میکند در غیر این صورت باید دستور زیر را فراخوانی کنید:
۱
۲
|
const meta = await MetaCoin.deployed( );
|
شاید بعضی افراد این موضوع را نپسندند اما این رویکرد تاکنون عملکرد خوبی داشته است.
ضمیمه کردن آزمایش ها در سایر آزمایش ها
این نکته اساسا مشابه با اصلاح تجزیه تابع اجرا کننده آزمایش از قرارداد ترافل (Truffle) موجود در shared.js میباشد. برای MetaCoin این نکته به صورت زیر میباشد:
۱
۲
۳
۴
۵
|
const run = exports.run=
(accounts) => { … MetaCoin tests … };
contract(‘MetaCoin’, run);
|
سپس از این مورد در قرارداد دیگری استفاده میکنید:
۱
۲
۳
۴
۵
۶
۷
۸
|
const metaTests= require(‘./metacoin.js’); contract(‘MetaCoinSale’,
(accounts) => {
…
metaTests.run(accounts);
…
});
|
به خاطر داشته باشید که اجرا کننده واقعی قرارداد، ناهمزمان نیست:
۱
۲
|
const run = exports.run=
(accounts) => {
|
هرچند تمام آزمایش شما میتواند به صورت زیر، ناهمزمان شود:
۱
۲
۳
۴
۵
۶
|
const run = exports.run = (accounts) => {
it(“should do some async
magic”, async() => { … }
};
contract(‘MetaCoin’, run);
|
با استفاده از این نکات میتوانید آزمایش های کوچکی بنویسید که منجر به قراردادهای هوشمند ایمن و بسیار خوبی شوند.