How to unit test a jQuery selector? - javascript

Just a quick question... I currently have the following jQuery code with a selector in it.
var ID = "idControl"
function doesTreeViewExist()
{
if($('#' + ID).length == 0)
{
return false;
}
else
{
return true;
}
}
I was wondering how do I write the test to test the selector using QUnit? More specifically, I'm having trouble coming up with the syntax/code.
EDIT:
Ok, suppose now I want to mock the selector call instead because I cannot get access to the actual website. I'm using JsTestDriver as my testing tool, which means I cannot touch the browser the tests are running in (else the test runs will stop). What about in such situation? How can I possibly test the code?
Thanks.

The function you post, can be heavily simplified:
var ID = "idControl";
function doesTreeViewExist() {
return !!$('#' + ID).length;
}
Using the !! construct (double bitwise NOT), to convert the length property to Boolean, it will return false only when length is zero.
Speaking about qUnit, you can easily setup a simple test like this:
test("Your selector test", function() {
ok($('#idControl').length > 0, "idControl exists");
// or simply
ok($('#idControl').length, "idControl exists");
});
The ok function does a boolean assertion, equivalent to JUnit's assertTrue.

I test selectors manually, then pass them to code that uses them. I can then unit test the code that uses them. If you want to just test a selector, you need access to the HTML it affects. Your test could include HTML to target, something like:
test("selector works", function() {
var html = $('<input type="select"><option value=0/></input');
var result = $('option', html);
ok(result.count() == 1);
});
But I don't do that... I put my selectors at the edge of the code so I can get to them quickly and step through them under the debugger. I'll have a simple class whose properties are those selectors. Then I'll mock/stub that simple class, so I can write code for everything dependent on those selectors.
The reason I don't test my selectors is because the HTML they target is generated by ASP.NET code, and hard to get to from a javascript test. But I can wrap them in a Humble Object ("http://xunitpatterns.com/Humble Object.html") then test code that depends on that humble object. Here's a simple wrapper class that I can replace with test doubles:
var createSelectWidget = function(rootSelector)
{
return {
userText : $('span', rootSelector),
inputList : $('option', rootSelector),
};
}
Whatever dependency injection pattern you use, you can then stub it like below. Suppose my widget has a select input to read the value of, and a span I want to write some results to:
var createSelectWidgetStub = function()
{
return {
userText : { text = function() {}},
inputList : { val = function() {}},
};
}
I can then pass around this stub in tests where I want to isolate the dependency but don't care about interactions with that dependency. When I want to verify interactions with the dependency, I can mock it with JSMock. Suppose I want to verify interactions with the input list, I would prepare a stub with the one element mock'd:
var selectMock = createSelectWidgetStub();
selectMock.inputList = mc.createMock(selectMock.inputList);

I used Jack and successfully mocked the jquery call and returned a custom length and expected result.
Something like:
jack (function() {
jack.expect("$").exactly("1").withArguments("#" + ID).returnValue( {length:0} );
doesTreeViewExist()
assertEquals(false, result);
});
I have also managed to supply a DOM structure to satisfy the jquery call, as I'm using one of JsTestDriver's built-in function HtmlDoc. Something like:
/*:DOC += <span id="idControl"></span> */
or simply create a DOM sturcture without the specified id to get the false statement.
HTH.

Related

Mocha Unit Test with Jquery Submit

I'm working on creating a Mocha unit test based on solution code that was given to me by someone else. (The goal here is to create an online code assessment for students that will be run against my unit test). It's a simple exercise and will not be extensible at all in the future.
I want to get the return value from a jQuery on-submit event and use that for my test case but am unsure how I can do that given the solution code that was given to me to work from.
I've gone through the document here (https://gist.github.com/soheilhy/867f76feea7cab4f8a84) but my particular case is different since we are using jQuery's on document ready and the on-submit.
I've also tried to do something like "export.validate = function(){}" to match an example from the docs but everything I've tried I either get that Mocha doesn't know the function, or Mocha doesn't know the boolean variable references.
solutionCode.js
$(document).ready(function() {
$("#form-submit").on("submit", function () {
var xValid = true;
var yValid = true;
//...Bunch of logic here that could change the boolean values...
return xValid && yValid;
});
});
And here is my Mocha test file.js
this.jsdom = require('jsdom-global')()
global.$ = global.jQuery = require('jquery');
var assert = require('assert');
var work = require('path/to/solutionCode.js');
describe('Validate Form', function() {
it('Form is valid', function(done) {
//Not sure how to get the return value here to do my assertion...
done();
});
});
If the value of both booleans in the return are True, the test should pass, otherwise it should fail.

How to mock dom element from karma testing

