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

Get currently executed test name in beforeEach #611

Closed
nikku opened this issue Jun 16, 2014 · 38 comments
Closed

Get currently executed test name in beforeEach #611

nikku opened this issue Jun 16, 2014 · 38 comments

Comments

@nikku
Copy link

nikku commented Jun 16, 2014

TLDR; Is there a way to get the currently executed test case (its description derived from describe / it) in a beforeEach block?

What for?

Assuming I have a test runner that performs some sort of visual job on the screen (e.g. drawing SVG elements).

I provide each test case with a container to draw on and want the result to be inspectable by a user. In addition, I would like to add a caption to each of the tests, so that the user can easier keep track of which drawing result belongs to which test execution.

@nikku nikku changed the title Get currently executed test case Get currently executed test name Jun 16, 2014
@nikku nikku changed the title Get currently executed test name Get currently executed test name in beforeEach Jun 16, 2014
@stoeffel
Copy link

You can try this:

describe('TestCase', function() {
    var mySpec = it('should return the spec', function() {
        console.log(mySpec);
        expect(mySpec.getFullName()).toEqual('TestCase should return the spec');
    });
});

@nikku
Copy link
Author

nikku commented Jun 17, 2014

Nice to see that there is a way to access the spec created by it().
That does not quite satisfy my use case thought.
In fact, I'd like to access my spec in a beforeEach block like this:

describe('specs', function() {

  beforeEach(function() {

    // get current spec
    // append <h1>{{ currentSpec.getFullName() }}</h1> before spec runs
  });

  it('should append elements to body', function() { /* */ });

  it('should append some other elements to body', function() { /* */ });
});

Any ideas?

@stoeffel
Copy link

I don't think that there is a way to get the current spec in the beforeEach callback.
Sorry.

@infews
Copy link
Contributor

infews commented Jun 17, 2014

It would be good testing practice to inject a DOM selector into your drawing function. I could imagine a test helper that makes unique DOM ids and you can call that helper in each spec. You shouldn't need to get ahold of the spec to do this.

As for captioning, you could build a reporter - and add it in boot.js that finds the current spec DOM id an captions it after the fact with the spec name. Look at the HtmlReporter and see how it builds the spec name for the links in the runner.

I think this helper/reporter combo could be a generally useful add-on. Good idea.

ps: there may be more suggestions on the mailing list: [email protected]

@nikku
Copy link
Author

nikku commented Jun 17, 2014

Thanks for the suggestions @infews.

As it turns out a custom reporter can easily be abused as a test environment 😃.

var JsApiReporter = jasmine.JsApiReporter;

function Env(options) {
  JsApiReporter.call(this, options);

  this.specStarted = function(result) {
    this.currentSpec = result;

    var label = document.createElement('div');
    label.innerHTML = result.fullName;

    document.body.appendChild(label);
  };

  this.specDone = function(result) {
    this.currentSpec = null;
  };
}

Need to register it with jasmine, too:

var environment = new Env({
  timer: new jasmine.Timer()
});
jasmine.getEnv().addReporter(environment);

The reporter will be notified by Jasmine and print a caption before each executed test case.
In my tests I can now access the current environment via environment.currentSpec. I could also provide a HTML element to draw into from inside my tests.

@infews
Copy link
Contributor

infews commented Jun 17, 2014

It looks like you're using Jasmine 1.3? Is that right? With 2.0 the interface has changed a little, making currentSpec harder to get (on purpose). So caveat emptor.

Can we close this issue?

@infews infews closed this as completed Jun 17, 2014
@infews infews reopened this Jun 17, 2014
@nikku
Copy link
Author

nikku commented Jun 18, 2014

The above example is running fine against Jasmine 2.0.

@nikku nikku closed this as completed Jun 18, 2014
@nikku
Copy link
Author

nikku commented Jun 18, 2014

Just in case anyone is interested: I created the library jasmine-test-container-support that provides test containers + captioning for Jasmine 2.0.

@EvHaus
Copy link

