Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
tapioko opened this issue Feb 1, 2019 · 24 comments
Open

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

tapioko opened this issue Feb 1, 2019 · 24 comments

Comments

@tapioko
Copy link

tapioko commented Feb 1, 2019

🚀 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()}`);
});
@jeysal
Copy link
Contributor

jeysal commented Feb 1, 2019

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.

@thymikee
Copy link
Collaborator

thymikee commented Feb 1, 2019

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

@SimenB
Copy link
Member

SimenB commented Feb 1, 2019

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

@ChavaSobreyra
Copy link

ChavaSobreyra commented Apr 23, 2019

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

@andykais
Copy link

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', () => {
    })
  })
})

@optimistex
Copy link

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.

@RyanRHall
Copy link

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.

@StephanBijzitter
Copy link
Contributor

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

@jeysal
Copy link
Contributor

jeysal commented May 20, 2020

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.

@aesyondu
Copy link

aesyondu commented Jun 8, 2020

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

@rook2pawn
Copy link

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

@brentertz
Copy link

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

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

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

@aesyondu
Copy link

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 jest@25.5.1. Thanks!

@dandv
Copy link
Contributor

dandv commented Feb 9, 2021

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 🤦‍♂️. 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.

@benkeil
Copy link

benkeil commented Jun 2, 2021

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.

@billyvg
Copy link

billyvg commented Jun 8, 2021

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

@andrasferenczi
Copy link

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

@reach2jeyan
Copy link

Is there a way we can get the testStatus afterEach?

@jrichardsz
Copy link

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.

@eilinwis
Copy link

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

@gabriel-dehan
Copy link

gabriel-dehan commented Aug 18, 2023

Bump, it was so easy with Jasmine...

@aaronvg
Copy link

aaronvg commented Mar 2, 2024

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?

@ClayShentrup
Copy link

ClayShentrup commented Mar 11, 2024

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.

@vpanta
Copy link

vpanta commented Mar 26, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests