Search code examples
javascriptangularjsunit-testingkarma-jasmineaudiocontext

How to Release AudioContext in Jasmine Tests


I've got an Angular service that sets up an audioContext. Jasmine is creating a new service for each test, so after 6 tests all tests fail with the error:

Error: Failed to construct 'AudioContext': The number of hardware contexts provided (6) is greater than or equal to the maximum bound (6).

Is there a way for me to clear the AudioContext between tests? I've tried AudioPlayer.context.close() in an afterEach block, but doesn't seem to be working.

service looks kinda like this:

angular.module('myApp')
  .service('AudioPlayer', function () {

    var self = this;

    self.context = new AudioContext();

    this.doSomething = function () {
       // doing super cool testable stuff here
    }
  })

and tests looks kinda like this:

describe('AudioPlayer', function () {
  var AudioPlayer;

  beforeEach(function () {
    inject(function ($injector) {
      AudioPlayer = $injector.get('AudioPlayer');
    });
  });

  afterEach(function () {
    AudioPlayer.context.close();
  });

  it('does cool stuff', function () {
    AudioPlayer.doSomething();    
    // unit test
  });

  it('does other cool stuff', function () {
    AudioPlayer.doSomething();    
    // unit test
  });

});

Thanks for the help!

Here is a jsFiddle illustrating the problem: http://jsfiddle.net/briankeane/cp929can/1/


Solution

  • I ended up creating a singleton-like context in the tests, then stubbing the constructor with a function that returns that same AudioContext... here is the final test code:

    describe('AudioPlayer', function () {
      var AudioPlayer;
      var context = new AudioContext();     // create the AudioContext once
    
      beforeEach(function () {
        module('myApp');
    
        inject(function ($injector) {
          spyOn(window, 'AudioContext').and.callFake(function () {
            return context;                // stub the constructor
          });
          AudioPlayer = $injector.get('AudioPlayer');
        });
      });
    
      for (var i=0;i<7;i++) { 
          it('does cool stuff', function () {
            AudioPlayer.doSomething();  
            expect(true).toBe(true);
            // unit test
          });
      }
    });
    

    And here is the working fiddle: http://jsfiddle.net/briankeane/3ctngs1u/

    Hopefully this helps someone out.