我想为我的代码编写可维护和可读的单元测试。要求:
问:有人能告诉我如何使用单元测试框架的内置特性正确地完成这个任务吗?例如在混合测试、松露测试或任何其他框架中。
这是我想要编写的测试代码的一个例子:
contract BankTests {
function test() public {
Bank bank = new Bank(); // a bank that only allows two customers
TestActor customer1 = new TestActor{value: 1 ether}(); // has 1 ether
TestActor customer2 = new TestActor{value: 1 ether}(); // has 1 ether
TestActor customer3 = new TestActor{value: 1 ether}(); // has 1 ether
switchToActor(bank, customer1); // from now on customer1 interacts with the bank
Assert.equal(bank.balance(), 0);
bank.deposit{value: 0.5 ether}();
Assert.equal(bank.balance(), 0.5 ether);
bank.deposit{value: 0.25 ether}();
Assert.equal(bank.balance(), 0.75 ether);
switchToActor(bank, customer2); // from now on customer2 interacts with the bank
Assert.equal(bank.balance(), 0);
bank.deposit{value: 0.1 ether}();
Assert.equal(bank.balance(), 0.1 ether);
switchToActor(bank, customer3); // from now on customer3 interacts with the bank
try bank.deposit{value: 0.5 ether}() {
Assert.equal(true, false); // must not succeed; bank only allows 2 customers
} catch Error(string memory reason) {
Assert.equal(reason, "Bank already has two customers.");
} catch {
Assert.equal(true, false); // wrong error; we expect a reason
}
resetActor(); // no current actor anymore
Assert.equal(address(customer1).balance, 0.25 ether);
Assert.equal(address(customer2).balance, 0.9 ether);
Assert.equal(address(customer3).balance, 1 ether);
}
}我没有设法达到我的三个要求与混合测试和松露测试与他们的内置功能。所以我写了我自己的框架,在混合测试和松露测试中运行。如果有更好的方法,我很乐意再次删除这些代码。这里是我的方法的一个完整例子:
// The contract we want to test (made up for this question):
contract Bank {
mapping(address => uint256) internal customers;
uint8 internal customerCount;
function deposit() public payable {
require(msg.value > 0, "No money provided.");
require(customers[msg.sender] > 0 || customerCount < 2,
"Bank already has two customers.");
customers[msg.sender] += msg.value;
if(customers[msg.sender] == msg.value) {
customerCount++;
}
}
function balance() public view returns (uint256) {
return customers[msg.sender];
}
}
// One unit test:
// initialBalance and #value to get 3 ether from remix-tests or truffle-test.
contract BankTests is TestContract {
uint64 public initialBalance = 3 ether;
/// #value: 3000000000000000000
function test() public payable {
TestBank bank = new TestBank();
TestActor customer1 = new TestActor{value: 1 ether}();
TestActor customer2 = new TestActor{value: 1 ether}();
TestActor customer3 = new TestActor{value: 1 ether}();
switchToActor(bank, customer1);
Assert.equal(bank.balance(), 0, "1a failed");
bank.deposit{value: 0.5 ether}();
Assert.equal(bank.balance(), 0.5 ether, "1b failed");
bank.deposit{value: 0.25 ether}();
Assert.equal(bank.balance(), 0.75 ether, "1c failed");
switchToActor(bank, customer2);
Assert.equal(bank.balance(), 0, "2a failed");
bank.deposit{value: 0.1 ether}();
Assert.equal(bank.balance(), 0.1 ether, "2b failed");
switchToActor(bank, customer3);
try bank.deposit{value: 0.5 ether}() {
Assert.equal(true, false, "3a failed");
} catch Error(string memory reason) {
Assert.equal(
reason,
"Bank already has two customers.",
"3b failed"
);
} catch {
Assert.equal(true, false, "3c failed");
}
resetActor(bank);
Assert.equal(address(customer1).balance, 0.25 ether, "4a failed");
Assert.equal(address(customer2).balance, 0.9 ether, "4b failed");
Assert.equal(address(customer3).balance, 1 ether, "4c failed");
}
}
// Base class of test contracts to enable actor switching:
contract TestContract {
function switchToActor(TestBank bank, TestActor actor) internal {
bank.switchToActor{value: address(this).balance}(actor);
}
function resetActor(TestBank bank) internal {
bank.resetActor{value: address(this).balance}();
}
receive() external payable {} // accept payments
}
// An actor contract to hold money and interact with other contracts:
contract TestActor {
constructor() payable {}
function sendAllMoneyTo(address payable recipient) public {
recipient.transfer(address(this).balance);
}
receive() external payable {} // accept payments
}
// Subclass of bank contract to enable actor switching:
contract TestBank is Bank {
address payable private currentActualCustomerAddr;
function switchToActor(TestActor actualCustomer) public payable {
resetActor();
address payable proxyCustomerAddr = payable(msg.sender);
address payable actualCustomerAddr = payable(address(actualCustomer));
customers[proxyCustomerAddr] = customers[actualCustomerAddr];
actualCustomer.sendAllMoneyTo(proxyCustomerAddr);
currentActualCustomerAddr = actualCustomerAddr;
}
function resetActor() public payable {
address payable proxyCustomerAddr = payable(msg.sender);
if(currentActualCustomerAddr != address(0)) {
customers[currentActualCustomerAddr] = customers[proxyCustomerAddr];
currentActualCustomerAddr.transfer(msg.value);
}
currentActualCustomerAddr = payable(address(0));
}
}测试合同(BankTests)始终是客户与银行合同的实际交互。当请求切换到另一个参与者时,测试契约获得该参与者的余额,并且银行合同数据被修改,以便测试契约获得参与者状态。这不是很漂亮,但允许-- IMHO --编写可读和可维护的测试。
以这种方式编写多个测试,很快就会遇到“超出气体限制”或“内存不足”的错误:
总结一下:我建立了一个我希望没有必要的解决办法。我希望有人给我一个更好的方法来实现同样的目标。如果这是不可能的,那么我希望这个问题能够帮助处于相同情况下的人们更快地开始他们的测试框架。
发布于 2021-05-30 20:15:51
我对此还比较陌生,但从我所见的所有关于块菌和单元测试的教程来看,它们都倾向于使用Mocha和柴测试框架。我建议查看这两个库。网上有很多关于他们的教程。
看看DAPP大学的这篇教程。他很好地解释了如何使用库进行测试。(单元测试开始于47分钟左右)
https://youtu.be/CgXQC4dbGUE?t=2838
有几个在那里,所以可以随意使用另一个。在我看过的所有教程中,我都不记得曾经见过有人将单元测试直接放入可靠的智能契约中。通常,它们要么是javascript文件,要么是与智能契约交互的python文件。
通过将您的测试代码移到.js或.py文件中,您应该可以大大降低您的气体费用。(我认为这也有助于解决内存问题,但就像我说的那样,我对此非常陌生,从未见过有人试图在可靠的范围内执行单元测试。)
发布于 2022-07-28 11:50:38
我目前正在学习这个FreeCodeCamp教程,它有几个单元测试和分阶段测试的示例。他们用的是安全帽,柴族,以太,瓦夫勒和摩卡。强烈推荐。非常有效的教学。https://youtu.be/gyMwXuJrbJQ。
发布于 2022-09-22 05:16:57
对于Remix测试,您需要使用注释中的项来切换帐户并在测试函数中从它们发送eth。
/// #value: 1000000000000000000
/// #sender: account-2
function f() public payable {
contract.g{value:1000000000000000000}()
}https://ethereum.stackexchange.com/questions/99955
复制相似问题