Get name of current test in setup using nose
Asked Answered
G

3

6

I am currently writing some functional tests using nose. The library I am testing manipulates a directory structure.

To get reproducible results, I store a template of a test directory structure and create a copy of that before executing a test (I do that inside the tests setup function). This makes sure that I always have a well defined state at the beginning of the test.

Now I have two further requirements:

  1. If a test fails, I would like the directory structure it operated on to not be overwritten or deleted, so that I can analyze the problem.
  2. I would like to be able to run multiple tests in parallel.

Both these requirements could be solved by creating a new copy with a different name for each test that is executed. For this reason, I would like to get access to the name of the test that is currently executed in the setup function, so that I can name the copy appropriately. Is there any way to achieve this?

An illustrative code example:

def setup_func(test_name):
    print "Setup of " + test_name

def teardown_func(test_name):
    print "Teardown of " + test_name

@with_setup(setup_func, teardown_func)
def test_one():
    pass

@with_setup(setup_func, teardown_func)
def test_two():
    pass

Expected output:

Setup of test_one
Teardown of test_one
Setup of test_two
Teardown of test_two

Injecting the name as a parameter would be the nicest solution, but I am open to other suggestions as well.

Grandaunt answered 23/5, 2013 at 9:21 Comment(0)
G
3

I have a solution that works for test functions, using a custom decorator:

def with_named_setup(setup=None, teardown=None):
    def wrap(f):
        return with_setup(
            lambda: setup(f.__name__) if (setup is not None) else None, 
            lambda: teardown(f.__name__) if (teardown is not None) else None)(f)
    return wrap

@with_named_setup(setup_func, teardown_func)
def test_one():
    pass

@with_named_setup(setup_func, teardown_func)
def test_two():
    pass

This reuses the existing with_setup decorator, but binds the name of the decorated function to the setup and teardown functions passed as parameters.

Grandaunt answered 23/5, 2013 at 10:5 Comment(0)
M
9

Sounds like self._testMethodName or self.id() should work for you. These are property and method on unittest.TestCase class. E.g.:

from django.test import TestCase


class MyTestCase(TestCase):
    def setUp(self):
        print self._testMethodName
        print self.id()

    def test_one(self):
        self.assertIsNone(1)

    def test_two(self):
        self.assertIsNone(2)

prints:

...
AssertionError: 1 is not None
-------------------- >> begin captured stdout << ---------------------
test_one
path.MyTestCase.test_one

--------------------- >> end captured stdout << ----------------------
...
AssertionError: 2 is not None
-------------------- >> begin captured stdout << ---------------------
test_two
path.MyTestCase.test_two

--------------------- >> end captured stdout << ----------------------

Also see:

Hope that helps.

Mckim answered 23/5, 2013 at 9:56 Comment(2)
Hm, thanks for this suggestion, but I would prefer a solution that does not require deriving from unittest.TestCase.Barkentine
Sure, got it. I think your solution using __name__ inside of a decorator looks pretty good.Mckim
G
3

I have a solution that works for test functions, using a custom decorator:

def with_named_setup(setup=None, teardown=None):
    def wrap(f):
        return with_setup(
            lambda: setup(f.__name__) if (setup is not None) else None, 
            lambda: teardown(f.__name__) if (teardown is not None) else None)(f)
    return wrap

@with_named_setup(setup_func, teardown_func)
def test_one():
    pass

@with_named_setup(setup_func, teardown_func)
def test_two():
    pass

This reuses the existing with_setup decorator, but binds the name of the decorated function to the setup and teardown functions passed as parameters.

Grandaunt answered 23/5, 2013 at 10:5 Comment(0)
S
1

In the case you neither want to subclass unittest.TestCase or use a custom decorator (as explained in the other answers) you can get the information by digging through the call stack:

import inspect

def get_current_case():
    '''
    Get information about the currently running test case.

    Returns the fully qualified name of the current test function
    when called from within a test method, test function, setup or 
    teardown.

    Raises ``RuntimeError`` if the current test case could not be
    determined.

    Tested on Python 2.7 and 3.3 - 3.6 with nose 1.3.7.
    '''
    for frame_info in inspect.stack():
        if frame_info[1].endswith('unittest/case.py'):
            return frame_info[0].f_locals['self'].id()
    raise RuntimeError('Could not determine test case')
Scent answered 25/5, 2017 at 16:21 Comment(1)
Exactly was I was looking for. Thanks!Jeanene

© 2022 - 2024 — McMap. All rights reserved.