How to fail a python unittest in setUpClass? - python

I am doing some unittests with python and some pre-test checks in setUpClass. How can I throw a unitest-fail within the setUpClass, as the following simple example:
class MyTests(unittest.TestCase):
#classmethod
def setUpClass(cls):
unittest.TestCase.fail("Test")
def test1(self):
pass
if __name__ == '__main__':
unittest.main()
gives the error TypeError: unbound method fail() must be called with TestCase instance as first argument (got str instance instead).
I understand the error I get as fail is a instance method, and I don't have an instance of MyClass yet. Using an instance on-the-fly like
unittest.TestCase().fail("Test")
also does not work, as unittest.TestCase itself has no tests. Any ideas how to fail all tests in MyClass, when some condition in setUpClass is not met?
Followup question: Is there a way to see the tests in setUpClass?

self.fail("test") put into your setUp instance method fails all the tests
I think the easiest way to do this at the class level is to make a class variable so something like:
#classmethod
def setUpClass(cls):
cls.flag = False
def setUp(self):
if self.flag:
self.fail("conditions not met")
Hope this is what you want.

Using a simple assert should work
assert False, "I mean for this to fail"

I'm not an expert in python but with same problem, I've resolved adding cls param:
...
#classmethod
def setUpClass(cls):
cls.fail(cls, "Test")
...
I think is strange but clearer.
When you pass cls, warning appear (Expected type 'TestCase', got 'Type[MyTests]' instead) and MyTests inherits from TestCase thus can
ignoring adding: # noinspection PyTypeChecker

Related

How to mock a Python class during testing for runtime typing checks?

I have some application method that uses the #typeguard.typechecked decorator for performing runtime checks of the passed parameters:
class SalesDeal:
pass
#typechecked
def do_something(deal: SalesDeal):
pass
Inside a test I have fake class FakeSalesDeal which implements a minimal mock for SalesDeal (which is a very complicated class in reality):
class FakeSalesDeal:
pass
def test_foo():
deal = FakeSalesDeal()
do_something(deal)
This test will of course fail because the #typechecked decorator will raise an error due to a different class.
Is there a way to mock/fake the class of FakeSalesDeal in order make the test pass?
You can use MagicMock with spec set to SalesDeal instead of creating a fake class.
isinstance(mock, SalesDeal) will be True for that mock object & you should be able to bypass the typecheck.
from unittest.mock import MagicMock
# ...
def test_foo():
deal = MagicMock(spec=SalesDeal)
print(isinstance(deal, SalesDeal))
do_something(deal)
test_foo()
This prints:
True
& doesn't throw any type check error.
This works because typechecked explicitly checks for Mock objects being passed with:
if expected_type is Any or isinstance(value, Mock):
return
Code from here
So if you use proper mocks, typechecked should not be causing any issues for you.
My final solution:
class FakeSalesDeal(MagicMock):
pass

Unittest and mocks, how to reset them?

I am testing a class that needs a mock in the constructor, so I usually do this:
class TestActionManager(unittest.TestCase):
#patch('actionlib.SimpleActionClient', return_value=create_autospec(actionlib.SimpleActionClient))
def setUp(self, mock1):
self.action_manager = ActionManager()
Then in this class I add all the tests. So the first one is working fine
def test_1(self):
self.action_manager.f()
self.action_manager.f.assert_called_once()
But if I add another test and run both
def test_2(self):
self.action_manager.f()
self.action_manager.f.assert_called_once()
It says f has been called twice. I was expecting setUp to create a new ActionManager (and hence create a new mock) before starting every test, but it is clearly not happening, since the mock is somehow shared. Also I tried to do
def tearDown(self):
del self.action_manager
But it does not fix the problem.
I have read something related in
Python Testing - Reset all mocks?
where the solution is to use a different library (something that I would like to avoid)
and in Any way to reset a mocked method to its original state? - Python Mock - mock 1.0b1 where it is using different classes to do it.
Is there any possibility to reset the mock in the same class before or after every test?
BTW, this is a unittest question, not a pytest question.
Anyways,
I believe what you're looking for is reset_mock
Here's, in general, how it works:
def test_1(self):
f = MagicMock() # or whatever you're mocking
f()
f.assert_called_once()
f.reset_mock()
f()
f.assert_called_once()
The result will be PASSED
If you want to automate, then you store the mocked thing inside setUp, and in tearDown you call the mocked thing's .reset_mock() method.
def setUp(self, mock1):
self.mock1 = mock1
# ... do other things ...
def tearDown(self):
self.mock1.reset_mock()

