I am developing a JavaScript library (https://github.com/yvesgurcan/web-midi-player) to enable MIDI playback in a web application. The library relies on the Web Audio API to create a way to play these MIDI files (https://github.com/yvesgurcan/web-midi-player/blob/test/src/MidiPlayer.js#L50). However, I am having trouble creating meaningful unit tests with Jest (https://github.com/yvesgurcan/web-midi-player/blob/test/tests/midiPlayer.js) because these tests don't have access to the window
object and more particularly to window.AudioContext
. As a consequence, running my application code which relies on AudioContext
throws errors related to the fact that this object does not exist and I can't actually test very much things in the library.
I've tried the following packages to solve my problem: jsdom
, jsdom-global
, and also web-audio-test-api
but none of these seem to inject AudioContext
in the environment.
I am thinking that the solution here would be to stub/mock AudioContext
but that does not sound like a good solution for solid unit tests.
What do you folks suggest to test the Web Audio API? Is stubbing the only viable solution here?
I think it depends a bit on what you want to test. Since you're using Jest I imagine you're just interested in testing the correctness of your own code. In that case I would recommend to fully mock the Web Audio API. It's not part of your responsibility and you can assume it works the way it should. The only thing you have to test is if your code is making the expected calls.
Mocking globally available variables like the AudioContext constructor is always a bit tricky but you could allow an AudioContext to be passed into your MidiPlayer
class as an optional argument. It would make testing a little easier and it would also allow users of your library to bring their own AudioContext.
I think of something like this:
class MidiPlayer {
constructor({
// ... the other options ...
context = new AudioContext()
}) {
// ...
}
}
Inside a test you could then simply instantiate the MidiPlayer
with a fake AudioContext
.
const fakeAudioContext = {
currentTime: 3,
// ... and all the other things your code uses ...
};
const midiPlayer = new MidiPlayer({ context: fakeAudioContext });
I recently answered a similar question related to Tone.js which might be helpful. The basic idea is the same.
In case you want to test if your library works nicely with the Web Audio API in a browser I would recommend to use a test runner like Karma. It executes the tests in a real browser and therefore can use all the browser APIs.