Truffle

用於執行和編寫測試的 Truffle 替代品

  • March 6, 2020

Truffle 為乙太坊智能合約編寫測試提供了許多便利。好處包括不需要像 Ganache 這樣的單獨鏈流程,連結和部署庫合約的複雜自動化,如 SafeMath。但是,控制是逆向的,必須使用 truffle 命令執行測試,並且必須遵循 Mocha/Chai 模式。

有沒有鬆露的替代品,特別是

  • 將使用像 Jest 這樣的現代和標準測試執行器,而不是自定義包裝器命令
  • 與 TypeScript 一起玩得很好
  • 輕鬆設置和拆除程序內區塊鏈
  • 支持自動編譯、連結和部署複雜合約,如 SafeMath 庫

我為 Jest 找到了這個例子,但它是未完成的項目Ganache 和 Jest 還有一個,但非常簡化,不支持連結合約

我發現openzeppelin-test-environment解決了我的問題。它允許臨時乙太坊區塊鏈設置、合約部署等相對容易。

下面是我原來的 Truffle + TypeScript 測試翻譯成 OpenZeppelin + Jest + power-assert。

有一個測試

import assert = require('assert');

import { accounts, contract } from '@openzeppelin/test-environment';

import {
 BN,           // Big Number support
 constants,    // Common constants, like the zero address and largest integers
} from '@openzeppelin/test-helpers';

// https://etherscan.io/address/0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7#readContract
const TOKEN_1ST_TOTAL_SUPPLY = new BN('93468683899196345527500000');

// Ethereum accounts used in these tests
const [
 deployer,  // Deploys the smart contract
 owner, // Owns the initial supply
 user2 // Random dude who wants play with tokens
] = accounts;

// Loads a compiled contract using OpenZeppelin test-environment
const Dawn = contract.fromArtifact('Dawn');

beforeEach(() => {
 // No setup
});

afterEach(() => {
 // No setup
});

test('The supply should match original token', async () => {
 const token = await Dawn.new(owner, { from: deployer });
 const supply = await token.totalSupply();

 // Big number does not have power-assert support yet - https://github.com/power-assert-js/power-assert/issues/124
 assert(supply.toString() == TOKEN_1ST_TOTAL_SUPPLY.toString());
});

test("Token should allow transfer", async () => {
 const token = await Dawn.new(owner, { from: deployer });
 const amount = new BN("1") * new BN("1e18");  // Transfer 1 whole token
 await token.transfer(user2, amount, { from: owner });
 const balanceAfter = await token.balanceOf(user2);
 assert(balanceAfter.toString() == amount.toString());
});

test("Token tranfers are disabled after pause", async () => {
 const token = await Dawn.new(owner, { from: deployer });
 const amount = new BN("1") * new BN("1e18");  // Transfer 1 whole token
 // Pause
 await token.pause({ from: owner });
 assert(await token.paused());
 // Transfer tokens fails after the pause
 assert.rejects(async () => {
   await token.transfer(user2, amount, { from: owner });
 });
});


test("Token tranfers can be paused by the owner only", async () => {
 const token = await Dawn.new(owner, { from: deployer });
 const amount = new BN("1") * new BN("1e18");  // Transfer 1 whole token
 // Transfer tokens fails after the pause
 assert.rejects(async () => {
   await token.pause({ from: user2 });
 });
});

test("Token cannot be send to 0x0 null address by accident", async () => {
 const token = await Dawn.new(owner, { from: deployer });
 const amount = new BN("1") * new BN("1e18");  // Transfer 1 whole token
 assert.rejects(async () => {
   await token.transfer(constants.ZERO_ADDRESS, amount, { from: owner });
 });
});

原始松露測試(Mocha + power-assert)

const Dawn = artifacts.require("Dawn");

var assert = require('assert'); // Power assert https://github.com/power-assert-js/espower-typescript

// https://etherscan.io/address/0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7#readContract
const TOKEN_1ST_TOTAL_SUPPLY = web3.utils.toBN('93468683899196345527500000');

contract("Dawn", ([deployer, user1, user2]) => {

 const tokenOwner = user1;

 it("should have total supply of 1ST token after deploy", async () => {

   const dawn = await Dawn.new(tokenOwner, { from: deployer });
   const supply = await dawn.totalSupply();

   assert(supply.toString() == TOKEN_1ST_TOTAL_SUPPLY.toString());
 });

 it("should allow transfer", async () => {

   const dawn = await Dawn.new(tokenOwner, { from: deployer });
   const amount = web3.utils.toWei("1", "ether"); // 1 full token

   // Transfer tokens
   await dawn.transfer(user2, amount, { from: tokenOwner });
   const balanceAfter = await dawn.balanceOf(user2);
   assert(balanceAfter.toString() == amount.toString());
 });

 it("should not allow transfers after pause", async () => {

   const dawn = await Dawn.new(tokenOwner, { from: deployer });
   const amount = web3.utils.toWei("1", "ether"); // 1 full token

   // Pause
   await dawn.pause({ from: tokenOwner });
   assert(await dawn.paused());

   // Transfer tokens fails
   assert.rejects(async () => {
     await dawn.transfer(user2, amount, { from: user1 });
   });
 });

 it("should not allow pause by a random person", async () => {

   const dawn = await Dawn.new(tokenOwner, { from: deployer });

   // Transfer tokens fails
   assert.rejects(async () => {
     await dawn.pause({ from: user2 });
   });
 });


});

我的 package.json

{
 "name": "dawntoken",
 "version": "1.0.0",
 "description": "",
 "main": "truffle.js",
 "directories": {
   "test": "test"
 },
 "scripts": {
   "generate": "truffle compile && typechain --target truffle './build/**/*.json'",
   "test": "jest",
   "tsc": "tsc --noEmit"
 },
 "author": "",
 "license": "ISC",
 "devDependencies": {
   "@openzeppelin/test-environment": "^0.1.3",
   "@openzeppelin/test-helpers": "^0.5.4",
   "@types/jest": "^25.1.3",
   "@types/power-assert": "^1.5.3",
   "babel-jest": "^25.1.0",
   "babel-preset-power-assert": "^3.0.0",
   "espower-typescript": "^9.0.2",
   "jest": "^25.1.0",
   "power-assert": "^1.6.1",
   "ts-jest": "^25.2.1",
   "ts-node": "^8.6.2",
   "typescript": "^3.8.3",
   "babel-core": "^6.26.3"
 },
 "dependencies": {
   "@truffle/hdwallet-provider": "^1.0.32",
   "bignumber.js": "^9.0.0",
   "dotenv": "^8.2.0",
   "ganache-core": "^2.10.2",
   "openzeppelin-solidity": "^2.5.0",
   "solc": "^0.6.3",
   "truffle": "^5.1.16",
   "web3": "^1.2.6"
 },
 "jest": {
   "verbose": true,
   "preset": "ts-jest",
   "testMatch": ["**/tests/*.ts"],
   "testEnvironment": "node",
   "globals": {
     "ts-jest": {
       "babelConfig": {
         "presets": [
           "power-assert"
         ]
       }
     }
   }
 }
}

有關如何設置 Jest + TypeScript + power-assert 的更多資訊,請查看此處

引用自:https://ethereum.stackexchange.com/questions/80364