I am trying to unit test a JavaScript web tracking function because we intend to expose it as a library and want to be sure its interface remains stable. JavaScript web tracking works similarly to image preloading like this:
if (document.images) {
var image = new Image();
image.onload = function () {
...
};
image.src = pixelUrl;
}
When in a unit test (with js-testdriver) I am now trying to validate that an image has been created with the right pixelUrl I thought I had to do something like this:
call_the_reporting_function();
assertNotUndefined(document.images[0]);
assertNotUndefined(document.images[0].src);
My problem is now that the first assertion already fails. It seems that the new image is not added to document.images immediately.
Does anybody has an idea how to do proper unit testing on such things in JavaScript?
Thanks
Stefan
One possible solution, which is the way we just took, is to circumvent the problem by extracting the part above doing the html manipulation and mocking it.
While this solution is fine, it certainly has at least two downsides:
- there is no point using a browser-based unit test framework (like jsTestDriver, which is, what we are using) if you do not use the browser-specific parts
- the code is untested and you have to ensure that it works either manually or using runtime-expensive Selenium tests (where you can configure a proxy before the HttpClient and simply catch all web tracking requests issued by the browser. Assertions are done on the caught requests)
Related
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.
What I've got
A large (proprietary unfortunately) JS library, the many small modules that get rolled up into it during the build process, the accompanying source map, and over 300 examples that use the built version of the library.
The goal
A form of dependency tracking, I guess? I need to be able to modify one of the small modules, rebuild the large file, and then only re-verify the examples that were affected by this change. Note: I don't care whether this requires static analysis or if I have to run all examples thru a headless browser to extract something or so - I'm fine as long as it can be automated.
What I've tried so far
I've read answers to questions like this and tried pre-existing tools like
Madge, but none of them seem to work for my case. Madge in particular is great for telling me which of the modules depend on which modules, but that's not what I'm looking for. Most solutions online are based on the assumption that you're already using something like require.js or similar on which they can piggy-back, but in my case the library is simply just a giant blob.
My current approach is instrumenting the built version of the library by simply appending every line with something like neededModules["the_file_this_line_comes_from.module.js"] = true similar to how code coverage tools do it. However, that fails because of several parts like this:
Points.prototype = Object.assign( Object.create( Info.prototype ), {
plot: ( function () {
var static = new Background();
return function plot( line, physics ) {
<code>
};
}() ),
copy: function () {
return new this.constructor( this.info, this.history ).copy( this );
}
} );
The copy function is tracked/skipped just fine, but because the plot function is an IIFE(right?), the line var static = new Background(); always gets executed, even if there is absolutely no connection to the Points module.
What I'm looking for
Either some help with my current approach and its problems with IIFE or a different solution altogether. I've seen Facebook's Jest does offer dependency tracking, maybe someone has experience with that one, or there's some way to incorporate the source map?
Again, as long as it's automatable and finishes in let's say < 5 min, I'm totally fine with it no matter if it's static analysis or just some hacky script or whatever :)
Thanks!
I am currently working on a calculator that will run as a packaged (desktop) chrome app. I am using the math.js library to parse math input. This is my old code:
evaluate.js:
var parser = math.parser();
function evaluate(input){
$("#output").text(parser.eval(input));
}
However, if the input is something unreasonable like 6234523412368492857483928!, the app just freezes, because it is trying to evaluate the input. I know that math.js is still in beta so eventually there might be a fix (overflow errors), but I couldn't find any other library that parses raw input the way math.js does.
To fix this, I am trying to fix this using web workers to run it asynchronously. Here is the code that I have right now:
main.js
var evaluator = new Worker('evaluate.js');
evaluator.addEventListener('message', function(e){
$("#output").text(e.data);
}, false);
function evaluate(input){
evaluator.postMessage(input);
}
evaluate.js
var parser = math.parser();
function mathEval(input){
return parser.eval(input);
}
self.addEventListener('message', function(e){
self.postMessage(mathEval(e.data));
});
However, this doesn't work when I run it. Also, I noticed that when I use web workers, it throws the error Uncaught ReferenceError: math is not defined - evaluate.js:1, but it didn't throw this error with the old code.
Questions: Why doesn't this code work to evaluate the input? Is it possible to use multiple workers to speed it up? If I wanted to implement some sort of overflow error for when the worker takes more than 2 seconds, what would be the best way to go about doing it? Finally, is there a better way to do this?
Web Workers are run in totally separate context. They don't have access to the objects from parent web page. If you want to use math.js you have to import it into the worker using importScript.
I recommend to read Using Web Workers guide, part "Importing Scripts And Libraries" which describes how to do it, and how it works in detail.
I am interested in how to globally mock out the file picker in the browser. Specifically, I am most interested in doing this in Firefox but would prefer a general solution.
I only care about preventing the file picker dialog from appearing. I do not need to be able to assert that it did open. The problem is that I have unit tests for JavaScript code that open the file picker. When the dialog opens, it halts the execution of the test suite.
An example situation is that I am testing the onRender method of a Backbone.View. That method renders a subview, which will open the File Picker when it is rendered. Since I am not directly testing that subview, I would prefer not to mock out portions of its behavior when I am only interested in unit testing some other part of the onRender method.
Example:
//Test file
it("should do something", function() {
var view = new App.Views.SomeView();
spyOn(view.modelBinder, "bind");
view.render();
expect(view.modelBinder.bind).toHaveBeenCalled();
});
//View file
onRender : function () {
this.modelBinder.bind(this.el, this.model);
this.$("#thing").html(this.subview.render().el); //This line has a side effect that opens file picker
}
Essentially, I do not want to explicitly mock out the behavior that causes the file picker to be opened because it is not what I am interested in testing here. Doing so will make the test suite much more brittle and difficult to maintain.
Use sinon to mock/spy/stub out the calls. You can test for the calls being made instead of actually making the calls.
That way you can test that the function has been called without calling the actual function that displays the dialog.
To answer your question: Just don't.
I would replace subview.render() with an empty function to avoid the undesired side effect. You say however:
"I do not want to explicitly mock out the behavior that causes the
file picker to be opened because it is not what I am interested in testing..."
Which is a little contradictory. If you want to Unit-Test App.Views.SomeView, you will have to mock out external collaborators, specially when not interesting, and including your file picker. On the other hand, you should not mess with the SUT when unit-testing it.
Mocking would in fact make your test more prone to be red, but is the only way to make sure your production code does not suffer from ill forms of coupling (IMHO, A common pitfall with Backbone.js apps.)
The only place in which you would need to avoid the file-picker to display, is when unit-testing your file-picker itself, in that case you could use sinon as sugested or leave it without coverage if you are using jQuery. Remember the "Never mock a type you don't own" rule.
I have a requirement where I need to communicate with native code to perform some operations. I have been successful by using JS-Ctypes and things are panning out as expected. Since the communication from my web application with the native code takes some time, thus blocking the main JS thread consequently freezing the UI.
Thus I need to create a separate thread to be delegated with the communication with the native code and post back results to the main thread which will give the appropriate feedback to the user. Firefox ChromeWorker are exactly what I need to use, since they are independent threads with access to JS-Ctypes.
My problem is that for the life of me, I can't seem to load a script using that approach. This is what I currently have:
main.js
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
Components.utils.import("resource://gre/modules/Services.jsm");
var worker = new ChromeWorker("js/fpman/myworker.js");
worker.onmessage = function(e){
console.log(e.data);
};
worker.postMessage('start');
myworker.js
self.onmessage = function(e){
var sum = 1 + 1;
postMessage("Sum is " + sum);
};
When that code runs in the main JS, I get this error on firebug console
Failed to load script: http://localhost:8080/myapp/js/fpman/myworker.js (nsresult = 0x805303f4)
Point to note, when I use a normal worker thread i.e
var worker = new Worker("js/fpman/myworker.js");
the js file (myworker.js) is loaded fine and I get the expected result, but of course that doesn't suffice my needs since a normal worker doesn't have access to JS-Ctypes. So it seems the problem is how am creating the ChromeWorker. Could someone please enlighten me on how to appropriately instantiate and use the ChromeWorker Object from an application. I have seen a lot of reference of usage of ChromeWorker in extensions, but that is not what I want, I want to use the ChromeWorker in my web application.
Thanks.
That particular error is NS_ERROR_DOM_BAD_URI
I don't believe what you are doing will work, and I know it won't work very soon in Firefox because enablePrivilege is going away completely.