How can I access variables set in the Python nosetests setup function - python

I think what I'm trying to do is fairly simple. I want to initialize a couple of variables in a test setup function, and then use them in the test functions that are decorated with that setup. The following trivial example illustrates what I mean:
from nose.tools import *
def setup():
foo = 10
def teardown():
foo = None
#with_setup(setup, teardown)
def test_foo_value():
assert_equal(foo, 10)
This results in:
$ nosetests tests/test_foo.py
E
======================================================================
ERROR: test_foo.test_foo_value
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/mtozzi/.virtualenvs/foo/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
self.test(*self.arg)
File "/home/mtozzi/code/foo/tests/test_foo.py", line 12, in test_foo_value
assert_equal(foo, 10)
NameError: global name 'foo' is not defined
----------------------------------------------------------------------
Ran 1 test in 0.006s
FAILED (errors=1)
With the old unittest style, I could set these as instance variables on the test class, but I thought nosetests didn't require you to use classes. I've also considered setting them as package global variables, but that doesn't seem like a terribly good practice. I hope there's something obvious I'm missing for doing this.

As the comments to your question already suggested, simply switch to classes and use instance variables like self.foo. That's the way it should be done.
If you insist on not using classes, try global variables. You didn't hear this from me, though.
from nose.tools import *
foo = None
def setup():
global foo # Ugly.
foo = 10
def teardown():
global foo # Ugly.
foo = None
#with_setup(setup, teardown)
def test_foo_value():
assert_equal(foo, 10)
A third variant might be to use a dictionary for your values. This is slightly less ugly but horribly clumsy:
from nose.tools import *
_globals = {'foo': None}
def setup():
_globals['foo'] = 10
def teardown():
_globals['foo'] = None
#with_setup(setup, teardown)
def test_foo_value():
foo = _globals['foo']
assert_equal(foo, 10)

I use a custom with_setup decorator that uses the poor man's nonlocal: https://gist.github.com/garyvdm/392ae20c673c7ee58d76
def setup():
foo = 10
return [foo], {}
def teardown(foo):
pass
#with_setup_args(setup, teardown)
def test_foo_value(foo):
nose.tools.assert_equal(foo, 10)
For projects that are Python 3 only, I use nonlocal rather than .extend/.update for arguments, kwargs.

Related

python contextvars pass and access a variable along the chain of calls

Context variables are convenient when we need to pass a variable along the chain of calls so that they share the same context, in the case when this cannot be done through a global variable in the case of concurrency. Context variables can be used as an alternative to global variables both in multi-threaded code and in asynchronous (with coroutines).
I can use contextvars in Python 3.7 and above like below and It's usually really easy:
Sample 1:
import contextvars
user_id = contextvars.ContextVar("user_id")
def f1(user, operation):
user_id.set(user.id)
f2()
def f2():
f3()
def f3():
print(user_id.get()) # gets the user_id value
Sample 2:
But when I am using the contextvars to another module's function it is not accessible, showing below error. It seems I am misunderstanding the usage of contextvars :)
NameError: name 'user_id' is not defined
test2.py
def abc():
print("inside abc")
print(user_id.get())
if __name__=='__main__':
abc()
test1.py
import contextvars
from test2 import abc
import uuid
user_id = contextvars.ContextVar("user_id")
request_id = uuid.uuid4()
def f1():
f2()
def f2():
f3()
def f3():
print("inside f3")
print(user_id.get())
user_id.set(request_id)
f1_calling = f1()
abc_calling = ABC()
Full Output:
inside f3
cdd36594-372d-438a-9bac-da53751af08a
inside abc
Traceback (most recent call last):
File "/var/www/test1.py", line 19, in <module>
abc_calling = abc()
File "/var/www/test2.py", line 3, in abc
print(user_id.get())
NameError: name 'user_id' is not defined
So my fundamental question is how can I pass and access the context variable that I set from one function and access that variable from any sub-function that is called by the main module.?
"Global" variables in Python are not actually global, but are rather attributes of the module that defines them.
You can therefore access a global variable defined in the main module from a sub-module by accessing it as an attribute of sys.modules['__main__']:
test2.py
import sys
def abc():
print("inside abc")
print(sys.modules['__main__'].user_id.get())
Demo: https://replit.com/#blhsing/TurquoiseAltruisticPercent#main.py

