0

I have a pytest function as such:

def test_zork1_serial_number_error(zork1_unicode_error_serial):
    "handles a serial code with a unicode error"
    with pytest.raises(UnicodeDecodeError) as execinfo:
        serial_code = zork1_unicode_error_serial.serial_code

    assert serial_code == "XXXXXX"

The code that this hits is:

@property
def serial_code(self) -> str:
    code_bytes = bytes(self.view[0x12:0x18])

    try:
        if code_bytes.count(b"\x00"):
            print("111111111111")
            return "XXXXXX"

        return code_bytes.decode("ascii")
    except UnicodeDecodeError:
        print("222222222222")
        return "XXXXXX"

The print statements were just there for me to validate that the appropriate path was being hit. When I run the test I get this:

zork1_unicode_error_serial = <zmachine.header.Header object at 0x10e320d60>

    def test_zork1_serial_number_error(zork1_unicode_error_serial):
        "handles a serial code with a unicode error"
        with pytest.raises(UnicodeDecodeError) as execinfo:
            serial_code = zork1_unicode_error_serial.serial_code
>           assert serial_code == "XXXXXX"
E           Failed: DID NOT RAISE <class 'UnicodeDecodeError'>

tests/header_test.py:42: Failed
------------------------------------------------------------------------------ Captured stdout setup ------------------------------------------------------------------------------
/Users/jnyman/AppDev/quendor/tests/../zcode/zork1-r15-sXXXXXX.z2
------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------
222222222222

Notice how the "222222222222" is captured in the standard output, thus the appropriate path is being hit and thus the exception is also clearly being generated. Yet Pytest is saying that this exception was not raised. (I have also tested this code manually as well to make sure the exception is being generated.)

I've also tried the path of instead "marking" the test as such, like this:

@pytest.mark.xfail(raises=UnicodeDecodeError)
def test_zork1_serial_number_error(zork1_unicode_error_serial):
    ...

And that passes. However, it also passes regardless of what exception I put in there. For example, if I do @pytest.mark.xfail(raises=IndexError) that also passes even though an IndexError is never raised.

I can't tell if this has something to do with the fact that what I'm testing is a property. Again, as can seen from the captured standard output, the appropriate code path is being executed and the exception is most definitely being raised. But perhaps the fact that my function is a property is causing an issue?

I have read this Python - test a property throws exception but that isn't using Pytest and it's unclear to me how to retrofit the thinking there. I also aware that perhaps throwing an exception in a property is not a good thing (referencing this: By design should a property getter ever throw an exception in python?) so maybe this test problem is pointing to a code smell. But I don't see an immediate to make this better without adding extra complication. And that still wouldn't explain why Pytest is not seeing the exception generated when it clearly is being generated.

Jeff Nyman
  • 870
  • 2
  • 12
  • 31
  • 2
    You are catching the exception, so the exception is not raised as viewed from the outside of the property – FlorianGD Jun 15 '20 at 11:15
  • Instead of the exception (which is caught, as mentioned by @FlorianGD) you should test for the correct exception handling (in your code the returned value, and may be the log). – MrBean Bremen Jun 15 '20 at 12:36
  • This all makes sense. I guess the trick is that sometimes the exception not raised and other times it is. So I wanted to to test those two conditions. The returned value will be the same in either case, so the only distinguishing aspect is whether an exception had to be caught and handled. Which is definitely what I would want to test for. – Jeff Nyman Jun 15 '20 at 13:53

0 Answers0