Home > other >  Unit test for function raising different exception
Unit test for function raising different exception

Time:10-16

provided the following code

from typing import Union, Tuple
import unittest


def split_value_and_unit(inp_value: Union[str, int, float]) -> Tuple[float, str]:
    unit = ''
    value: float
    if (isinstance(inp_value, int) or isinstance(inp_value, float)):
        value = float(inp_value)
    else:
        # get rid of = sign at the beginning of string
        str_value = inp_value
        if (inp_value.startswith('=')):
            str_value = inp_value[1:]
        if ('*' in inp_value):
            sep_index = str_value.index('*')
            try:
                value = float(str_value[:sep_index])
            except ValueError:
                raise Exception(f'Expected a string containing a float number and a unit. "{str(inp_value)}" does not fulfil this requirement')
            unit = str_value[sep_index   1:]
        else:
            try:
                value = float(str_value)
            except ValueError:
                raise Exception(f'Expected a string containing a float number and a unit. "{str(inp_value)}" does not fulfil this requirement')
    return (value, unit)


class TestX(unittest.TestCase):
    def test_split_value_and_unit(self):
        # self.assertRaisesRegex(Exception, r'Expected a string containing a float number and a unit. "_0" does not fulfil this requirement', lambda: split_value_and_unit('_0'))
        self.assertRaisesRegex(Exception, 'Expected a string containing a float number and a unit. "_1 e16" does not fulfil this requirement', lambda: split_value_and_unit('_1 e16'))

I would have expected the unit test to pass. But running coverage run -m unittest -v test.x returns the following output:

test_split_value_and_unit (test.x.TestX) ... FAIL

======================================================================
FAIL: test_split_value_and_unit (test.x.TestX)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\Users\klosemic\Documents\SVN_wc\trunk\Tools\ng_scripts\orchid_ng\test\x.py", line 24, in split_value_and_unit
    value = float(str_value)
ValueError: could not convert string to float: '_1 e16'

During handling of the above exception, another exception occurred:

Exception: Expected a string containing a float number and a unit. "_1 e16" does not fulfil this requirement

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "c:\Users\klosemic\Documents\SVN_wc\trunk\Tools\ng_scripts\orchid_ng\test\x.py", line 33, in test_split_value_and_unit
    self.assertRaisesRegex(Exception, 'Expected a string containing a float number and a unit. "_1 e16" does not fulfil this requirement', lambda: split_value_and_unit('_1 e16'))
AssertionError: "Expected a string containing a float number and a unit. "_1 e16" does not fulfil this requirement" does not match "Expected a string containing a float number and a unit. "_1 e16" does not fulfil this requirement"

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

If I swap the two lines that test for the exception (so if I pass "_0" instead of "_1e 16") the test passes.
So my question is, why is "_1e 16" a different case than "_0" for the test case? Especially as I explicitly handle the ValueError in the function (if that is good style or not is not the point here).

Thanks in advance

CodePudding user response:

self.assertRaisesRegex takes a regex expression as a second argument. Since your regex expression contains a wildcard (.) and a quantifier ( ) your string no longer matches.

For more detail see https://regex101.com/r/GPwpTV/1 versus https://regex101.com/r/Z9O7tP/1 .

The regex expression you are looking for needs to escape those characters is below:

r'Expected a string containing a float number and a unit\. "_1\ e16" does not fulfil this requirement'

Without it, the quantifier will match 1 once and then have nothing to match the literal .

Full code included below:

from typing import Union, Tuple
import unittest


def split_value_and_unit(inp_value: Union[str, int, float]) -> Tuple[float, str]:
    unit = ''
    value: float
    if (isinstance(inp_value, int) or isinstance(inp_value, float)):
        value = float(inp_value)
    else:
        # get rid of = sign at the beginning of string
        str_value = inp_value
        if (inp_value.startswith('=')):
            str_value = inp_value[1:]
        if ('*' in inp_value):
            sep_index = str_value.index('*')
            try:
                value = float(str_value[:sep_index])
            except ValueError:
                raise Exception(f'Expected a string containing a float number and a unit. "{str(inp_value)}" does not fulfil this requirement')
            unit = str_value[sep_index   1:]
        else:
            try:
                value = float(str_value)
            except ValueError:
                raise Exception(f'Expected a string containing a float number and a unit. "{str(inp_value)}" does not fulfil this requirement')
    return (value, unit)


class TestX(unittest.TestCase):
    def test_split_value_and_unit(self):
        # self.assertRaisesRegex(Exception, r'Expected a string containing a float number and a unit. "_0" does not fulfil this requirement', lambda: split_value_and_unit('_0'))
        self.assertRaisesRegex(Exception, r'Expected a string containing a float number and a unit\. "_1\ e16" does not fulfil this requirement', lambda: split_value_and_unit('_1 e16'))

Alternatively you could import re and use re.escape('Expected a string containing a float number and a unit. "_1 e16" does not fulfil this requirement') as your pattern if you don't want to have to worry about always escaping special characters.

  • Related