Jest: Make test name available in beforeEach() and afterEach()

Created on 1 Feb 2019  路  13Comments  路  Source: facebook/jest

馃殌 Feature Proposal

Make test name available in beforeEach() and afterEach().

Motivation

When running Selenium tests it's helpful to take a screenshot after every test. Best name for those files would be the test name. Now I use url and timestamp, which are not that good.

For further logging purposes beforeEach() could use the test name too, to log when that test starts. It makes easier to search server logs from the same time.

Example

afterEach(async (testName) => {
  await saveScreenshot(testName);
  log(`Test '${testName}' ended at ${getTime()}`);
});
Feature Request

Most helpful comment

My current solution.

File: jest.config.js:

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

    .................
};

File jest.setup.js:

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

File any-test.spec.ts

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

    afterEach(() => console.log(
      'Test', 
      jasmine['currentTest'].fullName,
     'failed',
     !!jasmine['currentTest'].failedExpectations.length
   ));

   it('example', () => expect(1).toBe(1));
});

I hope it helps somebody.

All 13 comments

Interesting suggestion, thanks! If this is implemented, the argument should probably be an object literal to make it easy to pass in more things to the function without a breaking change.

Sounds cool. This will be possible once we get rid of done and replace it with generic purpose object, like AVA does :)

That won't happen any time soon though - in the meantime you can use jest-image-snapshot - the snapshot file will get the test's name automatically. If that doesn't fit, I'd look into implementing it in some other way using jest's snapshot features so that you get the name included

just wanted to add that this would be super useful when using an XHR vcr like nock back. Right now I have to do this in each test

    const { nockDone } = await nockBack('fixtureName.json')

    // the actual test

    nockDone()

it would be awesome to just do something like

  let nockDone

  beforeEach(() => {
    const result = await nockBack(testName + '.json')
    nockDone = result.nockDone
  })

  afterEach(() => {
    nockDone()
  })

in case this is helpful, mocha keeps a whole suite of helpers in the befores' this scope. Would it make sense to pass some sort of class instance, instead of an object literal, so we can pave the way for things like .skip() programmatically?

https://mochajs.org/api/suite#fullTitle

```js
describe('test suite', () => {
beforeEach(function () {
console.log(this.currentTest.fullTitle())
// test suite nested names are included too
}
describe('nested names are', () => {
it('included too', () => {
})
})
})

My current solution.

File: jest.config.js:

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

    .................
};

File jest.setup.js:

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

File any-test.spec.ts

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

    afterEach(() => console.log(
      'Test', 
      jasmine['currentTest'].fullName,
     'failed',
     !!jasmine['currentTest'].failedExpectations.length
   ));

   it('example', () => expect(1).toBe(1));
});

I hope it helps somebody.

This would be handy simply because I'm using jest to run long integration tests with lots of logging. It's nice to see which test is being run before the setup in the before* hooks start.

@optimistex 's solution still works today, so that's nice. Here's the types, so you don't have to do the ['currentTest'] and force it into an any type:

declare namespace jasmine {
    const currentTest: {
        id: string;
        description: string;
        fullName: string;
        failedExpectations: {
            actual: string;
            error: Error;
            expected: string;
            matcherName: string;
            message: string;
            passed: boolean;
            stack: string;
        }[];
        passedExpectations: unknown[];
        pendingReason: string;
        testPath: string;
    };
}

Note I made passedExpectations an unkown[], as it was always an empty array for me. Not sure if it ever contains something.

Just a quick note: The last bits of Jasmine remaining in Jest are supposed to be phased out soon, as jest-circus becomes the default test runner implementation. This does not mean using Jasmine-specific APIs will no longer be possible, just that you may miss out on any new features / fixes specific to jest-circus. See https://jestjs.io/blog/2020/05/05/jest-26 for a timeline.

In jest-circus, I noticed that when I console.log the event.test inside handleTestEvent, I get a recursive test type (something like test -> parent -> parent -> ROOT_DESCRIBE_BLOCK):

console.log({
  type: 'test',
  asyncError: "ErrorWithStack: etc...",
  duration: null,
  errors: [],
  fn: [Function (anonymous)],
  invocations: 1,
  mode: undefined,
  name: 'first it',
  parent: {
    type: 'describeBlock',
    children: [ [Circular *1], [Object] ],
    hooks: [],
    mode: undefined,
    name: 'first describe',
    parent: {
      type: 'describeBlock',
      children: [Array],
      hooks: [Array],
      mode: undefined,
      name: 'ROOT_DESCRIBE_BLOCK',
      parent: undefined,
      tests: []
    },
    tests: [ [Circular *1] ]
  },
  startedAt: 1591598218051,
  status: null,
  timeout: undefined
})

I was wondering if there is an equivalent function to jasmine['currentTest'].fullName?

EDIT: In the meantime this is what I did to get the full test name:

    if (event.name === "test_start") {
      let testNames = [];
      let currentTest = event.test;
      while (currentTest) {
        testNames.push(currentTest.name);
        currentTest = currentTest.parent;
      }
      this.global.testName = testNames
        .slice(0, testNames.length - 1)
        .reverse()
        .map((name) => name.replace(/\W/g, "-"))
        .join("-");
    }

Bumping this if there is any clear update or expectation on this feature.

I stumbled across the following which may be of interest to others.

beforeEach(() => {
  console.log(expect.getState().currentTestName);
})

afterEach(() => {
  console.log(expect.getState().currentTestName);
})

I stumbled across the following which may be of interest to others.

beforeEach(() => {
  console.log(expect.getState().currentTestName);
})

afterEach(() => {
  console.log(expect.getState().currentTestName);
})

Very nice, It works on [email protected]. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kentor picture kentor  路  3Comments

ianp picture ianp  路  3Comments

paularmstrong picture paularmstrong  路  3Comments

samzhang111 picture samzhang111  路  3Comments

jardakotesovec picture jardakotesovec  路  3Comments