Can I "turn-off" #unittest.expectedFailure?

Let's say that, for whatever reason, I want to write a set of test cases that start out from the premise of failing code.
Maybe because setting up a failure is really complicated, while the demonstrating a fixed state is simple. In my case, it's not even that, I wanted to start with a failed test and then show how to fix it, for documentation purposes.
I can decorate #unittest.expectedFailure on the base class.
But the the fixed subclasses blow up with unexpected success because the decoration is inherited.
Can I remove the expectedFailure somehow?
In the code itself, not in a command line argument?
While I use and appreciate pytest this is a question for regular unittest.
unittest.skipXXX is not what I want, I do want to run Test_ThisShouldFail's test.
import sys
import unittest
#unittest.expectedFailure
class Test_ThisShouldFail(unittest.TestCase):
""" for argument's sake, let's say
the configuration and testing is very complicated
and I want it fail by default.
Subclasses fix the issue but re-use the test code
"""
data = dict(a=1, b=2)
def test_evens(self):
for key, v in self.data.items():
self.assertFalse(v % 2, f"{key}:Odd on {v}")
## 👉???unittest.expectedSuccess????
class Test_ThisShouldWork(Test_ThisShouldFail):
""" how do I turn off the expected failure? """
def setUp(self):
self.data.update(a=10)
if __name__ == "__main__":
sys.exit(unittest.main())
output:
FAILED (expected failures=1, unexpected successes=1)
(venv) #explore$ py test_expectedfail.py -v
test_evens (__main__.Test_ThisShouldFail) ... expected failure
test_evens (__main__.Test_ThisShouldWork) ... unexpected success
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (expected failures=1, unexpected successes=1)
this didn't work:
I was hoping the MRO would look at TurnItOff's blank unittest settings and use them. No such luck.
class TurnItOff(unittest.TestCase):
pass
class Test_ThisShouldWork(TurnItOff, Test_ThisShouldFail):
....
This relies on the internal implementation of unittest.expectedFailure, but works for a start:
def expectedSuccess(test_item):
test_item.__unittest_expecting_failure__ = False
return test_item
#unittest.expectedFailure
class TestFailure(unittest.TestCase):
def test_something(self):
self.assertTrue(False)
#expectedSuccess
class TestSuccess(TestFailure):
def test_something(self):
self.assertTrue(True)
Note that test_item can be both a class or a function, depending on where you put the decorator.

Unittest immutable object variables?

I am having issues changing object variables within class method. Example:
import unittest
class Example(unittest.TestCase):
def setUp(self):
self.var_A = "foo"
def test_1(self):
self.assertEqual(self.var_A, "foo")
self.var_A = "bar"
def test_2(self):
self.assertEqual(self.var_A, "bar")
if __name__ == '__main__':
unittest.main()
test_2 fails as self.var_A value has not been changed to "bar" in test_1
If I try to place it outside setUp and change it via self.__class__.var_A , it works.
Working example:
import unittest
class Example(unittest.TestCase):
var_A = "foo"
def setUp(self):
pass
def test_1(self):
self.assertEqual(self.var_A, "foo")
self.__class__.var_A = "bar"
def test_2(self):
self.assertEqual(self.var_A, "bar")
if __name__ == '__main__':
unittest.main()
Question: Why is the second example working and the first isn't?
It is confusing because it appears self.var_A is immutable and can not be changed after setUp, but I can use it as a part of the object within other test methods
The test runner will execute the setUp() method for each test method, so attributes defined in setUp() are reset each time. That's intentional and documented FWIW - you don't want your tests to depend on each other.
Class attributes on the other hand are left alone, which is why your second example "works".
Note that there's also a setUpClass method that is only executed once for all tests in the testcase, but you should only use it for attributes that are 1/ read-only and 2/ costly to set up. Once again, your tests should NOT depend on side-effects from other tests, each test MUST work in isolation, and each test must work whatever other tests from the same testcase have been (or not) executed before.