There is an input type file element. During angular file upload multiple times, the value is not being cleared. Hence manually clearing it using plain javascript dom manipulation.
Below is the code:
function removeFromQueue(item) {
vm.uploads.uploader.removeFromQueue(item);
// Clearing input file field for re-uploading
if(!vm.uploadFile) {
document.getElementById('upload-file-' + vm.type).value = null;
}
}
In this case, not able to mock the document.getElementById, hence controlling it using vm.uploadFile undefined variable from unit test case which is wrong. How to mock the dom element here?
You should be able to spyOn the document.getElementById and return the useful properties (i.e. value here). Like this,
spyOn(document, "getElementById").and.callFake(function() {
return {
value: 'test'
}
});
And then if you want, you can expect it to have been called,
expect(document.getElementById).toHaveBeenCalledWith('...')

Javascript Performance in Node modules

I have code in a module which looks something like this:
var MyModule = module.exports;
MyModule.some_function = function(arg) {
// Do some code here
// Do some logging using the function name
// var function_name = calleeArgs.callee.toString().match(/function ([^\(]+)/)[1];
// BAD, this method doesnt have a name
};
The code above does not work as the function does not have a name.
As an alternative, I could do the following, in which case the log would contain the method name:
function _some_function(arg) {
// Do some code here
// Do some logging using the function name - BAD, this method doesnt have a name
// var function_name = calleeArgs.callee.toString().match(/function ([^\(]+)/)[1];
// GOO, this method doesnt have a name
}
MyModule.some_function = function(arg) {
_some_function(arg);
};
So my question is:
1.) Does this way of writing make any sense - as far as I understand _some_function() is local to this module so there will be no negative implications as far as global scope/access is concerned
2.) Does this (the second option) have any performance implications? (my guess would of course be no, or at least relatively negligible)?
1) I find that code style very confusing and bloated. I think the following is the cleanest approach:
function some_function(arg) {
// Do some code here
// Do some logging using the function name
}
// Put exports at the end
exports.some_function = some_function;
2) A function wrapping another function will add a negligible overhead, but it should be avoided if it adds no value.

Javascript Module pattern - how to reveal all methods?

I have module pattern done like this:
var A = (function(x) {
var methodA = function() { ... }
var methodB = function() { ... }
var methodC = function() { ... }
...
...
return {
methA: methodA,
methB: methodB
}
})(window)
This code let's me call only methA and methB() on A which is what I want and what I like. Now the problem I have - I want to unit test it with no pain ot at least with minimal efforts.
First I though I can simply return this but I was wrong. It returns window object.(can someone explain why?).
Second - I found solution somewhere online - to include this method inside my return block:
__exec: function() {
var re = /(\(\))$/,
args = [].slice.call(arguments),
name = args.shift(),
is_method = re.test(name),
name = name.replace(re, ''),
target = eval(name);
return is_method ? target.apply(this, args) : target;
}
This method let's me call the methods like this: A.__exec('methA', arguments);
It is almost what I want, but quite ugly. I would prefer A.test.methA() where test would never be used in production code - just for revealing private methods.
EDIT
I see people telling me to test the big thing instead of the small parts. Let me explain. In my opinion API should reveal only the needed methods not a bunch of internal functions. The internals because of their small size and limited functionality are much easier to test then test the whole thing and guess which part gone wrong.
While I may be wrong, I would still like to see how I could return references to all the methods from the object itself :).
Answer to your first question(you return this, but it returns window, not the object you wanted): in javascript this inside the function returns global object unless this function is a method of the object.
Consider next examples:
1) this points to the global object ():
function(){
return this;
}
2) this points to the object:
var obj = {
value: "foo",
getThisObject: function(){
return this;
}
}
Your case is example #1, because you have a function, that returns an object. This function is not a method of any object.
The best answer to your second question is to test only public methods, but if
that is so important for you, I can propose next:
create your modules dynamically on server side.
How it works:
create separate scripts for functionality you want;
create tests for these separate scripts;
create method that will combine scripts into one however you want;
to load script, reference to the combining scripts method.
Hopefully, it can solve your problem. Good luck!
Why not use namespaces to add your modules and public methods to js engine. Like this:
window['MyApp']['MODULE1'] = { "METHOD1" : {}, "METHOD2" : {}};
I write modules like this Sample module in JavaScript.
And test it like this: Simple unit testing in JavaScript
The use of eval() is generally not good idea.

What is the right way to wire together 2 javascript objects?

