mardi 26 juillet 2016

Jasmine tests with timeout fail when run together, succeed when separate

Attempting to test our PostMessageService that handles toast messages from our other services. The tests pass individually, but fail when all run together. They also pass when we run the tests via Chrome instead of Phantom. I suspect its something to do with the timeouts we had to put into the tests in order to make sure that the recentToasts array from the previous tests will have auto-cleared.

Service to test

export class PostMessageService implements IPostMessageService {
private embedder: any = window.top;
private self: any = window.self;
private origin: string;
private recentToasts: IToastMessage[] = new Array<IToastMessage>();

/** @ngInject */
constructor(private $log: ng.ILogService,
            private translationService: ITranslationService,
            private $timeout: ng.ITimeoutService) {
    // Does nothing
}

public post(message: any): void {
    var that = this;
    this.$log.info(Constants.PRODUCT_NAME + ': PostMessageService Posting message to embedder: ' + JSON.stringify(message));

    if (this.embedder !== this.self) {
        if (!this.origin) {
            this.origin = window.location.origin ? window.location.origin : (`${window.location.protocol}//${window.location.host}` + (window.location.port ? ':' + window.location.port : ''));
        }

        if (message.type === Constants.TOAST) {
            if (this.noDupes(message)) {
                // Put the message on the watch list
                this.recentToasts.push(message);
                // In X seconds, delete the message from watch list
                this.$timeout(function(message: IToastMessage) {
                    _.remove(that.recentToasts, message);
                }, 1000);
                this.embedder.postMessage(message, '*');
            }
        } else {
            this.embedder.postMessage(message, '*');
        }
    }
}

// Some code excluded

private noDupes(message: any): boolean {
    return !_.some(this.recentToasts, (toast: any) => {
        return toast.data.title === message.data.title &&
                toast.data.body === message.data.body &&
                toast.data.type === message.data.type;
    });
}
}

The test

describe('In the PostMessageService', () => {
let postMessageService: IPostMessageService;
let translationService: ITranslationService;
let $timeout: ng.ITimeoutService;
let embedder: any = window.top;
let testMessage: any = {
    data: {
        title: "Test Title",
        body: "This is a test",
        type: 'error'
    },
    type: Constants.TOAST
};

beforeEach(angular.mock.module('pccrmBase'));

beforeEach(() => {
    inject(function (_postMessageService_: IPostMessageService,
                     _$httpBackend_: ng.IHttpBackendService,
                    _translationService_: ITranslationService,
                    _$timeout_: ng.ITimeoutService) {
        prepareHttpBackend(_$httpBackend_);
        $timeout = _$timeout_;
        translationService = _translationService_;
        postMessageService = _postMessageService_;
    });
});

it("should post the message", () => {
    spyOn(embedder, 'postMessage');
    postMessageService.post(testMessage);
    expect(embedder.postMessage).toHaveBeenCalledWith(testMessage, '*');
    $timeout.flush();
});

it("should post the message only once", (done: Function) => {
    spyOn(embedder, 'postMessage');
    setTimeout(function() {
        postMessageService.post(testMessage);
        postMessageService.post(testMessage);
        setTimeout(function () {
            expect(embedder.postMessage.calls.count()).toBe(1);
            done();
        }, 250);
    }, 1500);
    $timeout.flush();
});

it("should post the same message twice when adequately spaced apart", (done: Function) => {
    spyOn(embedder, 'postMessage');
    postMessageService.post(testMessage);
    setTimeout(function() {
        postMessageService.post({
            data: {
                title: "Test Title",
                body: "This is a test",
                type: 'error'
            },
            type: Constants.TOAST
        });
        expect(embedder.postMessage.calls.count()).toBe(2);
        done();
    }, 3000);
    $timeout.flush();
});

it("should post different messages in succession", (done: Function) => {
    spyOn(embedder, 'postMessage');
    setTimeout(function() {
        postMessageService.post({
            data: {
                title: "Test Title",
                body: "This is a test",
                type: 'error'
            },
            type: Constants.TOAST
        });
        postMessageService.post({
            data: {
                title: "Test Title 2",
                body: "This is a test 2",
                type: 'error'
            },
            type: Constants.TOAST
        });
        expect(embedder.postMessage.calls.count()).toBe(2);
        done();
    }, 2000);
    $timeout.flush();
});
});

The Errors

PhantomJS 2.1.1 (Mac OS X 0.0.0) In the PostMessageService should post the message FAILED Expected spy postMessage to have been called with [ Object({ data: Object({ title: 'Test Title', body: 'This is a test', type: 'error' }), type: 'toast' }), '*' ] but it was never called.

PhantomJS 2.1.1 (Mac OS X 0.0.0) In the PostMessageService should post the message only once FAILED Expected 0 to be 1.

PhantomJS 2.1.1 (Mac OS X 0.0.0) In the PostMessageService should post the same message twice when adequately spaced apart FAILED Expected 0 to be 2.

PhantomJS 2.1.1 (Mac OS X 0.0.0) In the PostMessageService should post different messages in succession FAILED Expected 0 to be 2.

PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 98 of 100 (4 FAILED) (skipped 2) (8.689 secs / 8.551 secs)

Again, these all work individually (by changing 'it' to 'fit), but fail when the whole batch is run. If anyone can offer some clues, I'd appreciate!

Aucun commentaire:

Enregistrer un commentaire