Solidity

用於恢復交易的布朗尼測試不適用於 pytest.raises() 或 brownie.reverts()

  • February 19, 2022

免責聲明:這是來自https://stackoverflow.com/questions/71126128/的副本我不知道如何移動問題,所以我在這裡複製了它。

問題描述: 布朗尼測試包含

pytest.raises(exceptions.VirtualMachineError)

或者

brownie.reverts()

當設計請求失敗(被還原)時產生錯誤。

Github 上用於測試 GitHub brownie.reverts() 範常式式碼的儲存庫

預期行為: 測試應該失敗或成功,具體取決於事務的結果。

值得注意的是: 當測試以某種方式設置時,事務未還原,測試正常工作而不會產生錯誤。

到目前為止我所嘗試的: 我減少了程式碼以刪除所有不必要的依賴項,只包含有問題的部分。在僅具有必要功能的python虛擬環境的全新設置(在下面的設置中描述)之後,我再次執行程式碼,以確保問題存在並且沒有解決。

projectroot/sourcecode/contracts/ExampleContract.sol 的原始碼

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.6 <0.9.0;

contract ExampleContract {
   address public owner;

   constructor() {
       owner = msg.sender;
   }

   modifier onlyOwner() {
       require(msg.sender == owner, "only owner");
       _;
   }

   function withdraw() public payable onlyOwner {
       payable(msg.sender).transfer(address(this).balance);
   }
}

圖表 A projectroot/sourcecode/test/test_reverts.py 的原始碼

import brownie
from brownie import ExampleContract, accounts, exceptions


def test_only_owner_can_withdraw():
   example_contract = ExampleContract.deploy({"from": accounts[0]})
   bad_account = accounts.add()
   with brownie.reverts():
       example_contract.withdraw({"from": bad_account})

圖表 B 使用 pytest.raises() 的替代方案

import brownie
import pytest
from brownie import ExampleContract, accounts, exceptions

def test_only_owner_can_withdraw():
   example_contract = ExampleContract.deploy({"from": accounts[0]})
   bad_account = accounts.add()
   with pytest.raises(exceptions.VirtualMachineError):
       example_contract.withdraw({"from": bad_account})

Exhibit C Alternative with not reverting transaction,正常工作:

import brownie
from brownie import ExampleContract, accounts, exceptions

def test_only_owner_can_withdraw():
   example_contract = ExampleContract.deploy({"from": accounts[0]})
   bad_account = accounts.add()
   with brownie.reverts():
       example_contract.withdraw({"from": accounts[0]})

結果:

>raise AssertionError("Transaction did not revert")
E AssertionError: Transaction did not revert

展品 A 和 B 的完整終端輸出

(.venv) testcode>brownie test
Brownie v1.16.4 - Python development framework for Ethereum

======================================================================================================= test session starts =======================================================================================================
platform win32 -- Python 3.10.2, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: C:\Users\Zarathustra\blockchainProjects\testing.revert\testcode
plugins: eth-brownie-1.16.4, hypothesis-6.21.6, forked-1.3.0, xdist-1.34.0, web3-5.23.1
collected 1 item

Launching 'ganache-cli.cmd --accounts 10 --hardfork istanbul --gasLimit 12000000 --mnemonic brownie --port 8545'...

tests\test_reverts.py F                                                                                                                                                                                                      [100%]

============================================================================================================ FAILURES =============================================================================================================
__________________________________________________________________________________________________ test_only_owner_can_withdraw ___________________________________________________________________________________________________

   def test_only_owner_can_withdraw():
       example_contract = ExampleContract.deploy({"from": accounts[0]})
       good = accounts[0]
       bad = accounts.add()
       # with pytest.raises(exceptions.VirtualMachineError):
>       with brownie.reverts():

tests\test_reverts.py:12:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests\test_reverts.py:13: in test_only_owner_can_withdraw
   example_contract.withdraw({"from": bad})
..\.venv\lib\site-packages\brownie\network\contract.py:1693: in __call__
   return self.transact(*args)
