I try to test a property with a doctest in py.test but I get a NameError saying that the class is not defined.
Here's a little example:
class ExampleClass:
def __init__(self, attribute):
self.attribute = attribute
#property
def attribute_squared(self):
"""
Examples:
>>> ExampleClass(attribute=2).attribute_squared
4
"""
return self.attribute ** 2
When running it whin py.test I get:
Error
**********************************************************************
Line 3, in ExampleClass
Failed example:
ExampleClass(attribute=2)
Exception raised:
Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.1.5\helpers\pycharm\docrunner.py", line 140, in __run
compileflags, 1), test.globs)
File "<doctest ExampleClass[0]>", line 1, in <module>
ExampleClass(attribute=2).attribute_squared
NameError: name 'ExampleClass' is not defined
Is there a way to test class properties with or should I write a real test?
Finally I found that the test doesn't pass only when I do right click/Run 'Doctest attribute_squared' in Pycharm.
When running pytest, the test passes.
Related
The package addict allows you to use dicts through attribute setting:
Example from the website:
from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'
Now when when I use for example a = body.B and I haven't included B yet it does not throw an error, but just returns nothing. How can I make it throw an error when the attribute was net yet set?
addict.Dict implements the __missing__ method to generate a value for any missing key(/attribute); the current implementation generates a new Dict instance. If you don't want this behaviour, you'll have to override it:
class MyDict(Dict):
def __missing__(self, name):
raise KeyError(name)
Note that this will throw a KeyError for attribute access, which may be confusing; you could also override __getattr__ if you want to throw an AttributeError instead. In use:
>>> body = MyDict()
>>> body.B
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "path/to/addict.py", line 62, in __getattr__
return self.__getitem__(item)
File "<stdin>", line 3, in __missing__
KeyError: 'B'
>>> body.B = "hello"
>>> body.B
'hello'
Note also that this will break the other examples you showed as well, as e.g. foo.bar.baz = qux calls __getattr__ on foo before calling __setattr__ on foo.bar (if successful):
>>> body.query.filtered.query.match.description = 'addictive'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "path/to/addict.py", line 62, in __getattr__
return self.__getitem__(item)
File "<stdin>", line 3, in __missing__
KeyError: 'query'
Python3 test cases (doctests) are failing with my sample code. But the same is working fine in Python2.
test.py
class Test(object):
def __init__(self, a=0):
self.a = a
def __getattr__(self, attr):
return Test(a=str(self.a) + attr)
tst.py
from test import Test
t = Test()
Run test cases: python3 -m doctest -v tst.py
Error:
Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/usr/lib/python3.6/doctest.py", line 2787, in <module>
sys.exit(_test())
File "/usr/lib/python3.6/doctest.py", line 2777, in _test
failures, _ = testmod(m, verbose=verbose, optionflags=options)
File "/usr/lib/python3.6/doctest.py", line 1950, in testmod
for test in finder.find(m, name, globs=globs, extraglobs=extraglobs):
File "/usr/lib/python3.6/doctest.py", line 933, in find
self._find(tests, obj, name, module, source_lines, globs, {})
File "/usr/lib/python3.6/doctest.py", line 992, in _find
if ((inspect.isroutine(inspect.unwrap(val))
File "/usr/lib/python3.6/inspect.py", line 513, in unwrap
raise ValueError('wrapper loop when unwrapping {!r}'.format(f))
ValueError: wrapper loop when unwrapping <test.Test object at 0x7f6e80028550>
Can anyone please help in how to overcome this error.
Thanks.
For unittest.mock try importing items as
from unittest import mock
instead of
from unittest.mock import patch
This worked around the bug for me.
This is arguably a bug in doctest. What's happening is that doctest is searching for functions/methods/callables with a docstring, and while doing so it's unwrapping any decorators it finds. Why it does this, I have no idea. But anyway, doctest ends up calling inspect.unwrap(t) (where t is a Test instance), which is essentially equivalent to doing this:
while True:
try:
t = t.__wrapped__
except AttributeError:
break
Because t is a Test instance, accessing t.__wrapped__ calls __getattr__ and returns a new Test instance. This would go on forever, but inspect.unwrap is smart enough to notice that it's not getting anywhere, and throws an exception instead of entering an infinite loop.
As a workaround, you can rewrite your __getattr__ method to throw an AttributeError when __wrapped__ is accessed. Even better, throw an AttributeError when any dunder-attribute is accessed:
def __getattr__(self, attr):
if attr.startswith('__') and attr.endswith('__'):
raise AttributeError
return Test(a=str(self.a) + attr)
I was trying to run all my unit test unit from the unit test object:
def run_unit_test_for_user(f,*args,**kwargs):
test = Test_code(f,args,kwargs)
test.run()
class Test_code(unittest.TestCase):
def __init__(self,f,args,kwargs):
self.f = f, ...etc...
pass
def test1(self):
#do stuff
def test2(self):
#do stuff
def test3(self):
#do stuff
however I get errors like:
Traceback (most recent call last):
File "my_module.py", line 191, in <module>
user_test.run_unit_test_for_user(Test_code)
File "/Users/user/path/my_module.py", line 6, in run_unit_test_for_user
test.run()
File "/Users/user/miniconda3/envs/eit/lib/python3.6/unittest/case.py", line 576, in run
testMethod = getattr(self, self._testMethodName)
AttributeError: 'Test_code' object has no attribute '_testMethodName'
why is it so difficult? I need to feed code/variables etc from another module so I dont want to do:
unittest.main()
because I need to pass arguments from other already running code.
I am not using command line nor do I wish, please don't suggest it.
I have simple Python code that uses dockets
#!/usr/bin/python
# http://stackoverflow.com/questions/2708178/python-using-doctests-for-classes
class Test:
def __init__(self, number):
self._number=number
def multiply_by_2(self):
"""
>>> t.multiply_by_2()
4
"""
return self._number*2
if __name__ == "__main__":
import doctest
doctest.testmod(extraglobs={'t': Test(2)})
I can use it with python interpreter:
> python simple.py
However, when I execute the code from doctest module, I get this error:
> python -m doctest simple.py
**********************************************************************
File "simple.py", line 10, in simple.Test.multiply_by_2
Failed example:
t.multiply_by_2()
Exception raised:
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/doctest.py", line 1289, in __run
compileflags, 1) in test.globs
File "<doctest simple.Test.multiply_by_2[0]>", line 1, in <module>
t.multiply_by_2()
NameError: name 't' is not defined
**********************************************************************
1 items had failures:
1 of 1 in simple.Test.multiply_by_2
***Test Failed*** 1 failures.
Why is this difference? How to resolve this issue?
The difference is that when you execute via doctest, it is the __main__ module compared to executing directly where your script's if __name__ == '__main__' block will execute.
I don't know of a good solution other than to put all the information you need in the docstring itself:
def multiply_by_2(self):
"""
>>> t = Test(2)
>>> t.multiply_by_2()
4
"""
return self._number * 2
This will have the added benefit that users who are reading your docstrings will know what's going on ... They won't have to stumble upon your extraglobs keyword to figure out what t is and how it was initialized.
I use below method to generate cases automatically in python unittest.
import unittest
class Tests(unittest.TestCase):
def check(self, i):
self.assertNotEquals(0, i)
for i in [0, 1, 2]:
def ch(i):
return lambda self: self.check(i)
setattr(Tests, "test_%d" % i, ch(i))
if __name__ == "__main__":
unittest.main()
It works well when "python test.py" to run all cases together.
But fails to run a specific case, like "python test.py Tests.test_0"
Traceback (most recent call last):
File "test.py", line 12, in <module>
unittest.main()
File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
self.parseArgs(argv)
File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
self.createTests()
File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
self.module)
File "/usr/lib/python2.7/unittest/loader.py", line 128, in loadTestsFromNames
suites = [self.loadTestsFromName(name, module) for name in names]
File "/usr/lib/python2.7/unittest/loader.py", line 109, in loadTestsFromName
return self.suiteClass([parent(obj.__name__)])
File "/usr/lib/python2.7/unittest/case.py", line 191, in __init__
(self.__class__, methodName))
ValueError: no such test method in <class '__main__.Tests'>: <lambda>
Anyone can help?
It appears (from the error message) and from perusing the source of unittest, that the test runner gets a reference to the method and then uses the method's __name__ attribute to figure out which method to run. This is a bit silly (after all, we already picked up a reference to the method!), but it does have some benefits in terms of code simplicity. The quick fix is to make sure that you set the name on your lambda functions as well:
import unittest
class Tests(unittest.TestCase):
def check(self, i):
self.assertNotEquals(0, i)
for i in [0, 1, 2]:
def ch(i):
return lambda self: self.check(i)
f = ch(i)
name = "test_%d" % i
f.__name__ = name
setattr(Tests, name, f)
if __name__ == "__main__":
unittest.main()
Now everything works as it should:
mgilson$ python test.py Tests.test_0
F
======================================================================
FAIL: test_0 (__main__.Tests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 13, in <lambda>
return lambda self: self.check(i)
File "test.py", line 5, in check
self.assertNotEquals(0, i)
AssertionError: 0 == 0
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
and:
mgilson$ python test.py Tests.test_1
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Note that it appears this this was a bug which was fixed. In the current HEAD, we can see that the name is parsed from the string as opposed to in the version which I have (and apparently you do to) where the name is picked up from the object (function) to be run.