I am new to JavaScript unit testing.
We are converting a PHP legacy application to Symfony2 framework. We are planning to use the legacy javascript library. The TDD approach is working for Controller and Services (PHP code).
For Javascript functions TDD is not required, as we are successfully able to use the legacy Javascript functions by making the elemenent IDs similar to the old code in new Symfony2 forms (twigs).
For future enhancement and unit testing of javascript code also, we need to write unit test cases for these legacy functions also. We are planning to use Qunit for this.
An example function is
function DetailDrop(fieldname) {
fn = fieldname.id;
var field = fn.substring(0, fn.length - 2);
if (document.getElementById(field + "_C") && document.getElementById(field + "_C").checked == true && document.getElementById(field + "_D")) {
document.getElementById(field + "_D").style.display = 'inline';
}
else if (((document.getElementById(field + "_Y") && document.getElementById(field + "_Y").checked == true) || (document.getElementById(field + "_/") && document.getElementById(field + "_/").checked == true)) && document.getElementById(field + "_D")) {
document.getElementById(field + "_D").style.display = 'none';
document.getElementById(field + "_D").selectedIndex = 0;
}
else if (((document.getElementById(field + "_N") && document.getElementById(field + "_N").checked == true) || (document.getElementById(field + "_P") && document.getElementById(field + "_P").checked == true)) && document.getElementById(field + "_D")) {
document.getElementById(field + "_D").style.display = 'inline';
}
}
Giving the problem context above, I am not able to find out a way to unit test such javascript functions, which access HTML elements from a web form.
In such a scenario should I look for options to mock the complete web form
or am I totaly out of context here?
You're talking about keeping your tests atomic with respect to the DOM, which is great! Don't give up!
The basic idea is to have a version of the form in a "fixture" that is reset before/after each test in order to not have one test affect another (ensuring atomicity). You can do this two ways: (1) add HTML code to the "#qunit-fixture" div in your QUnit HTML file, or (2) simply access that element in your module beforeEach()
and afterEach()
to create what you need.
(1) HTML method: add the <form>
to #qunit-fixture
:
<html>
...
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<form ...>
<!-- only what MUST be there for JS to work -->
</form>
</div>
...
</body>
</html>
QUnit will reset anything inside the #qunit-fixture
element before each test is run, so you don't need to worry about it at all. This is probably the easiest method, but doesn't give you extensive control.
(2) Use the module beforeEach()
and afterEach()
methods to set up your HTML (note that my code uses jQuery, but you don't need to):
// in your JavaScript test file...
var fixture = $( "#qunit-fixture" );
QUnit.module("form tests", {
beforeEach: function() {
fixture.html("<form> ..... </form>");
},
afterEach: function() {
fixture.find("form").remove();
// any other cleanup (events maybe?)
}
});
In either case, you can now access the <form>
in your tests and it will be "refreshed" before each run:
QUnit.test("test form submission", function(assert) {
$("form").submit();
// maybe make sure an Ajax call was made?
// or that there is an error on the page?
});