So I have class Dog:
class Dog:
def __init__(self, res):
self._res = res
def error(self):
raise IndexError
and unit test:
class TestDog(unittest.TestCase):
def setUp(self):
self.dog = Dog([1,2])
def testError(self):
self.assertRaises(IndexError, self.dog.error())
Why is the testcase testError failing? self.dog.error() should result in IndexError being rasied, and I believe the assertRaises is correctly formatted.
Any help is appreciated, thanks.
When Python runs the line self.assertRaises(IndexError, self.dog.error()), it has to calculate the arguments of the method self.assertRaises before passing them to it, so it calls self.dog.error() which raises an error. This is how all functions work. The presence of self.assertRaises does nothing to stop the error being raised. You are meant to call self.assertRaises(IndexError, self.dog.error), where the second argument is now the actual function/method object self.dog.error without having called it yet. self.assertRaises will call it for you wrapped in a try/except so that it can do the test properly.
Alternatively:
with self.assertRaises(IndexError):
self.dog.error()
Related
I have a class which raises ValueError for a non-existent file path constructor parameter and I'd like to test it.
import os
import os.path
class MetricsManager:
def __init__(self, reference_file_path, translations_file_path):
if not os.path.isfile(reference_file_path):
raise ValueError(f'{reference_file_path} does not exist.')
if not os.path.isfile(translations_file_path):
raise ValueError(f'{translations_file_path} does not exist')
self._references_file_path = reference_file_path
self.translations_file_path = translations_file_path
But my test below fails.
import unittest
from src.metrics_manager import MetricsManager
def create_instance():
x = MetricsManager('foo.txt', 'bar.txt')
class MyTestCase(unittest.TestCase):
def test_something(self):
self.assertRaises(ValueError, create_instance())
if __name__ == '__main__':
unittest.main()
I did not have the assertRaises() call coded properly:
self.assertRaises(ValueError, MetricsManager, 'foo.txt', 'bar.txt')
assertRaises takes the function to be called as an argument, not the return value from actually calling it.
class MyTestCase(unittest.TestCase):
def test_something(self):
self.assertRaises(ValueError, create_instance)
If you call it first, the exception occurs before assertRaises gets called. You have to let assertRaises make the actual call.
You can also use assertRaises in a with statement:
class MyTestCase(unittest.TestCase):
def test_something(self):
with self.assertRaises(ValueError):
create_instance()
Here, you do call create_instance yourself, because any exception raised inside the with statement is implicitly caught and passed to the context manager's __exit__ method.
I have a python class in which I open files and read out data. If some creteria are not met, I raise an error, but before that I specify the error by giving the object an attribute: self.Error = specification. But since the error raising undos everything in the try block I can't access it. This happens in the __init__ function, so the created object doesn't even exist..
Here's the necessary code:
class MyClass:
def __init__(self):
#do something
if this_or_that:
self.Error = specification
raise MyCostumError
try:
object = MyClass()
except MyCostumError:
print(object.Error)
I get: NameError: name 'object' is not defined
Just for clarification:
I have defined MyCostumError, the variable names are just for better understanding: I use good ones and they are defined and I need the clarification, because an Error can be raised in different lines.
So here's my question:
Is there something like try/except, but when an error is raised it does NOT undo everything. Or am I just stupid and there is a much easier method for a achieving this?
If you are raising an exception in the initializer, you should not rely on the object to be created to get some error information to the caller. This is where you should use the exception to pass that information:
class MyCustomError(Exception):
pass
class MyClass:
def __init__(self):
#do something
if this_or_that:
raise MyCustomError(specification) # put the spec in the exception itself
try:
object = MyClass()
except MyCustomError as e:
print(e) # the spec is in the exception object
You are trying to reference to an object that cannot exist. Let me explain:
If an error occurs when you try to initialise an object, that object will not be initialised. So if you try to acced to it when it is not initialised, you will get an error.
try:
object = MyClass() #initialising object successful, object existing.
except: #initialising failed, object does not exist.
print(object.Error) #nameError, since object was never created.
Try/except doesn't undo anything, just stops doing something if an error occurs.
Error raising doesn't undo anything. Have a look at the docs.
As your output states, the object is not defined, this is because when you raise an error in the __init__, it is seen as the initialosor of your class failing, and this does not return an object.
I think this is what you're looking for:
class MyClass:
def __init__(self):
# do initialisation stuff
def other_method(self):
# do something
if this_or_that:
self.Error = specification
raise MyCustomError(specification)
object = MyClass()
try:
object.other_method()
except MyCustomError as e:
print(e)
print(object.Error)
It's not a beautiful solution but it should work:
errorcode = None
class MyClass:
def __init__(self):
global errorcode
#do something
if this_or_that:
errorcode = specification
raise MyCostumError
try:
object = MyClass()
except MyCostumError:
print(errorcode)
Given your question I think the following should fit your use case well.
class MyClass:
def __init__(self):
# Do something
try:
if this_or_that:
self.Error = specification
raise MyCostumError
except MyCustomError as e:
# Handle your custom error however you like
object = MyClass()
In the above case you should be able to mitigate the risk of instantiation failing due to custom exception/error raising failing by handling this behaviour within MyClass.__init__ itself.
This is also a much cleaner solution in terms of keeping logic relating to instantiation of MyClass objects contained within the __init__ function of the class - i.e. you won't have to worry about wrapping instantiations of this class in try/except blocks each time they are present in your code.
I tried to use setUpClass() method for the first time in my life and wrote:
class TestDownload(unittest.TestCase):
def setUpClass(cls):
config.fs = True
and got:
Ran 0 tests in 0.004s
FAILED (errors=1)
Failure
Traceback (most recent call last):
File "/opt/anaconda3/lib/python3.5/unittest/suite.py", line 163, in _handleClassSetUp
setUpClass()
TypeError: setUpClass() missing 1 required positional argument: 'cls'
What does it mean and how to satisfy it?
You need to put a #classmethod decorator before def setUpClass(cls).
class TestDownload(unittest.TestCase):
#classmethod
def setUpClass(cls):
config.fs = True
The setupClass docs are here and classmethod docs here.
What happens is that in suite.py line 163 the setUpClass gets called on the class (not an instance) as a simple function (as opposed to a bound method). There is no argument passed silently to setUpClass, hence the error message.
By adding the #classmethod decorator, you are saying that when TestDownload.setupClass() is called, the first argument is the class TestDownload itself.
Adding #classmethod before setUp and tearDown will resolve the issue. #classmethod is bound to the class.
class LoginTest(unittest.TestCase):
#classmethod
def setUpClass(cls):
**Your code**
#classmethod
def tearDownClass(self):
I am attempting to get into the practice of TDD so I have been trying to start with Python's unittest module. I have written a test which I fully expect to fail, but it always passes! Below is the test code:
def test_file_not_found_exception(self):
invalid_config = "Testing/invalidExperiment.xml"
experiment_with_invalid_config = MyClass(invalid_config)
self.assertRaises(OSError, experiment_with_invalid_config.run_experiment())
and my class definition is as follows:
class MyClass:
def __init__(self, experiments, output_directory = ".")
self._experiments = experiments
self._output_directory = output_directory
def run_experiment(self):
try:
x = 2 # dummy statement
except OSError:
print "Experiment file not found"
except:
print "Unexpected Error"
I figure that the try block should always execute correctly so I am at a loss as to why my unittest keep passing. I am very new to OOP in Python so I may be making a very obvious mistake... Also, if I catch the exception in the try-except block of the run_experiment() method, should the assertRaises() call ever work?
You are calling the function MyClass.run_experiment and passing it's output to self.assertRaises, for which reason it does not properly register an error when it fails to raise.
Incorrect Example:
import unittest
class MyClass(object):
def __init__(self, config):
pass
def run_experiment(self): pass
class MainTest(unittest.TestCase):
def test_file_not_found_exception(self):
invalid_config = "Testing/invalidExperiment.xml"
experiment_with_invalid_config = MyClass(invalid_config)
self.assertRaises(OSError, experiment_with_invalid_config.run_experiment())
if __name__ == "__main__":
unittest.main()
Correct:
with self.assertRaises(OSError):
experiment_with_invalid_config.run_experiment()
Or
self.assertRaises(OSError, experiment_with_invalid_config.run_experiment)
I'm quite a beginner in Python and started designing a unit test in Python and i need to post some messages to the server before i run the test class (cause it's gonna search for them). Thus i need to call a non-static method postMessages().
the stack-trace of the error i'm getting is this-
Error
Traceback (most recent call last):
File ".../TestMsgs.py", line 23, in setUpClass
instance = cls()
File ".../python2.7/unittest/case.py", line 191, in __init__
(self.__class__, methodName))
ValueError: no such test method in <class 'TestMsgs.TestMsgs'>: runTest
i have something like this in the code:
class A(object):
def postMessages(self):
print "i post messages in the server!"
class B(A):
#classmethod
def setUpClass(cls):
cls.foo() # should post messages for the tests in the class to work on
There's no option, right now, to make foo static. How can i instantiate B (or A, for that matter) in postMessages() so i can use it in setUpClass() ?
After having a read through the __init__ method for TestCase I see that you need to provide a test method name to it. The default is "runTest" which is why that error was popping up.
import unittest
class A(unittest.TestCase):
def postMessages(self):
print "i post messages in the server!"
class B(A):
#classmethod
def setUpClass(cls):
cls.foo(cls(methodName='test_method')) # should post messages for the tests in the class to work on
def foo(self):
self.postMessages()
def test_method(self):
pass
B.setUpClass()
You can see it running in an interactive Python console here. It will print out "i post messages in the server!"
The reason you need to pass in a valid method name in the class can be clearly seen in the source code for unittest:
class TestCase:
"""A class whose instances are single test cases."""
def __init__(self, methodName='runTest'):
"""Create an instance of the class that will use the named test
method when executed. Raises a ValueError if the instance does
not have a method with the specified name.
"""
try:
self._testMethodName = methodName
testMethod = getattr(self, methodName)
self._testMethodDoc = testMethod.__doc__
except AttributeError:
raise ValueError, "no such test method in %s: %s" % \
(self.__class__, methodName)
If you want to pass in parameters to the method that you have just passed in then you would need to do something like
class A(unittest.TestCase):
def foo(self, arg1):
pass
a = A(methodName='foo')
a.foo('an_argument')
But this whole question just feels really wrong. You should refactor rather than have a static method calling an instance method. It's just silly.