jest icon indicating copy to clipboard operation
jest copied to clipboard

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

Open tapioko opened this issue 6 years ago • 26 comments

🚀 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()}`);
});

tapioko avatar Feb 01 '19 12:02 tapioko

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.

jeysal avatar Feb 01 '19 12:02 jeysal

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

thymikee avatar Feb 01 '19 12:02 thymikee

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

SimenB avatar Feb 01 '19 12:02 SimenB

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()
  })

ChavaSobreyra avatar Apr 23 '19 03:04 ChavaSobreyra

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

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

andykais avatar May 10 '19 21:05 andykais

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.

optimistex avatar Aug 13 '19 10:08 optimistex

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.

RyanRHall avatar Mar 25 '20 15:03 RyanRHall

@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.

StephanBijzitter avatar May 09 '20 18:05 StephanBijzitter

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.

jeysal avatar May 20 '20 09:05 jeysal

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("-");
    }

aesyondu avatar Jun 08 '20 06:06 aesyondu

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

rook2pawn avatar Jul 22 '20 18:07 rook2pawn

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

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

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

brentertz avatar Aug 27 '20 20:08 brentertz

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!

aesyondu avatar Aug 28 '20 03:08 aesyondu

Despite being undocumented, expect.getState().currentTestName still works in Jest v26.6.3, including in test blocks, but it returns the full name of the describe -> describe -> ... -> test, with space separators between the path components :man_facepalming:. That may make it confusing to figure out what the test name is, vs. the describe name, especially in nested describes.

Anyway, I've added this method as my #1 answer to this StackOverflow question about accessing the test name.

dandv avatar Feb 09 '21 08:02 dandv

This kind of logging should be done by jest itself (like junit) and there is no need to make this information in another way available.

benkeil avatar Jun 02 '21 00:06 benkeil

Using jest-circus and a custom environment, we use handleTestEvent to essentially keep state of the test name in these different events. Our use case was to instrument our test suites... the output is something like this:

image

billyvg avatar Jun 08 '21 21:06 billyvg

Thanks @billyvg for your answer. The linked code gave me an idea and wrote an answer on SO.

andrasferenczi avatar Jun 14 '21 19:06 andrasferenczi

Is there a way we can get the testStatus afterEach?

reach2jeyan avatar Apr 04 '22 06:04 reach2jeyan

Based in the @aesyondu snippet and with hours of console.logs I achieved to get the describeName, testName and status

This is my CustomNodeEnvironment.js

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

class CustomNodeEnvironment extends NodeEnvironment {

    async setup() {
        await super.setup();
      }

    async handleTestEvent(event, state) {
             
        if (event.name === "test_start") {
            let testNames = [];
            let currentTest = event.test;
            while (currentTest) {
              testNames.push(currentTest.name);
              currentTest = currentTest.parent;
            }

            this.global.describeName = testNames[1]  
            this.global.testName = testNames[0]  
        }

        if (event.name === "test_fn_failure") {
            this.global.testStatus = "failure"
        }else if (event.name === "test_fn_success") {
            this.global.testStatus = "success"
        }
    }
}

module.exports = CustomNodeEnvironment

This my test

describe(`Im the describe`, () => {
  let driver;

  it(`Im the first test`, async () => {    
    expect(3).toBe(3);
  });

  it('Im the second test', async () => {    
    expect(3).toBe(4);
  }); 
  
  afterEach(() => {
    console.log({testStatus:testStatus, testName:testName, describeName:describeName});
  }) 
});

An my jest.config.js

const { defaults } = require("jest-config");
module.exports = {
  testEnvironment: './src/helpers/CustomNodeEnvironment.js',
};

An this is the log in which I have access to the detail of each test after its execution

image

I will try to improve the while to get the describe and test names.

jrichardsz avatar Nov 04 '22 01:11 jrichardsz

Hello guys! Is there still no way to get running test suite/test file name in Jest 29.4.1? i mean the Jest way

eilinwis avatar Jul 28 '23 18:07 eilinwis

Bump, it was so easy with Jasmine...

gabriel-dehan avatar Aug 18 '23 15:08 gabriel-dehan

Based in the @aesyondu snippet and with hours of console.logs I achieved to get the describeName, testName and status

This is my CustomNodeEnvironment.js

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

class CustomNodeEnvironment extends NodeEnvironment {

    async setup() {
        await super.setup();
      }

    async handleTestEvent(event, state) {
             
        if (event.name === "test_start") {
            let testNames = [];
            let currentTest = event.test;
            while (currentTest) {
              testNames.push(currentTest.name);
              currentTest = currentTest.parent;
            }

            this.global.describeName = testNames[1]  
            this.global.testName = testNames[0]  
        }

        if (event.name === "test_fn_failure") {
            this.global.testStatus = "failure"
        }else if (event.name === "test_fn_success") {
            this.global.testStatus = "success"
        }
    }
}

module.exports = CustomNodeEnvironment

This my test

describe(`Im the describe`, () => {
  let driver;

  it(`Im the first test`, async () => {    
    expect(3).toBe(3);
  });

  it('Im the second test', async () => {    
    expect(3).toBe(4);
  }); 
  
  afterEach(() => {
    console.log({testStatus:testStatus, testName:testName, describeName:describeName});
  }) 
});

An my jest.config.js

const { defaults } = require("jest-config");
module.exports = {
  testEnvironment: './src/helpers/CustomNodeEnvironment.js',
};

An this is the log in which I have access to the detail of each test after its execution

image

I will try to improve the while to get the describe and test names.

Does this work if the test cases (the "test(..)") are running in parallel? Don't the test events get sent out of order?

aaronvg avatar Mar 02 '24 22:03 aaronvg

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')

this is literally exactly what i came here looking for. this is how i do it in playwright.

ClayShentrup avatar Mar 11 '24 22:03 ClayShentrup

Just wanted to add a use-case to this kind of reflective-data available at runtime:

  • Enabling any kind of global rule (in our case, a detector ensuring a mock is set for xhr's) with an iterative rollout. It would fail in too many places to fix if we attempted to enable and fix everything in a single change. So we want an allow-list of locations in the meantime. If we could identify each test we'd then have a way to roll it out slowly while having a todo-list of things to fix.

Specifically we'd love if the filepath itself would be available of the executing spec, but even the full test name (such as that returned by expect.getState().currentTestName) would be useful in this case.

vpanta avatar Mar 26 '24 15:03 vpanta

This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Mar 26 '25 16:03 github-actions[bot]

commenting to keep this request alive

vpanta avatar Mar 26 '25 16:03 vpanta