恢复交易的布朗尼测试不适用于 pytest.raises() 或 brownie.reverts()
Brownie testing for reverted transactions does not work with pytest.raises() or brownie.reverts()
问题描述:
布朗尼测试包含
pytest.raises(exceptions.VirtualMachineError)
或
brownie.reverts()
当设计请求失败(被还原)时产生错误。
在 GitHub:
https://github.com/n4n0b1t3/brownie.reverts.example
预期行为:
测试应该失败或成功,具体取决于 t运行saction.
的结果
明显:
当以某种方式设置测试时,t运行saction 不会被还原,测试会正常工作而不会产生错误。
目前我尝试过的:
我减少了代码以删除所有不必要的依赖项以仅包含有问题的部分。在 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})
图表 C
不恢复 t运行saction 的替代方案,它可以正常工作:
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
INFORMATION: Es konnten keine Dateien mit dem angegebenen
Muster gefunden werden.
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():
fund_me = ExampleContract.deploy({"from": accounts[0]})
print(f"FundMe deployed to {fund_me.address}")
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
fund_me.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 -------------------------------------------------------------------------------------------------------
FundMe 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 才能获得最新的 brownie。
要使其在虚拟环境中工作,您不能使用 venv。
这是一个适合我的过程:
- 在你的标准 Python 版本上安装 virtualenv
- 下载 python 3.9.10 并在不“添加到路径”的情况下将其安装到专用目录中,例如$home/pythonversions
- 在你的项目目录中创建一个像这样的虚拟环境
python -m virtualenv -p="<path to the python executable >" <virtual_environment_directory>
启动您的虚拟环境,例如
首页>..venv\Scripts\activate.ps1
使用 python --version
测试您的 python 版本是否是所需版本
现在安装 Cython 以避免另一个错误。
安装nodeenv以安装ganage
使用 nodeenv -p
激活(对于此步骤,您需要具有管理员权限的 PowerShell)
使用 npm 安装 ganache
安装 eth-brownie 用 pip 检查你是否有最新版本
brownie --version
问题描述: 布朗尼测试包含
pytest.raises(exceptions.VirtualMachineError)
或
brownie.reverts()
当设计请求失败(被还原)时产生错误。
在 GitHub: https://github.com/n4n0b1t3/brownie.reverts.example
预期行为: 测试应该失败或成功,具体取决于 t运行saction.
的结果明显: 当以某种方式设置测试时,t运行saction 不会被还原,测试会正常工作而不会产生错误。
目前我尝试过的: 我减少了代码以删除所有不必要的依赖项以仅包含有问题的部分。在 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})
图表 C 不恢复 t运行saction 的替代方案,它可以正常工作:
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
INFORMATION: Es konnten keine Dateien mit dem angegebenen
Muster gefunden werden.
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():
fund_me = ExampleContract.deploy({"from": accounts[0]})
print(f"FundMe deployed to {fund_me.address}")
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
fund_me.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 -------------------------------------------------------------------------------------------------------
FundMe 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 才能获得最新的 brownie。 要使其在虚拟环境中工作,您不能使用 venv。 这是一个适合我的过程:
- 在你的标准 Python 版本上安装 virtualenv
- 下载 python 3.9.10 并在不“添加到路径”的情况下将其安装到专用目录中,例如$home/pythonversions
- 在你的项目目录中创建一个像这样的虚拟环境
python -m virtualenv -p="<path to the python executable >" <virtual_environment_directory>
启动您的虚拟环境,例如 首页>..venv\Scripts\activate.ps1
使用 python --version
测试您的 python 版本是否是所需版本现在安装 Cython 以避免另一个错误。
安装nodeenv以安装ganage
使用
nodeenv -p
激活(对于此步骤,您需要具有管理员权限的 PowerShell)使用 npm 安装 ganache
安装 eth-brownie 用 pip 检查你是否有最新版本
brownie --version