Home > front end >  python unit test is not covering code inside the except block
python unit test is not covering code inside the except block

Time:09-17

From what I understand in test_main_version_exception unit test, the compare_version.version_match() function would raise an exception and ideally this exception should get caught by the except block. But for some reason, the flow is not going into except block. (1st one seems to be working fine)

Could someone please help me with this? Any suggestions to improve theses tests are also welcome.

main.py file

try:
  is_version_same = compare_version.version_match()
  if is_version_same:
    LOGGER.info("Upgrade not required")
    sys.exit()
except compare_version.VersionError as error: #custom error
    #### these two statements  are not getting covered when I run test_main_version_exception(mocker):
    LOGGER.exception("Exception occurred in compare_version: %s", error) 
    sys.exit(f"Exception occurred in compare_version: {error}")

UNIT TESTS:

def test_main_same_version(mocker):
    mocker.patch(
        "compare_version.version_match",
        return_value=True,
    )
    with pytest.raises(SystemExit) as ex:
        main.main()
    assert ex.type == SystemExit

def test_main_version_exception(mocker):
    mocker.patch(
        "compare_version.version_match",
        side_effect=VersionError,
    )
    with pytest.raises(VersionError) as ex:
        main.main()
    assert ex.type == VersionError

CodePudding user response:

You have to patch the compare_version used in main.py specifically via mocker.patch("main.compare_version.version_match", ...) regardless if it was:

  1. Imported into main.py:
    from some_file import compare_version
    
  2. Or defined within main.py:
    class compare_version:  # Sample implementation only as you haven't included this in the question
        version_match = lambda: None
        VersionError = Exception
    

As documented:

a.py
    -> Defines SomeClass

b.py
    -> from a import SomeClass
    -> some_function instantiates SomeClass

Now we want to test some_function but we want to mock out SomeClass using patch()... If we use patch() to mock out a.SomeClass then it will have no effect on our test; module b already has a reference to the real SomeClass and it looks like our patching had no effect.

The key is to patch out SomeClass where it is used (or where it is looked up). In this case some_function will actually look up SomeClass in module b, where we have imported it. The patching should look like:

@patch('b.SomeClass')

Also, your assertion with pytest.raises(VersionError) as ex: is incorrect because your implementation doesn't raise VersionError anymore, instead it catches that error and raises SystemExit too. So what you need to assert here is if it raises SystemExit. If what you really need is the VersionError, then replace this line:

sys.exit(f"Exception occurred in compare_version: {error}")

To:

raise  # Will re-raise the previous exception, which is the VersionError

Sample run you might want to use as reference.

main.py

import sys

# # Whether it is defined in another file
# from some_file import compare_version
# # Or it is defined here
class compare_version:
    version_match = lambda: None
    VersionError = ValueError


def main():
    try:
      is_version_same = compare_version.version_match()
      if is_version_same:
        print("Upgrade not required")
        sys.exit()
    except compare_version.VersionError as error: #custom error
        print("Exception occurred in compare_version: %s", error) 

        # If you want this block to raise SystemExit
        sys.exit(f"Exception occurred in compare_version: {error}")

        # If you want this block to re-raise VersionError
        # raise

test_main.py

import pytest

import main


def test_main_same_version(mocker):
    mocker.patch(
        "main.compare_version.version_match",
        return_value=True,
    )
    with pytest.raises(SystemExit) as ex:
        main.main()
    assert ex.type == SystemExit


def test_main_version_exception(mocker):
    mocker.patch(
        "main.compare_version.version_match",
        side_effect=main.compare_version.VersionError,
    )
    with pytest.raises(SystemExit) as ex:
        main.main()
    assert ex.type == SystemExit

Output

$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
_________________________________________________________________________________________ test_main_same_version __________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Upgrade not required
_______________________________________________________________________________________ test_main_version_exception _______________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Exception occurred in compare_version: %s 
2 passed in 0.04s
  • Related