1

disclaimer: this is a duplicate from https://stackoverflow.com/questions/71126128/ I couldn't find out how to move the question, so I copied it here.

Issue description: Brownie tests containing either

pytest.raises(exceptions.VirtualMachineError)

or

brownie.reverts()

producing an error, when the request by design fails (is reverted).

Repository on Github for testing GitHub brownie.reverts() example code

Expected behavior: The test should either fail or succeed, depending on the outcome of the transaction.

Noticeable: When the test is set up in a way, that the transaction is not reverted, the test works correctly without producing an error.

What I have tried so far: I reduced the code to remove all unnecessary dependencies to contain only the problematic parts. After a complete new setup of a python virtual environment with only the necessary features (described below under setup), I ran the code again, to make sure the issue prevails and is not solved.

Source code of 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);
    }
}

Exhibit A Source code of 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})

Exhibit B Alternative making use of 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, which is correctly working:

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]})

With the result:

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

Complete terminal output for exhibits A and 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>

My setting: The root directory contains the .venv virtual python environment. Under venv Cython and Brownie are installed. In VSC I am using the latest solidity compiler. In order to install ganache I opened a powershell with admin rights and activated the virtual environment, installed nodeenv, and connected it with venv. After this step I installed ganache and tested it. Everything is working correctly. Complete projects are tested and work correctly in this setup, besides tests with brownie.reverts().

Here in short the command line overview:

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

1 Answer 1

0

This is fixed in Brownie v1.18.1. However, you will need to install Python 3.9.10 in order to get the latest brownie. For this to work in a virtual environment you can't use venv. Here is a process that works for me:

  • install virtualenv on your standard Python version
  • download python 3.9.10 and install it without "add to path" into a dedicated directory e.g. $home/pythonversions
  • in your project directory create a virtual environment like so

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

  • start your virtual environment e.g. home>..venv\Scripts\activate.ps1

  • test if your python version is the desired one with python --version

  • Now install Cython to avoid another error.

  • Install nodeenv in order to install ganage

  • Activate with nodeenv -p (for this step you will need PowerShell with admin rights)

  • Install ganache with npm

  • Install eth-brownie with pip check if you got the latest version with

brownie --version

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.