Testing postMessage with asynchronous Jasmine does not work

I am trying to use Jasmine 2.0 to write unit tests for some logic in an AngularJS application, but the logic is inside an event listener. From the controller:

window.addEventListener('message', function(e) { if (e.data === "sendMessage()") { $scope.submit(); } }, false); 

And from the test file:

  describe("post message", function() { beforeEach(function(done) { var controller = createController(controllerParams); spyOn($scope, 'submit'); window.postMessage('sendMessage()', '*'); done(); }); it('should submit on a sent message', function (done) { expect($scope.submit).toHaveBeenCalled(); done(); }); }); 

But the test fails, the spy never hits. Additional information from entering console debugging instructions:

  • window.addEventListener in an IS controller call.
  • The lock beforeEach and it are called.
  • The above message handler in the controller will not be called during the test.
  • The message sent in this test will eventually be processed by the message handler several times, but only after the test is completed.

What is my test missing here?

+8
javascript angularjs unit-testing jasmine postmessage
source share
1 answer

This is because in your beforeEach block beforeEach you call window.postMessage() (which is asynchronous, and you don't know when it will be executed), and then you call done() right after it, since it will be synchronous code. But window.postMessage() like async, and basically you need to call done() when your async operation is complete. This can be done as follows:

 beforeEach(function(done) { spyOn(someObject, 'submit').and.callFake(function () { done(); }); }); 

So, when your spy is running, the async operation is considered complete.

This can be expressed even shorter:

 beforeEach(function(done) { spyOn(someObject, 'submit').and.callFake(done); }); 

Here is the complete code:

 var someObject = { submit: function () { console.log('Submit'); } }; window.addEventListener('message', function(e) { if (e.data === "sendMessage()") { someObject.submit(); } }, false); // Test describe('window.postMessage', function () { beforeEach(function(done) { spyOn(someObject, 'submit').and.callFake(function () { done(); }); window.postMessage('sendMessage()', '*'); }); it('should submit on a sent message', function () { expect(someObject.submit).toHaveBeenCalled(); }); }); 

See working js bit: http://jsbin.com/xikewogagu/1/edit?html,js,console,output

I did not use Angular in this example because it plays with pure JS.

+6
source share

All Articles