
【中編】Ethereum Pet Shop DAppの作成とテスト~コントラクト作成編~
2020.12.07
~中編~Ethereum Pet ShopでDApp作成とテスト
今回は、前回に引き続き、「Ethereum Pet Shopを使って DAppの作成からテスト」までを行ってみたいと思います!
前編では、環境構築を行っていきましたが、今回からは実際にコントラクトを作成していきたいと思います!
作成工程
- 環境設定
- Truffle Box を使って Truffle プロジェクトを作成
- コントラクト作成
- コントラクトをコンパイルしてデプロイ
- コントラクトのテスト
- UI 作成
- ブラウザ上の DApp でログインする
こちらの記事もオススメ!
コントラクトの作成
それでは、実際にコントラクトを作成していきましょう!
contract フォルダー内にファイルを作成
Solidity のソースファイルである contract フォルダー内に、「Adoption.sol」ファイルを作成します。
そして、以下のコードを記入していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | pragma solidity ^0.5.0; contract Adoption { address[16] public adopters; // adopter変数のIDの領域 function adopt(uint petId) public returns (uint) { require(petId >= 0 && petId <= 15); adopters[petId] = msg.sender; return petId; } // getAdopters( )関数で設定したadopters変数の戻り値 function getAdopters() public view returns (address[16] memory) { return adopters; } } |
コード解説
PetShop のコントラクト作成は、web3 と連携するため、以下のような仕様になります。
- function で public の adopters 変数と petID (引数)を設定
- 引き渡しは address 型(戻り値を修正しない view で宣言)
- MetaMask と Ganache に設定したアドレスで開く
コントラクトのコンパイルとデプロイ
EVM の実行のため、コントラクトをコンパイルしてバイトコードに変換します。

DApp が含まれているディレクトリーで、ターミナルに以下のコマンドを入力します。
1 | truffle compile |
マイグレーションファイルの作成
マイグレーションファイルが、コントラクトを EVM へデプロイしてくれます。
migration フォルダーの中に、「1_initial_migration.js」ファイルがあります。
これが「migration.sol」の操作ファイルで、コントラクトが二重にコンパイルされるのを防ぎます。
「2_deploy_contract.js」ファイルを作成
migration フォルダーに、「2_deploy_contract.js」というファイルを作成します。
そして、以下のコードを書き込みます。
1 2 3 4 5 | var Adoption = artifacts.require("Adoption"); module.exports = function(deployer) { deployer.deploy(Adoption); }; |
コントラクトのデプロイ
ターミナルで以下のコマンドを入力し、デプロイします。
1 2 | truffle develop truffle(develop)>migrate |
コントラクトのテスト
それでは、コントラクトをテストしてみましょう!
Ganache アカウントの作成
Ganache の workspace に「truffle-config.js」をアップロードします。

Gas の設定
アカウント作成後は、全ての Blockchain に、テスト用として100Ether ずつ入っています。
この Ether は、デプロイする度に Gas(送金料)が消費されます。
Gas と RPC Server の設定は、以下のようになります。

コントラクトのテスト
PetShop 解凍時に、空のテスト用フォルダーが生成されています。
このフォルダーの中に「TestAdoption.sol」を作成し、以下のコードを記入します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | pragma solidity ^0.5.0; //上記2つのコントラクトは外部のグローバルフォルダーからインポートします import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/Adoption.sol"; contract TestAdoption { // TestAdoption関数にadoptiin変数でデプロイされたアドレスを読み込む Adoption adoption = Adoption(DeployedAddresses.Adoption()); // テストに使われるペットのID uint expectedPetId = 8; //アドレス型のexpectedAdopter変数の戻り値 address expectedAdopter = address(this); } |
最初にインポートしている3つのファイルの内、「Assert.sol」と「DeployedAddresses.sol」はグローバルのTruffleファイルなので、プロジェクトフォルダーの中には有りません。
この2つのファイルの役割は、以下のようになります。
Assert.sol
テスト時の確認作業を行います。
テストから成功/失敗を返すために、「同等(EQ)」「不等(NE)」などをチェックします。
DeployedAddresses.sol
テストを実行すると、デプロイされたコントラクトのアドレスを取得します。
関数設定
このコードには、以下の3つの wide variables (変数)が含まれています。
- DeployedAddresses
- expectedPetID
- expectedAdopter
uint型「expectedPetID」の引数8の戻り値が、address型「expectedAdopter」に返されます。
adopt()関数のコード修正
テストのため、adopt()関数のコードを修正します。
仕様は以下のようになります。
- expectedPetID を事前に宣言
- テストが成功したら、Assert.equal()に ID を返す
- 失敗したら、コンソールに「Adoption of the expected pet should match what is returned.」を表示
「TestAdoption.sol」コントラクトの「Adoption」変数の後に、以下のコードを追加してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | pragma solidity ^0.5.0; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/Adoption.sol"; contract TestAdoption { // adoption変数のアドレスの戻り値をテスト Adoption adoption = Adoption(DeployedAddresses.Adoption()); // adopt() 関数のテスト function testUserCanAdoptPet() public { uint returnedId = adoption.adopt(expectedPetId); Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned."); }</span> // テストに使われるペットのID uint expectedPetId = 8; // アドレス型のexpectedAdopter変数の戻り値 address expectedAdopter = address(this); } |
単一ペットオーナー受け入れ確認テスト
まず、単一ペットオーナー受け入れ確認テストをします。
public 変数は、自動ゲッターメソッドなので、上記の Adoption Test で格納したアドレスをそのまま取得できます。
また、保存されたデータはテスト中は存続するため、expectedPetID の値は他のテストでも取得可能です。
TestAdoption.sol をコード修正
「TestAdoption.sol」コントラクトを、以下のコードに修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ragma solidity ^0.5.0; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/Adoption.sol"; contract TestAdoption { // TestAdoption関数内でadoption変数で値を読み込む Adoption adoption = Adoption(DeployedAddresses.Adoption()); // testUserCanaAdoptPet()関数内でadopt()変数の値returnedID,expectedPetIdを検索して等価の場合は値を返し、不等価の場合はメッセージを返す。 function testUserCanAdoptPet() public { uint returnedId = adoption.adopt(expectedPetId); Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned."); } <span style="color: #ff0000;" data-mce-style="color: #ff0000;"> // ペットオーナーが同一のデータ検索テスト function testGetAdopterAddressByPetId() public { address adopter = adoption.adopters(expectedPetId); Assert.equal(adopter, expectedAdopter, "Owner of the expected pet should be this contract"); } </span> // テストに使われるペットのID uint expectedPetId = 8; //アドレス型のexpectedAdopter変数の戻り値 address expectedAdopter = address(this); } |
全てのペットオーナーの受け入れ確認テスト
続いて、すべてのペットオーナーの受け入れ確認テストをします。
配列は、単一キーを指定すると単一の値しか返せません。
そのため、配列全体に対して独自のゲッターを作成しましょう。
TestAdoption.sol をコード修正
以下のコードを「TestAdoption.sol」に追加し、修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | pragma solidity ^0.5.0; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/Adoption.sol"; contract TestAdoption { // テストされる adoptionコントラクトのアドレス Adoption adoption = Adoption(DeployedAddresses.Adoption()); // adopt() 関数をテストする function testUserCanAdoptPet() public { uint returnedId = adoption.adopt(expectedPetId); Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned."); } // ペットオーナーが同一のデータ検索のテスト function testGetAdopterAddressByPetId() public { address adopter = adoption.adopters(expectedPetId); Assert.equal(adopter, expectedAdopter, "Owner of the expected pet should be this contract"); } //全てのペットオーナーの検索テスト function testGetAdopterAddressByPetIdInArray() public { // メモリー内のadopter変数に値を格納する address[16] memory adopters = adoption.getAdopters(); Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract"); } </span> // テストに使われるペットのID uint expectedPetId = 8; //アドレス型のexpectedAdopter変数の戻り値 address expectedAdopter = address(this); } |
メモリー属性に注意!
ここで、「adopter 変数のメモリー属性」に注意してください。
メモリー属性は、値をコントラクトのストレージに保存するのではなく、メモリーに一時保存するよう Solidity に指示します。
adopter 変数は配列であり、最初の adoption test から、「expectedPetID」が存続しています。
そのため、「testGetAdopterAddressByPetIdInArray()」関数で、配列内の「expectedPetID」と「expectedAdopter」の戻り値を比較して検索します。
テストを実行
以下のコードを、ターミナルで入力します。
1 | truffle test |
テスト終了後の画面