Python Unittest and object initialization

My Python version is 3.5.1
I have a simple code (tests.py):
import unittest
class SimpleObject(object):
array = []
class SimpleTestCase(unittest.TestCase):
def test_first(self):
simple_object = SimpleObject()
simple_object.array.append(1)
self.assertEqual(len(simple_object.array), 1)
def test_second(self):
simple_object = SimpleObject()
simple_object.array.append(1)
self.assertEqual(len(simple_object.array), 1)
if __name__ == '__main__':
unittest.main()
If I run it with command 'python tests.py' I will get the results:
.F
======================================================================
FAIL: test_second (__main__.SimpleTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests.py", line 105, in test_second
self.assertEqual(len(simple_object.array), 1)
AssertionError: 2 != 1
----------------------------------------------------------------------
Ran 2 tests in 0.003s
FAILED (failures=1)
Why it is happening? And how to fix it. I expect that each tests run will be independent (each test should pass), but it is not as we can see.
The array is shared by all instances of the class. If you want the array to be unique to an instance you need to put it in the class initializer:
class SimpleObject(object):
def __init__(self):
self.array = []
For more information take a look at this question: class variables is shared across all instances in python?
This can also be accomplished directly in unittest if you prefer to use only 1 class. Implement the setUp class. setUp runs before any of the tests are run as the class is instantiated. It is similar to init but conforms to the unittest library. Note the opposite is tearDown which is executed at the end of the test class if you need to build and deprecate objects. Example:
class SimpleObject(unitest.TestCase):
def setUp(self):
# I run first
self.array = []
def some_test(self):
self.assertTrue(self.array == [])
<some test code here>
def tearDown(self):
# I run last
self.array = []. # or whatever teardown code you need
enjoy!

Asserting that __init__ was called with right arguments

I'm using python mocks to assert that a particular object was created with the right arguments. This is how my code looks:
class Installer:
def __init__(foo, bar, version):
# Init stuff
pass
def __enter__(self):
return self
def __exit__(self, type, value, tb):
# cleanup
pass
def install(self):
# Install stuff
pass
class Deployer:
def deploy(self):
with Installer('foo', 'bar', 1) as installer:
installer.install()
Now, I want to assert that installer was created with the right arguments. This is the code I have so far:
class DeployerTest(unittest.TestCase):
#patch('Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
# Can't do this :-(
mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)
This is the error I get:
File "test_deployment.py", line .., in testInstaller
mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)
AttributeError: 'function' object has no attribute 'assert_called_once_with'
Here is the fixed code (Call it test.py). Thanks, all!
import unittest
from mock import patch
class Installer:
def __init__(self, foo, bar, version):
# Init stuff
pass
def __enter__(self):
return self
def __exit__(self, type, value, tb):
# cleanup
pass
def install(self):
# Install stuff
pass
class Deployer:
def deploy(self):
with Installer('foo', 'bar', 1) as installer:
installer.install()
class DeployerTest(unittest.TestCase):
#patch('tests.test.Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
# Can't do this :-(
# mock_installer.__init__.assert_called_once_with('foo', 'bar', 1)
# Try this instead
mock_installer.assert_called_once_with('foo', 'bar', 1)
So, the error message you are getting is actually because you are not checking your mock properly. What you have to understand here is that in your decorator you are ultimately saying that, the call to Installer will relturn a Mock object instead.
Therefore, for any call to Installer() with respect to where you are patching, the return value of that will call Mock() instead.
So, the assertion you actually want to check is simply at the mock_installer, and not the mock_installer.__init__.:
mock_installer.assert_called_once_with('foo', 'bar', 1)
Here is the modification made to your code:
class DeployerTest(unittest.TestCase):
#patch('Installer', autospec=True)
def testInstaller(self, mock_installer):
deployer = Deployer()
deployer.deploy()
mock_installer.assert_called_once_with('foo', 'bar', 1)
A little extra information to provide some more explanation, if you were testing now if install was called within your context manager, you have to realize here that you actually have to check inside your __enter__, so a structure would be like this:
For clarity sake create a mock_obj in your test method and:
mock_obj = mock_installer.return_value
Now, within your context manager, you will need to look inside the call to __enter__(). In case you don't know why this is, read up on context managers.
So, with that in mind, you simply perform your check as:
mock_obj.__enter__().install.assert_called_once_with()
The problem is that Installer(...) doesn't call Installer.__init__ directly; rather, because Installer is an instance of type, and type.__call__ is defined, you get Installer() being equivalent to
type.__call__(Installer, ...)
which results in a call to Installer.__new__(Installer, ...), whose return value x is passed to Installer.__init__ along with the original arguments.
All of which is to say that since the mock object you bind to Installer isn't an instance of type, none of the preceding applies. Installer(...) is simply a call to a mock object, so you check that that directly:
mock_installer.assert_called_once_with('foo', 'bar', 1)