EvHaus commented Aug 22, 2014

I find I'm struggling without this feature as well. My use case for needing the spec name is a bit different than @nikku's. In my case, I have about 100 it() tests which all share the same 250 lines of code. So the beforeEach() statement makes total sense. However, I have 2 tests (in those 100) where I need an additional extra variable configured in my 250 lines of beforeEach code.

Sure. I can just have those 2 tests duplicate the 250 lines of code and not use the beforeEach(), but that's a lot of code duplication, and it also requires me to move them out into a separate describe() block -- putting those tests out of context.

It would be fantastic if each it() test could pass a configuration variable into its beforeEach() call somehow.

Any suggestions?

@EvAlex
Copy link

EvAlex commented Dec 2, 2014

👍 such failure not to have this feature

@juliemr
Copy link

juliemr commented Feb 6, 2015

+1 It's useful for logging and debugging to be able to get hold of the current full name somehow - it would be great to see this feature come back.

@budnix
Copy link

budnix commented Apr 15, 2015

+1

@kegsay
Copy link

kegsay commented Apr 16, 2015

+1 for @juliemr's use case.

@robertjsaulnier
Copy link

+1@juliemr's use case is reason enough to restore the feature

@ryneeverett
Copy link

+1

@benlesh
Copy link

benlesh commented Oct 9, 2015

+1 I wanted this just so I could see which of my 700 tests was the slow one... there's probably some other idiomatic way to do that, but it would have been nice to just add timings to beforeEach and afterEach and console.log out the slow ones...

@christian-bromann
Copy link

I got my adapter working by doing this:

        let beforeAllMock = jasmine.Suite.prototype.beforeAll
        jasmine.Suite.prototype.beforeAll = function (...args) {
            self.lastSpec = this.result
            beforeAllMock.apply(this, args)
        }
        let executeMock = jasmine.Spec.prototype.execute
        jasmine.Spec.prototype.execute = function (...args) {
            self.lastTest = this.result
            executeMock.apply(this, args)
        }

@lxblvs
Copy link

lxblvs commented Dec 3, 2015

+1

@ggranum
Copy link

ggranum commented Feb 10, 2016

+1. A link to the justification for making 'spec' harder to get to would be nice. I can see wanting to prevent adding tests while executing tests and all that... but seriously: I just want the name of the test that's running. A stats object would be spiffy too. But making 'spec' harder to get without replacing it with something seems rather cruel.

@gregjacobs
Copy link

Definitely a +1 on this.

I have a situation where I have a test that passes in isolation but is failing when the entire suite is executed. I suspect this is an issue with ng-mock and want to add logging in a bunch of places based on the test name, but I'm currently unable to do this.

@leyendecker
Copy link

+1

@maddes
Copy link

maddes commented Apr 27, 2016

+1
It would be also helpful to debug the awful 'some test made a full page refresh!'…

@msaladin
Copy link

msaladin commented Oct 6, 2016

+1
In my Protractor tests, I want to create screenshots of the SUT to the file-system where it would be nice when the folder into which the screenshots are stored contain the current test name.

@kegsay
Copy link

kegsay commented Dec 21, 2016

So you can do this by abusing reporters as @nikku suggests, but it needn't be as complex as that. The simplest solution I could come up with was (tested on 2.5.2):

jasmine.getEnv().addReporter({
    specStarted: function(result) {
        console.log(result.fullName);
    }
});

You only need to add the reporter once and it will work for all test suites/cases in all files.

@slackersoft
Copy link
Member

Most of the reasons I'm seeing in this thread for wanting to get the spec name are probably best solved with a custom reporter that can do timing, or a different style of report, or screenshots based on pass/fail etc.

In general, we don't think you should have beforeEach or other setup, that relies on which test is being run. Instead, you should reset back to exactly the same state between the tests with the same setup.

Hope this helps. Thanks for using Jasmine!

@ggranum
Copy link

ggranum commented Feb 10, 2017

@slackersoft : I still don't see the reason to be so concerned with hiding this information and I really would like to understand the point of doing so.