I'm currently facing a conundrum: What is the right way to wire together 2 javascript objects?
Imagine an application like a text editor with several different files. I have some HTML page that represents the view for the notebook. I have a file notebook.js that contains class definitions for NotebookController and Notebook View.
NotebookControler object responsible for performing business logic on the Notebook like "Save Notebook," "Load Notebook," "New Notebook." NotebookView is responsible for managing the HTML that is used for presentation. It does low level stuff like "get/set notebook body" "get/set notebook name." It also listens for DOM events (onClick) and fires business events (saveNotebook). This is my attempt at the Passive View pattern.
I want my javascript client-side code to be object-oriented, separated concerns, and unit-testable. I want to test NotebookController with a mock NotebookView and vice versa. This means that I can't just instantiate a NotebookView inside the NotebookController. So do I
Put some logic in my notebook.js that wires the 2 together
Have a global function in my application that knows to instantiate one of each and wire them together
Use Dependency Injection, either a home-grown one or something like SquirrelIoc
In Java, the choice is a natural one: use Spring. But that doesn't seem very JavaScript-y. What's the right thing to do?
Dependency injection is probably your best bet. Compared to Java, some aspects of this are easier to do in JS code, since you can pass an object full of callbacks into your NotebookController. Other aspects are harder, because you don't have the static code analysis to formalize the interface between them.
Thanks for the insight. I ended up writing a simple JavaScript dependency injection utility. After debating for a while and your comments, it occured to me that DI was really the right answer because:
It totally separated the concerns of wiring from the business logic while keeping the wiring logic close to the things being wired.
It allowed me to generically provide a "you're all wired up" callback on my objects so that I could do a 3 phase initialization: instantiate everything, wire it all up, call everyone's callbacks and tell them they're wired.
It was easy to check for dependency missing problems.
So here's the DI utility:
var Dependency = function(_name, _instance, _dependencyMap) {
this.name = _name;
this.instance = _instance;
this.dependencyMap = _dependencyMap;
}
Dependency.prototype.toString = function() {
return this.name;
}
CONCORD.dependencyinjection = {};
CONCORD.dependencyinjection.Context = function() {
this.registry = {};
}
CONCORD.dependencyinjection.Context.prototype = {
register : function(name, instance, dependencyMap) {
this.registry[name] = new Dependency(name, instance, dependencyMap);
},
get : function(name) {
var dependency = this.registry[name];
return dependency != null ? dependency.instance : null;
},
init : function() {
YAHOO.log("Initializing Dependency Injection","info","CONCORD.dependencyinjection.Context");
var registryKey;
var dependencyKey;
var dependency;
var afterDependenciesSet = [];
for (registryKey in this.registry) {
dependency = this.registry[registryKey];
YAHOO.log("Initializing " + dependency.name,"debug","CONCORD.dependencyinjection.Context");
for(dependencyKey in dependency.dependencyMap) {
var name = dependency.dependencyMap[dependencyKey];
var instance = this.get(name);
if(instance == null) {
throw "Unsatisfied Dependency: "+dependency+"."+dependencyKey+" could not find instance for "+name;
}
dependency.instance[dependencyKey] = instance;
}
if(typeof dependency.instance['afterDependenciesSet'] != 'undefined') {
afterDependenciesSet.push(dependency);
}
}
var i;
for(i = 0; i < afterDependenciesSet.length; i++) {
afterDependenciesSet[i].instance.afterDependenciesSet();
}
}
}
I would say, just wire them together:
function wireTogether() {
var v = new View();
var c = new Controller();
c.setView(v);
}
But then of course another question raises - how do you test the wireTogether() function?
Luckily, JavaScript is a really dynamic language, so you can just assign new values to View and Controller:
var ok = false;
View.prototype.isOurMock = true;
Controller.prototype.setView = function(v) {
ok = v.isOurMock;
}
wireTogether();
alert( ok ? "Test passed" : "Test failed" );
There is also a framework for dependency injection for JavaScript: https://github.com/briancavalier/wire
I have a inversion of control library for javascript, I'm pretty happy with it. https://github.com/fschwiet/jsfioc. It also supports events, so if you want to have a startup event thats fine. It could use more documentation...
http://github.com/fschwiet/jsfioc
Another (newer?) option, which has better documentation and support, is requireJS (http://requirejs.org/).
I'll try to take a stab at this, but it will be a little difficult without seeing any actual code. Personally, I've never seen anybody do such a specific attempt at (M)VC with JavaScript or IoC for that matter.
First of all, what are you going to test with? If you haven't already, check out the YUI Test video which has some good info on unit testing with javascript.
Secondly, when you say "the best way to wire up that aggregation" I would probably just do it as a setter w/the controller
// Production
var cont = new NotebookController();
cont.setView( new NotebookView() );
// Testing the View
var cont = new NotebookController();
cont.setView( new MockNotebookView() );
// Testing the Controller
var cont = new MockNotebookController();
cont.setView( new NotebookView() );
// Testing both
var cont = new MockNotebookController();
cont.setView( new MockNotebookView() );
But this makes some big assumption on how you've designed your controller and view objects already.

Categories

Resources