'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.
Solution 1:[1]
You can also do this using the Request Fixture like this:
def test_name1(request):
    testname = request.node.name
    assert testname == 'test_name1'
    					Solution 2:[2]
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]
    					Solution 3:[3]
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 ===========================
    					Solution 4:[4]
Short answer:
- Use fixture called 
request - This fixture has the following interesting attributes:
request.node.originalname= the name of the function/methodrequest.node.name= name of the function/method and ids of the parametersrequest.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']
    					Solution 5:[5]
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
Solution 6:[6]
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
    					Solution 7:[7]
# 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)
    					Solution 8:[8]
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
Solution 9:[9]
Try type(self).__name__ perhaps?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source | 
|---|---|
| Solution 1 | Garrett | 
| Solution 2 | Joao Coelho | 
| Solution 3 | Dr. Jan-Philip Gehrcke | 
| Solution 4 | |
| Solution 5 | Delimitry | 
| Solution 6 | matt3o | 
| Solution 7 | |
| Solution 8 | |
| Solution 9 | kindall | 