以上で、ローカルのブロックチェーンにコントラクトを作成・展開し、コンソールで操作できるようになりました!
おすすめの本
後編へ続く
後編では、UI を作成していきたいと思います!
お楽しみに!
(株)ライトコードは、WEB・アプリ・ゲーム開発に強い、「好きを仕事にするエンジニア集団」です。
ブロックチェーンでのシステム開発依頼・お見積もりはこちらまでお願いします。
また、ブロックチェーンエンジニアを積極採用中です!詳しくはこちらをご覧ください。
※現在、多数のお問合せを頂いており、返信に、多少お時間を頂く場合がございます。
こちらの記事もオススメ!
後編はこちら
ライトコードよりお知らせ






一緒に働いてくれる仲間を募集しております!
ライトコードでは、仲間を募集しております!
当社のモットーは「好きなことを仕事にするエンジニア集団」「エンジニアによるエンジニアのための会社」。エンジニアであるあなたの「やってみたいこと」を全力で応援する会社です。
また、ライトコードは現在、急成長中!だからこそ、あなたにお任せしたいやりがいのあるお仕事は沢山あります。「コアメンバー」として活躍してくれる、あなたからのご応募をお待ちしております!
なお、ご応募の前に、「話しだけ聞いてみたい」「社内の雰囲気を知りたい」という方はこちらをご覧ください。
ライトコードでは一緒に働いていただける方を募集しております!
採用情報はこちら書いた人はこんな人

IT技術2021.01.11React Hooks登場でコンポーネントはどう変わった?
IT技術2021.01.05【Unity】Rigidbodyの基本
IT技術2021.01.04【Unity】ARkit3を使ったARアプリを開発する方法を解説(AR foundation,iOS)
IT技術2020.12.29【機械学習】単純なアルゴリズムで迷惑メールを分類してみた