トップへ戻る
公開日
2018年8月15日
筆者:Curvegrid
アン・キルツァー

9日目: 両者の長所を活かす: SolidityとJavascriptのスマート コントラクト ユニット テスト

スマートコントラクトは、分散型アプリやカスタム暗号通貨を作成するための強力なツールです。

スマートコントラクトは、分散型アプリやカスタム暗号通貨を作成するための強力なツールです。従来のソフトウェアとは異なり、一度デプロイされるとロールバックやパッチの適用が困難です。暗号通貨の時価総額は2,000億ドルを超えており、多くの価値がかかっています。ユニットテストは、スマートコントラクトが期待通りに動作することを確認するのに役立ちます。

では、ユニットテストはどのように書くのでしょうか?SolidityとJavaScriptの両方でテストを書くことができるようになりましたが、この2つは完全に互換性があるわけではありません。単純なトークンをテストする例を見てみましょう。 (シンプルなので複雑ではなく、必ずしも簡単ではありません!)github のレポに私の作業を載せていますので、よろしければご覧ください。最初にtruffleganache-cliをインストールする必要があります。私はTruffle v4.1.11、Solidity v0.4.24、Ganache v6.1.6を使用しており、Mac OS X High Sierra 10.13.6で動作しています。

私の最初のトークンは、次のようなコンストラクタを持っていました。


pragma solidity 0.4.24;contract Mikancoin {  uint public totalSupply;  mapping (address => uint) public balanceOf;  constructor(uint _initialSupply) public {    totalSupply = _initialSupply;    balanceOf[msg.sender] = _initialSupply;    emit Transfer(0x0, msg.sender, totalSupply);  }...


そしてmigrations/2_deploy_contracts.jsはこんな感じ。


var Mikancoin = artifacts.require("Mikancoin");module.exports = function(deployer) {    deployer.deploy(Mikancoin, 100);};


これで ganache のコマンドラインインターフェイスを起動できるようになりました。


ガナッシュクリ


別のターミナルで、これをテストブロックチェーンにデプロイしてみましょう。


truffle compile && truffle migrate


では、solidity で最初のユニットテストをtest/TestMikancoin.sol で書いてみましょう。


pragma solidity 0.4.24;

import 'truffle/Assert.sol';
import 'truffle/DeployedAddresses.sol';
import '../contracts/Mikancoin.sol';

contract TestMikancoin {
  function testConstructor() public {
    // Get the address and cast it
    Mikancoin mikan = Mikancoin(DeployedAddresses.Mikancoin());
    Assert.equal(mikan.totalSupply(), 100, "Total supply");
    Assert.equal(mikan.balanceOf(this), 0, "We have no Mikan");
  }
}


DeployedAddressesを使ってミカンコインが配備された場所を調べ、そのアドレスをミカンコインにキャストすることができます。そして、アサートを使ってコインが正しい動作をしているかどうかを確認することができます。トリュフテストを実行すると、100個のみかんコインが正常にデプロイされ、残高がゼロになっていることがわかります。

ここで最初の問題にたどり着きます。送信するトークンがなければ、テストを書くのも面白くないし、徹底したものにもなりません。 誰が最初のマイカンを取得したのでしょうか。移行スクリプトが最初のデプロイを行ったので、おそらく内部のトリュフアドレスが実を結んだのでしょう。ツールのトリュフコンソールを使って確認してみましょう。

トリュフコンソール

誰がミカンコインのコンストラクターを呼んだのか、どのくらいのガスが使われたのかがわかります。また、それが ganache-cli によって生成された最初の eth アドレスと一致していることもわかります。これで、常に同じ10個のアドレスを生成するように ganache-cli を設定することができましたが、まだ簡単には制御できません。デプロイ時に誰がコインを得るかを決めることができるように、契約を少し変更してみましょう。


 constructor(uint _initialSupply, address _initialOwner) public {
    require(_initialOwner != 0x0);
    totalSupply = _initialSupply;
    balanceOf[_initialOwner] = _initialSupply;
    emit Transfer(0x0, _initialOwner, totalSupply);
  }


ヘルパー契約書を書いて、みかんコインを簡単に管理することもできます。


pragma solidity 0.4.24;

import './Mikancoin.sol';

contract MikanFarm {

  Mikancoin [] public deployed;

  function deployMikancoin(uint _initialSupply) public returns (Mikancoin){
    Mikancoin latest = new Mikancoin(_initialSupply, msg.sender);
    deployed.push(latest);
    return latest;
  }
}



次に、移行スクリプトを更新します。


var MikanFarm = artifacts.require("MikanFarm");

module.exports = function(deployer) {
    deployer.deploy(MikanFarm);
};


そして、いよいよミカンを操るテスト!。


pragma solidity 0.4.24;

import 'truffle/Assert.sol';
import 'truffle/DeployedAddresses.sol';
import '../contracts/MikanFarm.sol';
import '../contracts/Mikancoin.sol';

contract TestMikancoin {

  function testTransfer() public {
    uint startTokens = 100;
    MikanFarm farm = MikanFarm(DeployedAddresses.MikanFarm());
    Mikancoin mikan = farm.deployMikancoin(startTokens);
	Assert.equal(mikan.balanceOf(this), startTokens, "We should have 100 Mikan");
	address fox = 0x284A84baA00626e2773a1138D53923b4acAED2F4;
	Assert.equal(mikan.balanceOf(fox), 0, "Fox has 0 mikan");

	uint tokens	= 7;
	Assert.isTrue(mikan.transfer(fox, tokens), "Transfer should succeed");
	Assert.equal(mikan.balanceOf(fox), tokens, "Fox balance after transfer");
	Assert.equal(mikan.balanceOf(this), startTokens - tokens, "Sender balance after transfer");
  }
}


さて、転送のrequire文をテストして、ゼロのアドレスにトークンを送るような悪いことができないことを確認したいとします。solidityでは、try-catchがないので、これは難しいです。0x0に送信したときのスタックトレースは本当にひどいものになってしまいます。


function testBadTransfer() public {
    MikanFarm farm = MikanFarm(DeployedAddresses.MikanFarm());
    Mikancoin mikan = farm.deployMikancoin(100);
    Assert.isFalse(mikan.transfer(0x0, 5), "Transfer to 0 address should fail"); // require() fails and therefore this will revert
  }


このテストを追加してトリュフテストを実行すると、以下のように生成されます。

トリュフ投げ

以前はスローをテストする方法がありましたが、require & revertに変更されてからは、これらのテクニックは機能しなくなりました。javascript に切り替えて、失敗例のテストをデモしてみましょう。このテストを実行する前に、ルートディレクトリで npm install を実行して、chai の依存関係を取得する必要があることに注意してください。


var Mikancoin = artifacts.require("Mikancoin.sol");

const should = require('chai').should();

contract('Mikancoin', function(accounts) {

  const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';

  beforeEach(async function () {
    this.mikancoin = await Mikancoin.new(300, accounts[0]);
  });

  describe("transfer", function() {
    describe("when the recipient is the zero address", function() {
      const amount = 123;
      it("reverts", async function () {
        try {
          await this.mikancoin.transfer(ZERO_ADDRESS, amount, { from: accounts[0] });
        } catch (error) {
          error.message.should.include('revert', `Expected "revert", got ${error} instead`);
          return;
        }
        should.fail('Revert did not happen');
      });
    });
  });
});


solidityでテストしたら、javascriptが楽勝に感じます。次に、truffleテストですべてを実行してみましょう。Truffle は、JavaScript と Solidity の両方のテスト ファイルをピックアップして、両方の世界のベストを提供します。

トリュフ検定合格

続きがあります...