Execute code after Jasmine test failure

前端 未结 4 479
故里飘歌
故里飘歌 2021-01-12 08:15

Is it possible to do something only if a Jasmine test fails? Juxtaposed with afterEach() which executes after an it() regardless of outcome, I\'m l

相关标签:
4条回答
  • 2021-01-12 08:32

    I wanted to do something similar on jasmine 2.5.2 (having custom logger object which would print out only when test fails, without having to do it manually).

    Struggled quite a bit to make it work generically through beforeEach/afterEach, finally I caved in to an uglier solution

    // logger print on fail
    
    function collectSpecs(suite: jasmine.Suite): jasmine.Spec[] {
        const result: jasmine.Spec[] = [];
        const process = [suite];
        while (process.length) {
            const suite = process.pop();
            const children = <jasmine.SuiteOrSpec[]> <any> suite.children; // wrong jasmine typing
            children.forEach(item => {
                switch (item.constructor.name) {
                    case "Suite":
                        process.push(<jasmine.Suite>item);
                        break;
                    case "Spec":
                        result.push(<jasmine.Spec>item);
                        break;
                }
            });
        }
        return result;
    }
    
    function findSpec(specId: string): jasmine.Spec {
        const rootSuite: jasmine.Suite = jasmine.getEnv()["topSuite"]();
        return collectSpecs(rootSuite)
            .filter(s => `${s.id}` === specId)[0];  // wrong jasmine typing on id
    }
    
    const loggerSpecProperty = "logger";
    
    function createReporter(): jasmine.CustomReporter {
        return {
            specDone: (result: jasmine.CustomReporterResult) => {
                const spec = findSpec(result.id);
                if (result.failedExpectations.length) {
                    const logger: modLog.MemoryLogger = spec[loggerSpecProperty];
                    if (logger) {
                        console.log(`\nfailed spec logger:\n${logger.lines.join("\n")}`);
                    }
                }
                delete spec[loggerSpecProperty];
            }
        };
    }
    
    export function registerReporter(): void {
        jasmine.getEnv().addReporter(createReporter());
    }
    
    function createLogger(): modLog.MemoryLogger {
        return new modLog.MemoryLogger(modLog.LogLevel.debug);
    }
    
    interface IItCallback {
        (done?: Function): void;
    }
    
    interface IItFunctionTyping {
        (name: string, callback: IItCallback): void;
    }
    
    interface IItFunction {
        (name: string, callback: IItCallback): jasmine.Spec;
    }
    
    // bad typings on it/xit/fit, actually returns Spec but is typed as void
    export function lit(fnIt: IItFunctionTyping, name: string, callback: IItCallback): jasmine.Spec {
        function inner(spec: jasmine.Spec, done?: Function) {
            const log = createLogger();
            this.log = log;
            spec[loggerSpecProperty] = log;
            callback.call(this, done);
        }
        const itFunc = <IItFunction> (fnIt || it);
        if (callback.length) {
            const spec = itFunc(name, function (done) {
                inner.call(this, spec, done);
            });
            return spec;
        } else {
            const spec = itFunc(name, function () {
                inner.call(this, spec);
            });
            return spec;
        }
    }
    

    some unneccessary type mumbo jumbo there due to @types/jasmine obscuring some details of the actual implementation (I suppose it's on purpose, typing version matches the jasmine package version), but I also wanted to practice my TypeScript

    passing "it" function to still allow for xit/fit when needed modLog is my logger module, override this to suite your needs

    usage:

    instead of

    it("should do something", function (done) {
        done();
    });
    

    use

    lit(it, "should do something", function (done) {
        this.log.debug("test");
        fail("test output");
        done();
    });
    

    (not very well documented but I think you can get the picture)

    it would be much nicer if there was a way for customReporter to access the spec context

    (then again this all is basically only for debugging purposes, you could as well add console.log to specific test when it fails and you're struggling with the details, but it was interesting excercise in getting to know jasmine a bit more)

    0 讨论(0)
  • 2021-01-12 08:52

    Here's a hack to re-enable jasmine.getEnv().currentSpec in Jasmine 2 (sort of, result isn't the full spec object, but contains id, description, fullName, failedExpectations, and passedExpectations):

    jasmine.getEnv().addReporter({
        specStarted(result) {
            jasmine.getEnv().currentSpec = result;
        },
        specDone() {
            jasmine.getEnv().currentSpec = null;
        }
    });
    
    0 讨论(0)
  • 2021-01-12 08:53

    I finally figured out how to grab a reference to the results of a failed spec with Jasmine 2.3.4, but I'm not sure it's going to be exactly what you're looking for.

    I used the vanilla PlayerSpec.js file that comes installed with the Jasmine setup. I ran the SpecRunner.html file to perform the test.

    Here is what I changed in the the PlayerSpec file:

    describe("Player", function() {
      var player;
      var song;
      var index = 0;
    
      beforeEach(function() {
        player = new Player();
        song = new Song();
        this.index = index++;
      });
    
      afterEach(function() {
        if (this.index > 0)
        {   var failed = jsApiReporter.specResults(this.index -1, 1)[0].failedExpectations;
            console.log('failed: ', failed);
            if (failed.length > 0)
            {
                console.log('After: ', this, failed[0].message);
                alert('ha');
            }
        }
      });
      it("should not fail", function()
      { expect(1).toEqual(2);
      });
    ...
    

    The rest of the file is the same as it originally was.

    Here is what I changed:

    Added an index variable to keep track of the current spec number.

    In the beforeEach function, I added the index value to the "this" object that gets passed between the it, beforeEach, and afterEach functions. This allows me to communicate between them all as necessary. I'm only using this mechanism to pass the index. Beware! If you just try to use the index value, it won't work! The functions process asynchronously, so there's a good possibility the index value will NOT be what you expect in the afterEach function.

    In the afterEach function, I check to make sure the index is greater than 0. In my local testing, the first spec fails, but that is not recognized until the SECOND time the afterEach is called. This is one of the reasons I'm not sure this will work llke you want. I then grab a reference to the failedExpectations and do some conditional processing if I recognize an error.

    The last change that is present is the addition of a new spec that will generate a failure.

    Here's a poor copy of my FireBug console results:

    failed: [Object { matcherName="toEqual",  message="Expected 1 to equal 2.",  stack="stack@http://localhost:4...ne-2.3.4/boot.js:110:5\n",  more...}]
    PlayerSpec.js (line 15)
    After: Object { index=1} Expected 1 to equal 2.
    PlayerSpec.js (line 18)
    failed: []
    PlayerSpec.js (line 15)
    failed: []
    PlayerSpec.js (line 15)
    failed: []
    PlayerSpec.js (line 15)
    failed: []
    PlayerSpec.js (line 15)
    

    This problem has been a journey for me. Sadly, I must move on to other things.

    I sincerely hope this either solves your problem or at least points you in the right direction. Hopefully, it will help others, too.

    Good Luck!

    0 讨论(0)
  • 2021-01-12 08:55

    I'm still using Jasmine 1.2 so perhaps things are different in Jasmine 2.0.

    In your afterEach function, you should be able to access the current spec that just completed by using:

    var currentSpec = jasmine.getEnv().currentSpec;
    

    That will return an object that has many properties. One of them is how many of the embedded tests passed (results_.passedCount).

    You could test against that value and perform your logging appropriately.

    Good luck!

    0 讨论(0)
提交回复
热议问题