用於恢復交易的布朗尼測試不適用於 pytest.raises() 或 brownie.reverts()
免責聲明:這是來自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