Python properties and unittest TestCase

Today I wrote test and typoed in one of test methods. My tests failed but I don't understand why. Is it special behaviour of Python properties or something else?
from unittest import TestCase
class FailObject(object):
def __init__(self):
super(FailObject, self).__init__()
self.__action = None
#property
def action(self):
return self.__action
#action.setter
def action(self, value):
self.__action = value
def do_some_work(fcells, fvalues, action, value):
currentFailObject = FailObject()
rects = [currentFailObject]
return rects
class TestModiAction(TestCase):
def testSetFailObjectAction(self):
rect = FailObject # IMPORTANT PART
rect.action = "SOME_ACTION" # No fail!
self.assertEquals("SOME_ACTION", rect.action)
def testSimple(self):
fcells = []
fvalues = []
rects = do_some_work(fcells, fvalues, 'act', 0.56)
rect = rects[0]
self.assertEquals('act', rect.action)
When I run this testcase with nose tests:
.F
======================================================================
FAIL: testSimple (test.ufsim.office.core.ui.cubeeditor.TestProperty.TestModiAction)
----------------------------------------------------------------------
Traceback (most recent call last):
File "TestProperty.py", line 36, in testSimple
self.assertEquals('act', rect.action)
AssertionError: 'act' != 'SOME_ACTION'
----------------------------------------------------------------------
Ran 2 tests in 0.022s
FAILED (failures=1)
If I fix typo with instance creation in testSetFailObjectAction all tests are work as expected. But this example turn me back to question: Is it safe to use properties? What if I will typo again some day?
You can use patch and PropertyMock from mock to this kind of jobs:
#patch(__name__."FailObject.action", new_callable=PropertyMock, return_value="SOME_ACTION")
def testSetFailObjectAction(self, mock_action):
self.assertEquals("SOME_ACTION", FailObject().action)
self.assertTrue(mock_action.called)
#This fail
self.assertEquals("SOME_ACTION", FailObject.action)
By patch you replace the property action just for the test context and you can also check if the property has been used.
Okay, that is Python default behaviour. In testSetFailObjectAction we add new static class variable that hides our properties. There is no way to protect yourself from mistakes like this.
The only suggestation is to use Traits library.

py.test: how to get the current test's name from the setup method?

