過去4回つづけてきましたが、今回がラストになります。
最後はやっぱりちゃんとテストを読もうと思います。
TestRPCで書かれています。js出かけますね。
https://github.com/AlisProject/ico-contracts/tree/master/test
ちなみに見たらわかりますが、本体のコードよりもテストの方が長いのではないかと思います。通常の開発でもテストは重要ですが、スマートコントラクトの場合デプロイしてしまうと修正ができないのでテストは基本カバレッジ100%にしておくのが吉とのこと。
全部読むと長いのでテストの中から面白いところだけピックアップして紹介します。
import advanceToBlock from './helpers/advanceToBlock';
ブロックを今の位置から進める処理が書かれています。内部的にはブロックをマイニングして進める、という形です。
まずadvanceBlockですが、内部でevm_mineというmethodを一回呼び出しています。これはマイニングをしているので一つブロックが生成されます。つまり一つブロックが前に進む。advanceToBlockで引数に渡した数だけブロックを前に進めることができます。
export function advanceBlock() {
return new Promise((resolve, reject) => {
web3.currentProvider.sendAsync({
jsonrpc: '2.0',
method: 'evm_mine',
id: Date.now(),
}, (err, res) => (err ? reject(err) : resolve(res)));
});
}
// Advances the block number so that the last mined block is `number`.
export default async function advanceToBlock(number) {
if (web3.eth.blockNumber > number) {
throw Error(`block number ${number} is in the past (current is ${web3.eth.blockNumber})`);
}
while (web3.eth.blockNumber < number) {
await advanceBlock();
}
}
以下はalis_crowdsale_finalizable.jsに書かれている簡単な使用例です。advanceToBlockでわざとクラウドセールを終了させたあと、ownerがクラウドセールをfinalizedできることをテストしています。
describe('finalize', () => {
it('can be finalized by owner after ending', async function () {
await advanceToBlock(this.endBlock);
await this.crowdsale.finalize({ from: owner }).should.be.fulfilled;
});
こちらは時間を進める処理です。
import increaseTime from './helpers/increaseTime';
durationで渡した数だけincreaseTimeによって時間が進められます。evm_increaseTimeで時間を進めてからevm_mineでブロックをマイニング。こうすることで時間を進めていきます。
export default function increaseTime(duration) {
const id = Date.now();
return new Promise((resolve, reject) => {
web3.currentProvider.sendAsync({
jsonrpc: '2.0',
method: 'evm_increaseTime',
params: [duration.asSeconds()],
id,
}, (err1) => {
if (err1) return reject(err1);
web3.currentProvider.sendAsync({
jsonrpc: '2.0',
method: 'evm_mine',
id: id + 1,
}, (err2, res) => (err2 ? reject(err2) : resolve(res)));
});
});
}
Alisのトークンセールは時間経過によって変換レートが変化していたのでそのテストのために使われています。increaseTimeで1週間後の2分前のように強化条件でのテストをしていることがわかります。
it('should rate of week1 be 2,900 ALIS when 1 minute before ended', async function () {
const duration = (60 * 60 * 24 * 7) - 120; // 1 week - 2 minute.
await increaseTime(moment.duration(duration, 'second'));
const expect = 2900;
await advanceToBlock(this.endBlock - 1);
const actual = await this.crowdsale.getRate();
await actual.should.be.bignumber.equal(expect);
});
トークンのテストにはブロックを進めたりタイムスタンプを進めたりと色々な工夫をしてあらゆるパターンのテストを実行していました。
他にも色々なテストが書かれていて面白いですが今回はここまでです。