..\.venv\lib\site-packages\brownie\network\contract.py:1566: in transact
   return tx["from"].transfer(
..\.venv\lib\site-packages\brownie\network\account.py:680: in transfer
   receipt._raise_if_reverted(exc)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <Transaction '0x1f310675db8ad41f0eb4cd9338f35f2800174168b18c019645b5b1d82ade2826'>, exc = None

   def _raise_if_reverted(self, exc: Any) -> None:
       if self.status or CONFIG.mode == "console":
           return
       if not web3.supports_traces:
           # if traces are not available, do not attempt to determine the revert reason
           raise exc or ValueError("Execution reverted")

       if self._dev_revert_msg is None:
           # no revert message and unable to check dev string - have to get trace
           self._expand_trace()
       if self.contract_address:
           source = ""
       elif CONFIG.argv["revert"]:
           source = self._traceback_string()
       else:
           source = self._error_string(1)
           contract = state._find_contract(self.receiver)
           if contract:
               marker = "//" if contract._build["language"] == "Solidity" else "#"
               line = self._traceback_string().split("\n")[-1]
               if marker + " dev: " in line:
                   self._dev_revert_msg = line[line.index(marker) + len(marker) : -5].strip()

>       raise exc._with_attr(
           source=source, revert_msg=self._revert_msg, dev_revert_msg=self._dev_revert_msg
       )
E       AttributeError: 'NoneType' object has no attribute '_with_attr'

..\.venv\lib\site-packages\brownie\network\transaction.py:420: AttributeError
------------------------------------------------------------------------------------------------------ Captured stdout call ------------------------------------------------------------------------------------------------------- 
ExampleContract deployed to 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87
mnemonic: 'abandon wisdom slot exclude buyer raccoon desert grid inmate flag state castle'
===================================================================================================== short test summary info ===================================================================================================== 
FAILED tests/test_reverts.py::test_only_owner_can_withdraw - AttributeError: 'NoneType' object has no attribute '_with_attr'
======================================================================================================== 1 failed in 6.40s ======================================================================================================== 
Terminating local RPC client...
(.venv) testcode>

我的設置: 根目錄包含 .venv 虛擬 python 環境。在 venv 下安裝了 Cython 和 Brownie。在 VSC 中,我使用的是最新的solidity 編譯器。為了安裝 ganache,我打開了一個具有管理員權限的 powershell 並啟動了虛擬環境,安裝了 nodeenv,並將其與 venv 連接。在這一步之後,我安裝了 ganache 並對其進行了測試。一切正常。除了使用 brownie.reverts() 進行測試外,完整的項目在此設置中經過測試並正常工作。

簡而言之,命令行概述:

projectroot>.\.venv\Scripts\Activate.ps1
(.venv) projectroot>pip install nodeenv
(.venv) projectroot>nodeenv -p
(.venv) projectroot>npm install -g ganache
(.venv) projectroot>ganache --version
(.venv) projectroot>mkdir sourcecode
(.venv) projectroot>cd sourcecode
(.venv) sourcecode>brownie init
(.venv) sourcecode>brownie --version

這是在 Brownie v1.18.1 中修復的。但是,您需要安裝 Python 3.9.10 才能獲得最新的巧克力蛋糕。為此,您不能在虛擬環境中使​​用 venv。這是一個對我有用的過程:

  • 在標準 Python 版本上安裝 virtualenv
  • 下載 python 3.9.10 並在沒有“添加到路徑”的情況下將其安裝到專用目錄中,例如 $home/pythonversions
  • 在您的項目目錄中創建一個像這樣的虛擬環境

python -m virtualenv -p="<path to the python executable >" <virtual_environment_directory>

  • 啟動您的虛擬環境,例如 home>..venv\Scripts\activate.ps1
  • 使用 python –version 測試您的 python 版本是否是所需的版本
  • 現在安裝 Cython 以避免另一個錯誤。
  • 安裝 nodeenv 以安裝 ganage
  • 啟動nodeenv -p(對於此步驟,您需要具有管理員權限的 PowerShell)
  • 使用 npm 安裝 ganache
  • 使用 pip 安裝 eth-brownie 檢查您是否獲得了最新版本

brownie --version

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