Lompat ke konten utama

Cara membuat mock kontrak pintar Solidity untuk pengujian

Solidity
kontrak pintar
pengujian
mocking
Menengah
Markus Waas
2 Mei 2020
4 menit baca

Objek mock (opens in a new tab) adalah pola desain yang umum dalam pemrograman berorientasi objek. Berasal dari kata bahasa Prancis kuno 'mocquer' yang berarti 'mengolok-olok', kata ini berevolusi menjadi 'meniru sesuatu yang nyata' yang sebenarnya merupakan apa yang kita lakukan dalam pemrograman. Silakan mengolok-olok kontrak pintar Anda jika Anda mau, tetapi buatlah mock-nya kapan pun Anda bisa. Hal ini akan membuat hidup Anda lebih mudah.

Pengujian unit kontrak dengan mock

Membuat mock kontrak pada dasarnya berarti membuat versi kedua dari kontrak tersebut yang berperilaku sangat mirip dengan aslinya, tetapi dengan cara yang dapat dikontrol dengan mudah oleh pengembang. Anda sering kali berakhir dengan kontrak yang kompleks di mana Anda hanya ingin melakukan pengujian unit pada bagian kecil dari kontrak. Masalahnya adalah bagaimana jika pengujian bagian kecil ini memerlukan status kontrak yang sangat spesifik yang sulit untuk dicapai?

Anda dapat menulis logika penyiapan pengujian yang kompleks setiap kali yang membawa kontrak ke dalam status yang diperlukan atau Anda menulis mock. Membuat mock kontrak itu mudah dengan pewarisan (inheritance). Cukup buat kontrak mock kedua yang mewarisi dari kontrak aslinya. Sekarang Anda dapat menimpa (override) fungsi ke mock Anda. Mari kita lihat dengan sebuah contoh.

Contoh: ERC20 Privat

Kita menggunakan contoh kontrak ERC-20 yang memiliki waktu privat awal. Pemilik dapat mengelola pengguna privat dan hanya mereka yang akan diizinkan untuk menerima token pada awalnya. Setelah waktu tertentu berlalu, semua orang akan diizinkan untuk menggunakan token tersebut. Jika Anda penasaran, kita menggunakan hook _beforeTokenTransfer (opens in a new tab) dari kontrak OpenZeppelin v3 yang baru.

1pragma solidity ^0.6.0;
2
3import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4import "@openzeppelin/contracts/access/Ownable.sol";
5
6contract PrivateERC20 is ERC20, Ownable {
7 mapping (address => bool) public isPrivateUser;
8 uint256 private publicAfterTime;
9
10 constructor(uint256 privateERC20timeInSec) ERC20("PrivateERC20", "PRIV") public {
11 publicAfterTime = now + privateERC20timeInSec;
12 }
13
14 function addUser(address user) external onlyOwner {
15 isPrivateUser[user] = true;
16 }
17
18 function isPublic() public view returns (bool) {
19 return now >= publicAfterTime;
20 }
21
22 function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
23 super._beforeTokenTransfer(from, to, amount);
24
25 require(_validRecipient(to), "PrivateERC20: invalid recipient");
26 }
27
28 function _validRecipient(address to) private view returns (bool) {
29 if (isPublic()) {
30 return true;
31 }
32
33 return isPrivateUser[to];
34 }
35}
Tampilkan semua

Dan sekarang mari kita buat mock-nya.

1pragma solidity ^0.6.0;
2import "../PrivateERC20.sol";
3
4contract PrivateERC20Mock is PrivateERC20 {
5 bool isPublicConfig;
6
7 constructor() public PrivateERC20(0) {}
8
9 function setIsPublic(bool isPublic) external {
10 isPublicConfig = isPublic;
11 }
12
13 function isPublic() public view returns (bool) {
14 return isPublicConfig;
15 }
16}
Tampilkan semua

Anda akan mendapatkan salah satu pesan kesalahan berikut:

  • PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.
  • PrivateERC20.sol: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?.

Karena kita menggunakan versi Solidity 0.6 yang baru, kita harus menambahkan kata kunci virtual untuk fungsi yang dapat ditimpa dan override untuk fungsi yang menimpa. Jadi mari kita tambahkan keduanya ke kedua fungsi isPublic.

Sekarang dalam pengujian unit Anda, Anda dapat menggunakan PrivateERC20Mock sebagai gantinya. Ketika Anda ingin menguji perilaku selama waktu penggunaan privat, gunakan setIsPublic(false) dan demikian pula setIsPublic(true) untuk menguji waktu penggunaan publik. Tentu saja dalam contoh kita, kita juga bisa menggunakan pembantu waktu (time helpers) (opens in a new tab) untuk mengubah waktu yang sesuai. Tetapi gagasan tentang pembuatan mock seharusnya sudah jelas sekarang dan Anda dapat membayangkan skenario di mana hal itu tidak semudah sekadar memajukan waktu.

Membuat mock banyak kontrak

Ini bisa menjadi berantakan jika Anda harus membuat kontrak lain untuk setiap mock. Jika ini mengganggu Anda, Anda dapat melihat pustaka MockContract (opens in a new tab). Pustaka ini memungkinkan Anda untuk menimpa dan mengubah perilaku kontrak secara langsung (on-the-fly). Namun, ini hanya berfungsi untuk membuat mock panggilan ke kontrak lain, jadi ini tidak akan berfungsi untuk contoh kita.

Pembuatan mock bisa menjadi lebih kuat

Kekuatan pembuatan mock tidak berakhir di situ.

  • Menambahkan fungsi: Tidak hanya menimpa fungsi tertentu yang berguna, tetapi juga sekadar menambahkan fungsi tambahan. Contoh yang baik untuk token adalah memiliki fungsi mint tambahan untuk memungkinkan pengguna mana pun mendapatkan token baru secara gratis.
  • Penggunaan di testnet: Saat Anda menerapkan dan menguji kontrak Anda di testnet bersama dengan dapp Anda, pertimbangkan untuk menggunakan versi mock. Hindari menimpa fungsi kecuali Anda benar-benar harus melakukannya. Bagaimanapun juga, Anda ingin menguji logika yang sebenarnya. Tetapi menambahkan misalnya fungsi reset bisa berguna yang hanya mengatur ulang status kontrak ke awal, tidak diperlukan penerapan baru. Jelas Anda tidak ingin memiliki itu di kontrak mainnet.

Pembaruan terakhir halaman: 25 Agustus 2025

Apakah tutorial ini membantu?