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.