How can you mock a variable inside of python class? - python

I have a Python class that I'm trying mock a certain variable in my unit test.
Excerpt of my Python class:
class something:
def __init__(self):
self._commandline = 'dir'
def run_function(self):
pass
In my unit test I am trying to set the class variable _commandline to something like "dirx" so that when I call run_function() that uses subprocess.run it'll fail and throw a subprocess.CalledProcessError so that I can test my assert.
This is my first time doing TDD please advice if there is a better way of doing this.
Thanks in advance!

Just re-assign the _commandLine variable.
E.g.
something.py:
class Something:
def __init__(self):
self._commandline = 'dir'
def run_function(self):
print(self._commandline)
pass
test_something.py:
import unittest
from something import Something
class TestSomething(unittest.TestCase):
def test_run_function(self):
instance = Something()
instance._commandline = 'dirx'
instance.run_function()
if __name__ == '__main__':
unittest.main()
test result:
dirx
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK

Related

Python unittest; assertTrue() strange behavior

I was amazed to see something like this in a code review request today:
import unittest
class SomeTestClass(unittest.TestCase):
#classmethod
def setUpClass(cls):
...
cls.assertTrue(some_condition, "a message")
This sparked my interest as I know in python2.7 you could not call assertXXX methods in a classmethod or a staticmethod and it would have failed there. I quickly put up some test code to check:
import unittest
class TestClass(unittest.TestCase):
#classmethod
def setUpClass(cls):
var = 34
cls.assertTrue(var == 34)
cls.assertTrue(var == 33)
def test_123(self):
self.assertFalse(False)
if __name__ == '__main__':
unittest.main()
Hoping for python language to fail on calling assertTrue which is an instance method in a static context and if it works (I thought may be in python3 it changed) then cls.assertTrue(var == 33) to raise an AssertionError. But to much of my surprize None of the above happened and I see:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
I am using python3.7. My questions are:
shouldn't this fail as assertTrue is an instance method? note that assertFalse and other such method do cause a failure
If it does work shouldn't assertTrue fail when the condition evaluate to False ?
This has to do with how functions are bound in 3.x. Consider a simpler example:
class Example:
def method(self):
print(f'Example.method called on {self!r}')
Example.method('a string')

Expecting Python assertRaises() to fail, but test always passes

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)

Testing methods each with a different setup/teardown

I'm testing a class, with many test methods. However, each method has a unique context. I then write my code as following:
class TestSomeClass(unittest.TestCase):
def test_a():
with a_context() as c:
pass
def test_b():
with b_context() as c:
pass
def test_c():
with c_context() as c:
pass
However, the context managers are irrelevant to the test case, and produce temporary files. So as to not pollute the file system when the test fails, I would like to use each context manager in a setup/teardown scenario.
I've looked at nose's with_setup, but the docs say that is meant for functions only, not methods. Another way is to move the test methods to separate classes each with a setup/teardown function. What's a good way to do this?
First of all, I'm not sure why what you have isn't working. I wrote some test code, and it shows that the exit code always gets called, under the unittest.main() execution environment. (Note, I did not test nose, so maybe that's why I couldn't replicate your failure.) Maybe your context manager is broken?
Here's my test:
import unittest
import contextlib
import sys
#contextlib.contextmanager
def context_mgr():
print "setting up context"
try:
yield
finally:
print "tearing down context"
class TestSomeClass(unittest.TestCase):
def test_normal(self):
with context_mgr() as c:
print "normal task"
def test_raise(self):
with context_mgr() as c:
print "raise task"
raise RuntimeError
def test_exit(self):
with context_mgr() as c:
print "exit task"
sys.exit(1)
if __name__ == '__main__':
unittest.main()
By running that with $ python test_test.py I see tearing down context for all 3 tests.
Anyway, to answer your question, if you want a separate setup and teardown for each test, then you need to put each test in its own class. You can set up a parent class to do most of the work for you, so there isn't too much extra boilerplate:
class TestClassParent(unittest.TestCase):
context_guard = context_mgr()
def setUp(self):
#do common setup tasks here
self.c = self.context_guard.__enter__()
def tearDown(self):
#do common teardown tasks here
self.context_guard.__exit__(None,None,None)
class TestA(TestClassParent):
context_guard = context_mgr('A')
def test_normal(self):
print "task A"
class TestB(TestClassParent):
context_guard = context_mgr('B')
def test_normal(self):
print "task B"
This produces the output:
$ python test_test.py
setting up context: A
task A
tearing down context: A
.setting up context: B
task B
tearing down context: B
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK

