22

Is there a way to get the current spec name from within the running test?

Basically I want to save a file, eg. using a function saveFile(), with the name of the file being the spec name. Without having to manually retype the name of the test.

4 Answers 4

32

There's been a 2+-year old issue in the Jest repo to make the test name available, with no official solution as of Feb 2021. One core maintainer said none would be available any time soon.

The community has provided workarounds, however:

Easiest

expect.getState().currentTestName

This will return the full path to the test, from the outermost describe to the test itself, separated with ' ' (not the best separator), e.g. static methods toString. You can't easily tell which one was the describe name, and which one was the test name.

With some setup, and soon to be obsoleted

The same GitHub issue has this alternative method, which makes the test name accessible directly in the test via jasmine['currentTest'].fullName, no extend needed. Note though that Jasmine will no longer be the default test reported starting in Jest 27.

jest.config.js:

module.exports = {
    setupFilesAfterEnv: ['./jest.setup.js'],
    
    .................
};

jest.setup.js:

// Patch tests to include their own name
jasmine.getEnv().addReporter({
    specStarted: result => jasmine.currentTest = result,
    specDone: result => jasmine.currentTest = result,
});

Then, in your *.test.js files...

describe('Test description', () => {
    beforeEach(() => console.log('Before test', jasmine['currentTest'].fullName));

    test(...);
});
16

This worked for me

console.log(expect.getState().currentTestName);
1
6

I found that the only possible way was through the use of expect(), which contains the spec name in its this. doing something like

expect.extend({
  async toSaveFile(data) {
    fs.writeFileSync(`${this.currentTestName}.txt`, data)
    return { pass: true };
  },
});

allows to then do

expect().toSaveFile('contents of the file');

it's definitely a hack, but it's the only way I could find to get a reference to the spec name. there is also this.testPath that indicates the test file

4
  • 1
    Brilliant! Nice find. I wish the jest time would document the contents available with this inside the extend call. As far as I see, this is the best available "documentation".
    – hyit
    Commented Apr 18, 2019 at 14:30
  • 1
    "the only possible way"? There are at least two more ways, and one is much easier. Commented Feb 9, 2021 at 8:26
  • 1
    Not sure how long this has existed, but there's expect.getState().currentTestName
    – Consti P
    Commented Nov 14, 2021 at 12:46
  • @ConstiP excellent! Commented Nov 21, 2022 at 20:33
6

Problem

The issue with expect.getState().currentTestName is that it provides a concatenated string.

So e.g. in case of sum.test.js test file:

import { sum } from "./sum";
import { sleep } from "./util/sleep";

describe("testing sum", () => {

    describe("middle", () => {

        test('adds 1 + 1 to equal 2', async () => {

            console.log("TESTNAME", expect.getState().currentTestName)

            await sleep(1000)

            expect(sum(1, 1)).toBe(2);
        });
    })

})

It prints out TESTNAME testing sum middle adds 1 + 1 to equal 2 where you cannot tell which belongs to which.

Solution

To get the name of each test separated, this is what you can do:

Step 1

Create custom environment. This listens to events and sets up the variable:

Note that instead of NodeEnvironment, you can use PuppeteerEnvironment if you are using jest-puppeteer:

const PuppeteerEnvironment = require("jest-environment-puppeteer")
const NodeEnvironment = require('jest-environment-node');

class CustomEnvironment extends NodeEnvironment {
    constructor(config, context) {
        super(config, context);
        this.testPath = context.testPath;
        this.docblockPragmas = context.docblockPragmas;
    }

    getNames(parent) {
        if (!parent) {
            return [];
        }

        if (parent.name === 'ROOT_DESCRIBE_BLOCK') {
            return [];
        }

        const parentName = this.getNames(parent.parent);
        return [
            ...parentName,
            parent.name
        ]
    }

    async handleTestEvent(event, state) {
        const {name} = event;

        if (["test_start", "test_fn_start"].includes(name)) {
            this.global.testNames = this.getNames(event.test)
        }
    }
}

module.exports = CustomEnvironment;

Step 2

Register the file in jest.config.js:


export default {

    // ...

    // location of the file above
    testEnvironment: "./src/my-custom-environment.js",

}

Step 3 (Optional for TypeScript)

Create global.d.ts file:

declare const testNames: string[]

Step 4

Use it in your tests:

import { sum } from "./sum";
import { sleep } from "./util/sleep";

describe("testing sum", () => {

    describe("middle", () => {

        test('adds 1 + 1 to equal 2', async () => {

            console.log("TEST NAMES", testNames)

            await sleep(1000)

            expect(sum(1, 1)).toBe(2);
        });
    })

})

Output:

TEST NAMES [ 'testing sum', 'middle', 'adds 1 + 1 to equal 2' ]

Credits goes to:

2
  • I'd think this would be a problem if jest tests are running in parallel
    – Alex
    Commented Sep 13, 2021 at 16:51
  • You can also retrieve the value by casting (global as any).testNames instead of resorting to global.d.ts
    – Alex
    Commented Sep 13, 2021 at 19:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.