Regardless. One of my use cases is simple. I want to use the name of the test in data that gets written out as part of the test, so that if something fails and the data isn't flushed correctly I know which test created what. Is it super hard to write the name of the test out twice? No. Does it feel really stupid? Yes, yes it does.

I just want to use the name of the test within the body of the test. That may include using it in beforeEach to do some prep work for the subset of tests within the set. Obviously others agree. I doubt anyone considers knowing the currently running test name a big deal. I think you mostly have power users hitting this ticket, so, at a guess, not a huge number really want to do conditional branching based on test name. Sure, some do. Maybe for good reason. Probably for bad ones. But we can't save everybody from themselves.

Sure, fine, there are other priorities. But if this isn't happening because of some desire to babysit the people who will abuse the information... it isn't going to work. Those who would abuse it are just going to abuse something even more complex, making it even harder to understand when someone has to un-cruft their workaround. Meanwhile, my goals are slightly more difficult to achieve. And that's really quite annoying.

@kegsay
Copy link

kegsay commented Feb 10, 2017

@slackersoft : We've actually ended up moving away from Jasmine to Mocha (and related libs) much to my dismay. We wanted --stop-on-failure to tell us which test failed, and the fact that we had to jump through these hoops to log the currently executing test rather than the unhelpful coloured . annoyed some of my team members enough to move over. I couldn't really give a coherent defensive argument for Jasmine here other than "the devs say X". We weren't being naughty and changing how beforeEach ran depending on the test, we just wanted some read-only information about the test.

I love the "batteries-included" nature of Jasmine over the other test frameworks, but there's often so little to differentiate picking one framework over the other that the small niggles like this can make all the difference. Sadly for us, this was one annoyance too many.

@cmstead
Copy link

cmstead commented May 10, 2017

I'm also interested in getting the name of the current running test for this specific reason:

I am using Approvals JS and trying to capture the test name so the approval can be written to a file. This is not for every test, but only for tests which use this external library. Moreover, no reporting should occur if the test passes normally, but if something fails I want to ensure approvals actually reports correctly and interacts with the correct file. These requirements are actually not mine, but rather they are the requirement of the external dependency, which no longer works as designed and has ceased support for Jasmine and only supports Mocha. Currently this is actually causing my team a significant amount of pain since they are testing Angular code which recommends Jasmine, but they have to duplicate magic strings all over the place rather than naming the tests and relying on an API which provides the name to external libraries.

For now I am going to have to set up a reporter that monkey patches the Jasmine library, which is tremendously disappointing.

@tobega
Copy link

tobega commented Jul 21, 2017

Seriously annoying issue. How hard could it be to e.g. pass this info in the this object, for example?

@Quramy
Copy link

Quramy commented Oct 12, 2017

I've created a patch library to do this. Check https://github.com/Quramy/jasmine-spec-name-patch out 😄

require('jasmine-spec-name-patch');

describe('My awesome function', function() {

  beforeEach(function() {
    console.log(this.fullName);  // -> My awesome function should returns ...
  });

  // or

  beforeEach((done, { fullName }) => {
    console.log(fullName);  // -> My awesome function should returns ...
    done();
  });

  it('should returns...', function() { /* test code */ });

  afterEach(function() {
    console.log(this.fullName);  // -> My awesome function should returns ...
  });

  // or

  afterEach((done, { fullName }) => {
    console.log(fullName);  // -> My awesome function should returns ...
    done();
  });
});

@monkpit
Copy link

monkpit commented Dec 14, 2017

@Quramy It doesn't work for me :(

@monkpit
Copy link

monkpit commented Dec 14, 2017

My use case for this issue is -

  • Browser instance is created in beforeEach
  • Browser is used for testing in it
  • I want a screenshot at the end of all tests (pass or fail) - afterEach uses the browser instance to take a screenshot before disposing of the browser with a close command.

Why can I not access the spec name in afterEach to pass to the screenshot function as a filename?