I am using py.test and wonder if/how it is possible to retrieve the name of the currently executed test within the setup method that is invoked before running each test. Consider this code:
class TestSomething(object):
def setup(self):
test_name = ...
def teardown(self):
pass
def test_the_power(self):
assert "foo" != "bar"
def test_something_else(self):
assert True
Right before TestSomething.test_the_power becomes executed, I would like to have access to this name in setup as outlined in the code via test_name = ... so that test_name == "TestSomething.test_the_power".
Actually, in setup, I allocate some resource for each test. In the end, looking at the resources that have been created by various unit tests, I would like to be able to see which one was created by which test. Best thing would be to just use the test name upon creation of the resource.
You can also do this using the Request Fixture like this:
def test_name1(request):
testname = request.node.name
assert testname == 'test_name1'
You can also use the PYTEST_CURRENT_TEST environment variable set by pytest for each test case.
PYTEST_CURRENT_TEST environment variable
To get just the test name:
os.environ.get('PYTEST_CURRENT_TEST').split(':')[-1].split(' ')[0]
The setup and teardown methods seem to be legacy methods for supporting tests written for other frameworks, e.g. nose. The native pytest methods are called setup_method as well as teardown_method which receive the currently executed test method as an argument. Hence, what I want to achieve, can be written like so:
class TestSomething(object):
def setup_method(self, method):
print "\n%s:%s" % (type(self).__name__, method.__name__)
def teardown_method(self, method):
pass
def test_the_power(self):
assert "foo" != "bar"
def test_something_else(self):
assert True
The output of py.test -s then is:
============================= test session starts ==============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.3
plugins: cov
collected 2 items
test_pytest.py
TestSomething:test_the_power
.
TestSomething:test_something_else
.
=========================== 2 passed in 0.03 seconds ===========================
Short answer:
Use fixture called request
This fixture has the following interesting attributes:
request.node.originalname = the name of the function/method
request.node.name = name of the function/method and ids of the parameters
request.node.nodeid = relative path to the test file, name of the test class (if in a class), name of the function/method and ids of the parameters
Long answer:
I inspected the content of request.node. Here are the most interesting attributes I found:
class TestClass:
#pytest.mark.parametrize("arg", ["a"])
def test_stuff(self, request, arg):
print("originalname:", request.node.originalname)
print("name:", request.node.name)
print("nodeid:", request.node.nodeid)
Prints the following:
originalname: test_stuff
name: test_stuff[a]
nodeid: relative/path/to/test_things.py::TestClass::test_stuff[a]
NodeID is the most promising if you want to completely identify the test (including the parameters). Note that if the test is as a function (instead of in a class), the class name (::TestClass) is simply missing.
You can parse nodeid as you wish, for example:
components = request.node.nodeid.split("::")
filename = components[0]
test_class = components[1] if len(components) == 3 else None
test_func_with_params = components[-1]
test_func = test_func_with_params.split('[')[0]
test_params = test_func_with_params.split('[')[1][:-1].split('-')
In my example this results to:
filename = 'relative/path/to/test_things.py'
test_class = 'TestClass'
test_func = 'test_stuff'
test_params = ['a']
# content of conftest.py
#pytest.fixture(scope='function', autouse=True)
def test_log(request):
# Here logging is used, you can use whatever you want to use for logs
log.info("STARTED Test '{}'".format(request.node.name))
def fin():
log.info("COMPLETED Test '{}' \n".format(request.node.name))
request.addfinalizer(fin)
Try my little wrapper function which returns the full name of the test, the file and the test name. You can use whichever you like later.
I used it within conftest.py where fixtures do not work as far as I know.
def get_current_test():
full_name = os.environ.get('PYTEST_CURRENT_TEST').split(' ')[0]
test_file = full_name.split("::")[0].split('/')[-1].split('.py')[0]
test_name = full_name.split("::")[1]
return full_name, test_file, test_name
You might have multiple tests, in which case...
test_names = [n for n in dir(self) if n.startswith('test_')]
...will give you all the functions and instance variables that begin with "test_" in self. As long as you don't have any variables named "test_something" this will work.
You can also define a method setup_method(self, method) instead of setup(self) and that will be called before each test method invocation. Using this, you're simply given each method as a parameter. See: http://pytest.org/latest/xunit_setup.html
You could give the inspect module are try.
import inspect
def foo():
print "My name is: ", inspect.stack()[0][3]
foo()
Output: My name is: foo
Try type(self).__name__ perhaps?

Categories