How can I set conditional decorators on setUpClass in unittest?

I want to set a conditional decorator to the setUpClass in python's unitest. I have tried the following (without a condition for now, to demonstrate the point):
import unittest
class conditional_decorator(object):
def __call__(self, func):
print ("Extra output")
return func
class First(unittest.TestCase):
#classmethod
#conditional_decorator
def setUpClass(cls):
print ("setting up")
def test1(self):
pass
if __name__ == '__main__':
unittest.main()
but I get an error
TypeError: object.__new__() takes no parameters
How can I solve this problem? Moreover, is there an easy way to 'combine' the two decorators for the setUpClass method?
When you look at the traceback line it will tell you where your error occurred. My best guess, because I dont know the rest of your code, is that in the body of your code you left an extra parameter or added an extra comma.
As for two decorators that questioned was asked here:Can I combine two decorators into a single one in Python?
*Also remove Class from setUpClass because setUp is it's own function
Just instantiate your conditional_decorator class first:
class First(unittest.TestCase):
#classmethod
#conditional_decorator() # Note the class is instantiated first.
def setUpClass(cls):
print ("setting up")
def test1(self):
pass
Or use a function instead of a class as your decorator:
def conditional_decorator(func):
print ("Extra output")
return func
class First(unittest.TestCase):
#classmethod
#conditional_decorator
def setUpClass(cls):
print ("setting up")
def test1(self):
pass
if __name__ == '__main__':
unittest.main()
Now it works:
$ python my_test.py
Extra output
setting up
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK

python unittest methods

Can I call a test method from within the test class in python? For example:
class Test(unittest.TestCase):
def setUp(self):
#do stuff
def test1(self):
self.test2()
def test2(self):
#do stuff
update: I forgot the other half of my question. Will setup or teardown be called only after the method that the tester calls? Or will it get called between test1 entering and after calling test2 from test1?
This is pretty much a Do Not Do That. If you want tests run in a specific order define a runTest method and do not name your methods test....
class Test_Some_Condition( unittest.TestCase ):
def setUp( self ):
...
def runTest( self ):
step1()
step2()
step3()
def tearDown( self ):
...
This will run the steps in order with one (1) setUp and one (1) tearDown. No mystery.
Try running the following code:
import unittest
class Test(unittest.TestCase):
def setUp(self):
print 'Setting Up'
def test1(self):
print 'In test1'
self.test2()
def test2(self):
print 'In test2'
def tearDown(self):
print 'Tearing Down'
if __name__ == '__main__':
unittest.main()
And the results is:
Setting Up
In test1
In test2
Tearing Down
.Setting Up
In test2
Tearing Down
.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Now you see, setUp get called before a test method get called by unittest, and tearDown is called after the call.
All methods whose name begins with the string 'test' are considered unit tests (i.e. they get run when you call unittest.main()). So you can call methods from within the Test class, but you should name it something that does not start with the string 'test' unless you want it to be also run as a unit test.
sure, why not -- however that means test2 will be called twice -- once by test1 and then again as its own test, since all functions named test will be called.
Yes to both:
setUp will be called between each test
test2 will be called twice.
If you would like to call a function inside a test, then omit the test prefix.

Categories