Why is a method of a Python class declared without "self" and without decorators not raising an exception?

I thought that the following code would result in an error because as far as I have read, a method in a Python class must either have "self" (or any other label, but "self" by convention) as its first argument, or "cls" or similar if the #classmethod decorator is used, or none if the #staticmethod decorator is used.
How come I get no error running this with Python 3.5 in the Terminal, even though test_method does not meet these requirements? It seems to work fine as a static method, but without the decorator.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
class MyClass:
def test_method(args):
print(args[1])
#staticmethod
def static_method():
print("static_method")
#classmethod
def class_method(cls):
print("class_method")
def main(args):
MyClass.test_method(args)
if __name__ == '__main__':
sys.exit(main(sys.argv))
Output:
$ python3 testscript.py "testing"
$ testing
EDIT:
My question could also be phrased differently, drawing attention away from self and to #staticmethod: "How come I'm getting a seemingly working static method without the #staticmethod decorator?"
In Python 2, functions defined in a class body are automatically converted to "unbound methods", and cannot be called directly without a staticmethod decorator. In Python 3, this concept was removed; MyClass.text_method is a simple function that lives inside the MyClass namespace, and can be called directly.
The main reason to still use staticmethod in Python 3 is if you also want to call the method on an instance. If you don't use the decorator, the method will always be passed the instance as the first parameter, causing a TypeError.
There is nothing special about this. In python 3 there is no difference between a function defined inside a class or a function defined outside a class. Both of them are normal functions.
The self that you are talking about here or maybe cls comes into picture only when you access the function through an instance. Hence here you didn't get any error.
However if you modify your code just a little bit to look like the following, then you'd get an error that you expected.
def main(args):
MyClass().test_method(args)
# Should throw an error
EDIT:
#staticmethod will work on both class instances like MyClass().test_method(args)and just a regular direct call like MyClass.test_method(args)
However a regular method(without self in it) can't be called on a class instance. So you will always have to call it as MyClass.test_method(args)
self isn't necessarily required. However, if you want to reference any variable or value that is associated with the object(instantiation of the class) (E.g. for a class about cars, it's speed, self.speed) you'll need to have self as a parameter in the function. For this reason, it's common practice to always have self as an argument, otherwise you aren't really using the class for the right reason.
EDIT:
This will actually throw an error if you do the following:
class a():
def __init__(self, x):
self.asd = x
def hello(x):
print(x)
>>> g = a(4)
>>> g.hello(5)
as when calling "hello", both "self" and "4" will be passed as parameters. It would work in the following instance, which is what I was saying above:
>>> g = a
>>> g.hello(4)
or
>>> a.hello(4)
To add on to the existing answers here and provide a code example:
class MyClass:
def __init__(self):
pass
def myStaticMethod():
print("a static method")
#staticmethod
def myStaticMethodWithArg(my_arg):
print(my_arg)
print("a static method")
MyClass.myStaticMethod()
MyClass.myStaticMethodWithArg("skhsdkj")
abc = MyClass()
abc.myStaticMethodWithArg("avc")
Try removing the #staticmethod decorator and rerunning the code and see what happens! (The very last call will fail since the method is passed in both self and the string input. By adding the decorator, we can guide the interpreter to perform our desired action)

Categories