
【中編】Ethereum Pet Shop DAppの作成とテスト~コントラクト作成編~
2021.12.20
~中編~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 を作成していきたいと思います!
お楽しみに!
こちらの記事もオススメ!
後編はこちら
書いた人はこんな人

- 「好きを仕事にするエンジニア集団」の(株)ライトコードです!
ライトコードは、福岡、東京、大阪の3拠点で事業展開するIT企業です。
現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。
いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。
システム開発依頼・お見積もり大歓迎!
また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」「WEBディレクター」を積極採用中です!
インターンや新卒採用も行っております。
以下よりご応募をお待ちしております!
https://rightcode.co.jp/recruit
ライトコードの日常12月 1, 2023ライトコードクエスト〜東京オフィス歴史編〜
ITエンタメ10月 13, 2023Netflixの成功はレコメンドエンジン?
ライトコードの日常8月 30, 2023退職者の最終出社日に密着してみた!
ITエンタメ8月 3, 2023世界初の量産型ポータブルコンピュータを開発したのに倒産!?アダム・オズボーン