I can't do this with a reporter without building all of my browser setup/teardown into the reporter, instead of the individual specs. If I do that, then I would have to pass the Browser instance between the reporter and the specs, which seems dirty since I can't pass context on this between the reporter and the specs.

The only workaround I have managed to use successfully is from @stoeffel, but it's clumsy:

describe("contrived example", function(){
  beforeEach(function(){
    browser.create();
  });
  let mySpec = it("tests a feature", function(){
    this.specName = mySpec.getFullName();
    browser.goto("https://google.com");
    expect(browser.title).toContain("Google");
  });
  afterEach(function(){
    browser.screenshot(this.specName);
    browser.close();
  });
});

It feels weird to need to do the let mySpec / this.specName = mySpec.getFullName(); thing for each spec.

@Pyrolistical
Copy link

I did this instead: enhance-it.js

module.exports = (it) => {
  if (it.__ENHANCED_WITH_FULLNAME__) {
    return it;
  }
  const enhancedIt = (name, closure) => {
    const spec = it(name, () => closure(spec.getFullName()));
  };
  enhancedIt.__ENHANCED_WITH_FULLNAME__ = true;
  return enhancedIt;
};

usage: something.spec.js

it = require('../enhance-it')(it);

describe('foo', () => {
  it('bar', (fullName) => {
    expect(fullName).toBe('foo bar');
  });
});

Works in jest as well.

@harrygreen
Copy link

harrygreen commented Aug 21, 2018

I needed the test name in my afterEach (not beforeEach, which is evidently harder). This basic pattern might help someone:

describe("tests", () => {
  let currentTest;

  afterEach(() => {
    const {
      result: { fullName }
    } = currentTest;
    console.log(fullName); // "test 1", then "test 2"
  });

  currentTest = it("test 1", () => {
    // ...
  });

  currentTest = it("test 2", () => {
    // ...
  });
});

@elixirautomation
Copy link

@monkpit
When I accessed this.mySpec in beforeEach() block it is giving "undefined" for first it block.

Any clue ?

@slackersoft
Copy link
Member

@abhilash04 since 2.0, Jasmine supplies a fresh empty object to each spec. This object is setup as this for all beforeEach and afterEach as well as the it for that spec. In Jasmine 1.x the actual Spec object itself was setup as the this and this had a potential for user's to accidentally clobber Jasmine built-in functionality.

@Jezorko
Copy link

Jezorko commented Mar 21, 2019

Here's my (super hacky) solution: https://github.com/Jezorko/smack-my-jasmine-up

@monkpit
Copy link

monkpit commented Nov 12, 2019

After lots of writing new tests and wishing for this feature, I found a great way to fix this (at least, for my purposes)...

Put this somewhere that will run before your tests - for me this was in protractor's onPrepare hook:

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

If you're using typescript you might have to add in a declare const jasmine: any to get past type warnings. If you have to add this, you'll need to do it in both the setup file and the file where you access jasmine.currentTest.

The contents of jasmine.currentTest will look like this from inside an it block:

type Result {
    id: 'spec0',
    description: 'This string is the title of your `it` block',
    fullName: 'This is the full title derived from nested `describe` and `it`s that you have created',
    failedExpectations: [], // possibly has contents during `afterEach` / `afterAll`
    passedExpectations: [], // possibly has contents during `afterEach` / `afterAll`
    pendingReason: '',
    testPath: '/path/to/the/spec/file/of/this/test.js'
}

And now this will work:

describe('My test', () => {
    beforeEach(() => {
        console.log(jasmine.currentTest.fullName);
        // logs: 'My test has currentTest'
    });

    it('has currentTest', () => {
        console.log(jasmine.currentTest.description);
        // logs: 'has currentTest'
        fail('Failure goes here');
    });

    afterEach(() => {
        if (jasmine.currentTest.failedExpectations.length) {
            console.log('This test failed:', jasmine.currentTest.description);
            // logs: 'This test failed: My test has currentTest'
        }
    });
});

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

No branches or pull requests