At the Forge - Testing JavaScript
Listing 1. triple.html
<html>
<head>
<script src="lib/jquery-1.2.6.js"></script>
<script src="lib/jquery.fn.js"></script>
<script src="lib/jquery.print.js"></script>
<script src="lib/screw.builder.js"></script>
<script src="lib/screw.matchers.js"></script>
<script src="lib/screw.events.js"></script>
<script src="lib/screw.behaviors.js"></script>
<link rel="stylesheet" href="lib/screw.css">
<!-- Here is the function we define, to test -->
<script type="text/javascript">
function triple(i) {
return i * 3;
}
</script>
<!-- Here is the test itself -->
<script type="text/javascript">
Screw.Unit(function() {
describe("Triple should triple", function() {
it("returns 6 for 2", function() {
expect(triple(2)).to(equal, 6);
});
});
});
</script>
</head>
<body>
</body>
</html>
The test is passed when Screw.Unit() is executed. If it works well, the body of the HTML document is modified accordingly, using CSS classes (defined in screw.css) that give the traditional green (for passing) or red (for failing) report on the tests you performed.
I'm going to add two more tests, one that I know will pass, which uses the not_equal test modifier. The other test will fail, so you can examine what it looks like when one does. If all goes well, you should see two green bars and one reddish-brown bar, the last of which indicates failure. The test itself looks like this:
<script type="text/javascript">
Screw.Unit(function() {
describe("Triple should triple", function() {
it("returns 6 for 2", function() {
expect(triple(2)).to(equal, 6);
});
it("does not return 100 for 2", function() {
expect(triple(2)).to_not(equal, 100);
});
it("does return 100 for 2 -- fail!", function() {
expect(triple(2)).to(equal, 100);
});
});
});
</script>
As you can see, you can include as many it statements inside a describe block as you need. Over time, you will see your spec grow to contain more and more descriptions, it statements and expect statements.
Testing JavaScript functions is certainly a good thing to do. However, one of the primary uses of JavaScript is to modify the DOM—the document object model that provides a handle onto the contents of an HTML page. Using the DOM, you can view or modify the page, both in its tags and in its content.
Thus, DOM manipulations are a primary target for JavaScript tests. You want to be able to say that when a particular piece of HTML is clicked on, another piece of HTML should appear.
Now, some of the documentation for Screw.Unit will tell you that you can (and should) use a click() method to simulate clicking on an element of your page. I not only found the click() method to be unreliable, but also was persuaded by a posting on the Screw.Unit mailing list to put my text-hiding code in a separate function, which can then be called from within the click() handler for the paragraph and also from the test within an it block. This not only worked, but also encouraged a style that is more readable and easily workable, in my opinion.
Listing 2. clickview.html
<html>
<head>
<script src="lib/jquery-1.2.6.js"></script>
<script src="lib/jquery.fn.js"></script>
<script src="lib/jquery.print.js"></script>
<script src="lib/screw.builder.js"></script>
<script src="lib/screw.matchers.js"></script>
<script src="lib/screw.events.js"></script>
<script src="lib/screw.behaviors.js"></script>
<link rel="stylesheet" href="lib/screw.css">
<!-- Here is the function we define, to test -->
<script type="text/javascript">
function hide_paragraph() {
$("#hideme").hide();
}
$(document).ready(function() {
$('#hideme').click(function() {
hide_paragraph();
});
});
</script>
<!-- Here is the test itself -->
<script type="text/javascript">
Screw.Unit(function() {
describe("Paragraph", function() {
it("should be hidden when clicked", function() {
hide_paragraph();
expect($('#hideme').is(':hidden')).to(equal, true);
});
});
});
</script>
</head>
<body>
<p id="hideme">Click to hide</p>
</body>
</html>
The full file, clickview.html, is in Listing 2. The idea is that the document contains a paragraph:
<p id="hideme">Click to hide</p>
You then attach a click() event handler to the paragraph, invoking a function when the paragraph is clicked on:
function hide_paragraph() {
$("#hideme").hide();
}
$(document).ready(function() {
$('#hideme').click(function() {
hide_paragraph();
});
});
Finally, you set up a Screw.Unit() test block, as follows:
Screw.Unit(function() {
describe("Paragraph", function() {
it("should be hidden when clicked", function() {
hide_paragraph();
expect($('#hideme').is(':hidden')).to(equal, true);
});
});
});
When you load the page, Screw.Unit first invokes the function hide_paragraph(), which has the same effect that clicking on it would have. Then it checks to make sure, using a pseudo-class (:hidden) to identify hidden text. If no text with the ID “hideme” is currently hidden, jQuery returns an empty list, and the assertion fails.
The fact that everything in Screw.Unit, as in jQuery, is done using CSS selectors makes it easy and fast to work with. It would seem that there are people doing TDD (test-driven development) and BDD (behavior-driven development) using Screw.Unit; although I don't count myself among those, I do see myself using this sort of testing in the future, if only to avoid egg on my face among my software users. Besides, testing JavaScript in this way, at least to my mind, gives me a feeling of working on serious software, rather than one or more basic hacks.
I should note that the style in which I presented Screw.Unit in this column is a concise, but not idiomatic way that most users will put it into practice. Screw.Unit users typically separate tests into multiple files, allowing them not only to define custom test matchers, but also to have an entire library of tests, rather than just one file. Once you get started with Screw.Unit, I'm sure you will find a style that suits your needs, without running too much against the grain of the system's expected use.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
Web Development News
Developer Poll
| Designing Electronics with Linux | May 22, 2013 |
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
- RSS Feeds
- Dynamic DNS—an Object Lesson in Problem Solving
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Designing Electronics with Linux
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- A Topic for Discussion - Open Source Feature-Richness?
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Validate an E-Mail Address with PHP, the Right Way
- What's the tweeting protocol?








8 hours 33 min ago
13 hours 24 sec ago
16 hours 36 min ago
17 hours 8 min ago
19 hours 32 min ago
19 hours 35 min ago
19 hours 36 min ago
1 day 1 min ago
1 day 1 hour ago
1 day 7 hours ago