22

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mark Tozzi
  • 10,353
  • 5
  • 22
  • 30
  • 4
    nosetests doesn't require you to use classes, but if you want this sort of functionality you probably should. – Will May 12 '12 at 16:46
  • 2
    There's nothing (at least, nothing that's not a ginormous hack) that nose can do to make `foo` accessible from that setUp function. Local variables go away when the function they're in returns. – Thomas K May 12 '12 at 17:44

2 Answers2

14

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)
roskakori
  • 3,139
  • 1
  • 30
  • 29
  • As per your suggestion and the comments, I've re-written the test as a class. It's a little strange having some tests as classes and some not, but it feels safer than globals. Thanks for your time! – Mark Tozzi May 15 '12 at 14:20
  • Hi @roskakori, if you use a class setup, setup and teardown will run only once. However, if I want the setup and teardown to run before each test, I will create a fixture setup, then how do I access the variables created in this setup? – Yahya Mar 18 '16 at 07:20
  • nose fixtures are not supported in classes. Yet classes are a bit cleaner way of using globals (instance vars, whatever). If you have lots of data driven tests, you might want to look at nosetest generators -- "yield()". – Scott Prive Sep 22 '16 at 21:44
  • I'd really like nose classes more if you could run the class setup ONCE, then run multiple test functions without re-running class setup/teardown for each one. You could say that ideally it's "always" better to setup and teardown before each test, but in "reality" sometimes setup and teardown takes forever. By not allowing recycling of setup work, I take an uglier workaround: cram 20+ asserts into the main test function. I'd rather use individual test_() functions which don't setup/teardown, and instead confirm the environment is suitable for continued testing. – Scott Prive Sep 22 '16 at 21:49
2

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gary van der Merwe
  • 9,134
  • 3
  • 49
  • 80
  • Excellent approach. This should be added as a recipe to the nosetests docs! – tohster Mar 06 '15 at 02:31
  • @gary-van-der-merwe thanks for sharing this recipe. I added this to my test helpers file. Now I import it from two test files. With one file it runs fine. When I run the whole suite I get errors like "_teardown() takes exactly 3 arguments (5 given)". Looks like the wrapper is applied twice. This puzzles me - any ideas? – moin moin Aug 09 '16 at 08:13