PDF Archive

Easily share your PDF documents with your contacts, on the Web and Social Networks.

Send a file File manager PDF Toolbox Search Help Contact



Saleh JavaScript Unit Testing .pdf



Original filename: Saleh - JavaScript Unit Testing.pdf

This PDF 1.4 document has been generated by Adobe InDesign CS5.5 (7.5.2) / Adobe PDF Library 9.9, and has been sent on pdf-archive.com on 17/12/2018 at 06:13, from IP address 46.219.x.x. The current document download page has been viewed 34 times.
File size: 3.3 MB (191 pages).
Privacy: public file




Download original PDF file









Document preview


JavaScript Unit Testing

Your comprehensive and practical guide to efficiently
performing and automating JavaScript unit testing

Hazem Saleh

BIRMINGHAM - MUMBAI

JavaScript Unit Testing
Copyright © 2013 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval
system, or transmitted in any form or by any means, without the prior written
permission of the publisher, except in the case of brief quotations embedded in
critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy
of the information presented. However, the information contained in this book is
sold without warranty, either express or implied. Neither the author, nor Packt
Publishing, and its dealers and distributors will be held liable for any damages
caused or alleged to be caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the
companies and products mentioned in this book by the appropriate use of capitals.
However, Packt Publishing cannot guarantee the accuracy of this information.

First published: January 2013

Production Reference: 1040113

Published by Packt Publishing Ltd.
Livery Place
35 Livery Street
Birmingham B3 2PB, UK.
ISBN 978-1-78216-062-5
www.packtpub.com

Cover Image by Jasmine Doremus (jasdoremus@gmail.com)

Credits
Author
Hazem Saleh
Reviewer
Allan Lykke Christensen
Acquisition Editor
Jonathan Titmus
Commissioning Editors
Harsha Bharwani
Priyanka Shah
Technical Editors
Hardik Soni
Devdutt Kulkarni
Copy Editors
Brandt D'Mello
Insiya Morbiwala
Alfida Paiva

Project Coordinator
Priya Sharma
Proofreaders
Lawrence A. Herman
Joel Johnson
Indexer
Hemangini Bari
Graphics
Aditi Gajjar
Production Coordinator
Melwyn D'sa
Cover Work
Melwyn D'sa

About the Author
Hazem Saleh has 9 years of experience in JEE and open source technologies.

He has worked as a technical consultant for different clients in Europe (Sweden),
North America (USA, Canada), South America (Peru), Africa (Egypt), and Asia
(Qatar, Kuwait). He is an Apache MyFaces committer, and the founder of many
open source projects.
Besides being the co-author of the book The Definitive Guide to Apache MyFaces
and Facelets, Zubin Wadia, Martin Marinschek, Hazem Saleh, Dennis Byrne, Apress
and the author of this book, Hazem is also an author of many technical articles,
a developerWorks contributing author, and a technical speaker at both local and
international conferences, such as the IBM Regional Technical Exchange, CONFESS,
and JavaOne. Hazem is now working for IBM Egypt (Cairo Lab SWG Services) as
an Advisory Software Engineer. He is a Web 2.0 subject matter expert and an IBM
Certified Expert IT Specialist.
I would like to thank my mother, my father, my brother Mohamed,
my sister Omnia, and all my family for endlessly supporting me
while writing this book. I would like to thank the love and best
friend of my life, my wife Naglaa, for encouraging and supporting
me while writing this book. I would like to thank all the people who
have done me a favor; I would like to thank Ahmed Fouad, Tamer
Mahfouz, my dearest brothers Ali AlKahki and Amr Ali, and every
one who has done me any kind of favor.

About the Reviewer
Allan Lykke Christensen is the Director of Interactive Media Management

and the Vice President of Danish ICT Management, an international consulting
firm with a focus on ICT in developing economies. He is responsible for the daily
management of teams in Uganda, Bangladesh, and Denmark. In his daily work, he is
also responsible for project planning, initiating, and overall implementation. He has
been developing and implementing IT projects for more than 10 years. His expertise
covers a wide range; he has developed workflow systems, information systems,
e-learning tools, knowledge-management systems, and websites. He has worked
as Team Leader on several major European Commission financed ICT projects in
various developing economies. He has co-authored the book The Definitive Guide to
Apache MyFaces and Facelets, Apress, and made countless presentations and training
sessions on programming-related topics around the world. Allan is also the Lead
Developer of the CONVERGE project, which aims at implementing an open source,
editorial content management system for media houses. More information on this
can be found at http://www.getconverge.com.

www.PacktPub.com
Support files, eBooks, discount offers
and more

You might want to visit www.PacktPub.com for support files and downloads related to
your book.
Did you know that Packt offers eBook versions of every book published, with PDF and ePub
files available? You can upgrade to the eBook version at www.PacktPub.com and as a print
book customer, you are entitled to a discount on the eBook copy. Get in touch with us at
service@packtpub.com for more details.
At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a
range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks.

http://PacktLib.PacktPub.com
Do you need instant solutions to your IT questions? PacktLib is Packt's online digital book
library. Here, you can access, read and search across Packt's entire library of books. 

Why Subscribe?


Fully searchable across every book published by Packt



Copy and paste, print, and bookmark content



On demand and accessible via web browser

Free Access for Packt account holders

If you have an account with Packt at www.PacktPub.com, you can use this to access
PacktLib today and view nine entirely free books. Simply use your login credentials for
immediate access.

Table of Contents
Preface 1
Chapter 1: Unit Testing JavaScript Applications
7
What unit testing is
7
Why we need unit testing
8
What Test-Driven Development (TDD) is
10
Complexities in testing JavaScript applications
11
Weather forecasting application
13
Exploring the application's HTML and JavaScript code
15
Running the weather application
28
Summary 29

Chapter 2: Jasmine

Configuration
Writing your first Jasmine test
The nested describe blocks
Jasmine matchers
The toBe matcher
The toBeDefined and toBeUndefined matchers
The toBeNull matcher
The toBeTruthy and toBeFalsy matchers
The toContain matcher
The toBeLessThan and toBeGreaterThan matchers
The toMatch matcher
Developing custom Jasmine matchers
Testing asynchronous (Ajax) JavaScript code
The runs() function
The waits() function
The waitsFor() function
The spyOn() function

31
31
32
38
39
39
40
41
41
42
42
43
43
45
45
46
47
49

Table of Contents

HTML fixtures
51
Configuring the jasmine-jquery plugin
52
The loadFixtures module
53
Testing the weather application
55
Testing the LoginClient object
56
Testing the RegistrationClient object
59
Testing the WeatherClient object
63
Running the weather application tests
63
Summary 63

Chapter 3: YUI Test

65

Writing your first YUI test
67
Assertions 74
The assert assertion
74
The areEqual and areNotEqual assertions
75
The areSame and areNotSame assertions
75
The datatype assertions
75
Special value assertions
76
The fail assertion
77
Testing asynchronous (Ajax) JavaScript code
78
The wait and resume functions
78
Testing the weather application
79
Testing the LoginClient object
80
Testing the RegistrationClient object
84
Testing the WeatherClient object
88
Running the weather application tests
89
Generating test reports
89
Automation and integration with build management tools
95
Configuring YUI Test Selenium Driver
95
Using YUI Test Selenium Driver in the weather application
96
Integration with build management tools
98
Summary 99

Chapter 4: QUnit

101

Configuration
101
Writing your first QUnit test
102
Assertions 108
The ok assertion
108
The equal and notEqual assertions
109
The deepEqual and notDeepEqual assertions
109
The expect assertion
110
Developing custom QUnit assertions
111
[ ii ]

Table of Contents

Testing asynchronous (Ajax) JavaScript code
114
The stop and start APIs
114
Testing the weather application
116
Testing the LoginClient object
118
Testing the RegistrationClient object
121
Testing the WeatherClient object
126
Running the weather application tests
128
Summary 129

Chapter 5: JsTestDriver

131

Architecture 131
Configuration
132
Writing your first JSTD test
134
Assertions 139
The assert, assertTrue, and assertFalse([msg], expression) assertions 140
The assertEquals and assertNotEquals([msg], expected, actual)
assertions
140
The assertSame and assertNotSame([msg], expected, actual)
assertions
140
The datatype assertions
141
Special value assertions
142
The fail([msg]) assertion
143
Testing asynchronous (Ajax) JavaScript code
143
AsyncTestCase, queue, and callbacks
144
Testing the weather application
145
Testing the LoginClient object
147
Testing the RegistrationClient object
149
Testing the WeatherClient object
153
Configuring the proxy
153
Running the weather application tests
154
Generating test reports
155
Integration with other JavaScript test frameworks
160
Integrating JSTD with Jasmine
162
Integrating JSTD with QUnit
164
Integration with build management tools
167
Integration with the IDEs
167
Eclipse integration
168
Summary 170

Index 171

[ iii ]

Preface
One of the biggest challenges of many web applications is being supported
by different browsers with different versions. JavaScript code that runs on
the Safari browser will not necessarily run correctly on Internet Explorer (IE),
Firefox, or Google chrome browsers. This challenge is caused by the lack of unit
testing of the JavaScript code that has lived in the web application from day one.
Without unit testing the JavaScript code, more money will have to be spent for
testing and retesting the application's web pages after deciding to upgrade to
current, supported browsers (or after updating the JavaScript code of the web
pages with non-trivial features).
The JavaScript Unit Testing book is a comprehensive practical guide that illustrates
in detail how to efficiently create and automate JavaScript tests for web applications
using popular, JavaScript unit testing frameworks, such as Jasmine, YUI Test, QUnit,
and JsTestDriver.
This book explains the concept of JavaScript unit testing and explores the bits of an
interactive Ajax web application (the weather application). Throughout the book,
the JavaScript part of the weather application is tested using different JavaScript unit
testing frameworks. The book illustrates how to generate test and code coverage
reports of developed JavaScript tests. It also explains how to automate the running of
JavaScript tests from build and continuous integration tools. The book shows how to
integrate different JavaScript unit testing frameworks with each other in order to test
web applications in the most efficient way.

Preface

What this book covers

Chapter 1, Unit Testing JavaScript Applications, helps you understand what unit
testing is, the requirements of a good unit test, and why unit testing is needed.
You will also learn the difference between Test-Driven Development and traditional
unit testing. You will understand the complexities of testing JavaScript code, and
the requirements of good, JavaScript unit testing tools. In this chapter, we will
explore the weather web application's JavaScript section which we will unit test
in the next chapters.
Chapter 2, Jasmine, helps you learn what Jasmine is and how to use it for testing
synchronous JavaScript code. You will learn how to test asynchronous (Ajax)
JavaScript code using the Jasmine Spies, waitsFor, and runs mechanisms. You will
learn how to perform mock Ajax testing using Jasmine. You will learn about the
various matchers provided by the framework, and how to load HTML fixtures in
your Jasmine tests. In this chapter, you will learn how to use Jasmine for testing the
weather application's JavaScript section.
Chapter 3, YUI Test, helps you to learn what YUI Test is and how to use this
JavaScript unit testing framework for testing synchronous JavaScript code. You will
learn how to test asynchronous (Ajax) JavaScript code using the YUI Test's wait and
resume mechanisms. You will learn about the various assertions provided by the
framework, how to display XML and JSON test reports using framework reporter
APIs, and how to generate test reports automatically using the YUI Test Selenium
Driver. You will learn how to automate running YUI tests using the YUI Test
Selenium Driver, and how to integrate an automation script with build management
and continuous integration tools. In this chapter, you will learn how to use YUI Test
for testing the weather application's JavaScript section.
Chapter 4, QUnit, helps you to understand what QUnit is and how to use it for
testing synchronous JavaScript code. You will learn how to test asynchronous
(Ajax) JavaScript code using the QUnit test mechanism and the QUnit asyncTest
mechanism. You will also learn the different assertions provided by the framework,
and how to develop your own assertion in order to simplify your test code. You will
learn how to load HTML fixtures in your QUnit tests. In this chapter, you will learn
how to use the framework for testing the weather application's JavaScript section.
Chapter 5, JsTestDriver, helps you to learn what JsTestDriver (JSTD) is, the JSTD
architecture, the JSTD configuration, and how to use JSTD for testing synchronous
JavaScript code. You will learn how to test asynchronous (Ajax) JavaScript code
using the JSTD AsyncTestCase object. You will learn the various assertions provided
by the framework, and how to generate test and code coverage reports using the
framework's code coverage plugin. You will learn how to use JSTD as a test runner
for the other JavaScript unit testing frameworks mentioned in the book, such as
[2]

Preface

Jasmine and QUnit, in order to enable the execution of the tests of these frameworks
from the command-line interface. You will learn how to integrate the tests of
JSTD (and the tests of the JavaScript frameworks on top of JSTD) with build and
continuous integration tools. You will learn how to work with the JSTD framework
in one of the most popular integrated development environments (IDEs) which
is Eclipse. In this chapter, you will learn how to use JSTD for testing the weather
application's JavaScript section.

What you need for this book

You will need the following software in order to run all of the examples in this book:
• Apache Tomcat 6, which can be found at http://tomcat.apache.org/
download-60.cgi

• Java Development Kit (JDK) Version 5.0 or later, which can be found at

http://www.oracle.com/technetwork/java/javase/downloads/index.
html

• The Selenium Server version 2.25.0 (for Chapter 3, YUI Test only), which can
be found at http://seleniumhq.org/download/
• Eclipse IDE (for Chapter 5, JsTestDriver only), which can be found at
http://www.eclipse.org/downloads/packages/release/indigo/sr2

Who this book is for

The target audience for this book is developers, designers, and architects of
web applications.

Conventions

In this book, you will find a number of styles of text that distinguish between
different kinds of information. Here are some examples of these styles, and an
explanation of their meaning.
Code words in text are shown as follows: "The validateLoginForm function
calls the LoginClient JavaScript object, which is responsible for validating the
login form."
A block of code is set as follows:
function validateLoginForm() {
var loginClient = new weatherapp.LoginClient();
[3]

Preface
var loginForm = {
"userNameField" :
"passwordField" :
"userNameMessage"
"passwordMessage"
};

"username",
"password",
: "usernameMessage",
: "passwordMessage"

return loginClient.validateLoginForm(loginForm);
}

When we wish to draw your attention to a particular part of a code block,
the relevant lines or items are set in bold:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>QUnit test runner</title>
<link rel="stylesheet" href="lib/qunit-1.10.0.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="lib/qunit-1.10.0.js"></script>
...The test code here...
</body>
</html>

Any command line input or output is written as follows:
java -jar JsTestDriver-1.3.4.b.jar --port 9876 --browser [firefoxpath],
[iepath],[chromepath]

New terms and important words are shown in bold. Words that you see on the
screen, in menus or dialog boxes, for example, appear in the text like this: "In this
application, the user enters his/her name and then clicks on the Welcome button."
Warnings or important notes appear in a box like this.

Tips and tricks appear like this.

[4]

Preface

Reader feedback

Feedback from our readers is always welcome. Let us know what you think about
this book—what you liked or may have disliked. Reader feedback is important for
us to develop titles that you really get the most out of.
To send us general feedback, simply send an e-mail to feedback@packtpub.com,
and mention the book title via the subject of your message.
If there is a topic that you have expertise in and you are interested in either writing
or contributing to a book, see our author guide at www.packtpub.com/authors.

Customer support

Now that you are the proud owner of a Packt book, we have a number of things
to help you to get the most from your purchase.

Downloading the example code

You can download the example code files for all Packt books you have purchased
from your account at http://www.PacktPub.com. If you purchased this book
elsewhere, you can visit http://www.PacktPub.com/support and register to have
the files e-mailed directly to you.

Errata

Although we have taken every care to ensure the accuracy of our content, mistakes
do happen. If you find a mistake in one of our books—maybe a mistake in the text or
the code—we would be grateful if you would report this to us. By doing so, you can
save other readers from frustration and help us improve subsequent versions of this
book. If you find any errata, please report them by visiting http://www.packtpub.
com/support, selecting your book, clicking on the errata submission form link, and
entering the details of your errata. Once your errata are verified, your submission
will be accepted and the errata will be uploaded on our website, or added to any list
of existing errata, under the Errata section of that title. Any existing errata can be
viewed by selecting your title from http://www.packtpub.com/support.

[5]

Preface

Piracy

Piracy of copyright material on the Internet is an ongoing problem across all media.
At Packt, we take the protection of our copyright and licenses very seriously. If you
come across any illegal copies of our works, in any form, on the Internet, please
provide us with the location address or website name immediately so that we can
pursue a remedy.
Please contact us at copyright@packtpub.com with a link to the suspected
pirated material.
We appreciate your help in protecting our authors, and our ability to bring you
valuable content.

Questions

You can contact us at questions@packtpub.com if you are having a problem with
any aspect of the book, and we will do our best to address it.

[6]

Unit Testing JavaScript
Applications
Before going into the details of unit testing JavaScript applications, we need to
understand first what unit testing is and why we need to unit test our applications.
This chapter also shows the complexities of testing JavaScript applications and
why it is not as simple as desktop applications. Finally, the chapter illustrates the
functionality and the JavaScript code of a sample weather application. We will unit
test its JavaScript code in the following chapters of the book.

What unit testing is

Unit testing is not a new concept in the software development world. Thanks to Kent
Beck, the concept of unit testing was introduced in Smalltalk, then the concept was
transferred to many other programming languages, such as C, C++, and Java. The
classical definition of unit testing is that it is a piece of code (usually a method) that
invokes another piece of code and later checks the correctness of some assumptions.
The definition is technically correct; however, it does not show us how to make
a really good unit test. In order to write a good unit test, we need to understand
the requirements of a good unit test.

Unit Testing JavaScript Applications

As shown in the following figure, a good unit test should be automated, repeatable,
easy to understand, incremental, easy to run, and fast.

A good unit test should be automated and repeatable, which means that other
team members can repeat running the application unit tests for every significant
code change automatically. It should also be easy to understand so that other team
members can understand what your test means and can continue adding more test
cases or updating an existing test case. A good unit test should be incremental; this
means that the unit test should be updated if a new relevant defect is detected in the
code, which means that this defect will not happen again as long as this unit test is
running periodically. Finally, a good unit test should be easy to run; it should run
by executing a command or by clicking a button and should not take a long time
for execution because fast unit tests can help in increasing the development
team's productivity.
So let's go back to the definition and refine it. Unit testing is a piece of code
(usually a method) that invokes another piece of code and checks the correctness
of some assumptions later. Unit testing should be automated, repeatable, easy to
understand, incremental, easy to run, and fast.

Why we need unit testing

Unit testing applications is not something nice to have. It is actually a mandatory
activity for having a successful software solutions that can cope with different
changes across time with high stability. There is no excuse to skip unit testing of
applications even for projects with a tight schedule. The importance of unit testing
may not appear in the early stages of the project; however, its advantages are visible
in the middle and the final stages of the project, when the code gets complicated,
more features are required, and more regression defects appear (defects that appear
again after a major code change).
[8]

Chapter 1

Without unit testing, the integration of the different components in the system
becomes complicated. This complexity results from the tracing of the defects of not
only the integration between the components but also each "buggy" component. This
complicates the life of the developers by making them spend nights in the office in
order to meet the schedule.
The number of new defects and the regression defects becomes unmanageable when
the code base becomes complicated and unit testing is not available. The developer
can resolve a specific defect and, after a set of code changes, this defect can happen
again because there is no repeatable test case to ensure that the defect will not
happen again.
Having more number of defects per lines of code affects the application's quality
badly, and this means that more time has to be spent on testing the application.
Bad quality applications have a longer test cycle for each project deployment
(or phase), because they have a high probability of having more defects for every
code change, which leads to more pressure on the project management, the project
developers, and the project testers.
Having good unit testing can be a good reference for the system documentation
because it contains the test scenarios of the system use cases. In addition to this,
unit testing shows how the system APIs are used, which reflect the current design
of the system. This means that unit testing is a powerful basis of code and design
refactoring for having more enhancements in the system.
Having good unit testing minimizes the number of regression defects because in good
unit testing the system has a repeatable number of test cases for every relevant defect.
Having a continuous integration job that runs periodically on the application unit tests
will ensure that these defects will not happen again, because if a specific defect appears
again due to a change in the application code, then the developer will be notified to fix
the defect and ensure that the test case of this defect passes successfully.
Continuous integration (CI) is a practice that ensures automating
the build and the test process of the application. In continuous
integration testing, the tests of the application source code run
periodically (for example many times per day) in order to identify
the application's potential problems and to reduce the integration
time of the application components.

As a result of reducing the regression defects, having good unit testing reduces the
test cycle for each phase (or system deployment). In addition to this, the application
can have more and more features per iterations or phases peacefully without
worrying if these features shall break an existing module that has good unit tests.
[9]

Unit Testing JavaScript Applications

What Test-Driven Development (TDD) is

There are two known approaches in writing unit tests for applications.
The first approach prefers writing unit tests after writing the actual application code
and this approach is called traditional unit testing. The second approach prefers
writing unit tests before writing the actual application code, and this approach is
called Test-Driven Development (TDD) or the Test-First approach.
As shown in the following figure, traditional unit testing is about writing the
application code first. It can simply be a class or a method. After writing the piece
of code, the unit tests, which test the functionality of the code, are written. Then the
unit tests run. If the unit tests fail then the developer fixes the defects and runs the
unit tests again. If the unit tests succeed then the developer can either refactor the
code and run the tests again or continue to write the next piece of code and so on.

As shown in the following figure, TDD starts by writing a failing unit test to indicate
that the functionality is missing. After writing the unit test, the unit test must be run
to ensure that it fails. After that, the developer writes the application code that meets
the unit test expectation. The unit test must be run again to ensure that it succeeds.
If it fails then the developer fixes the bugs and if it succeeds the developer can either
refactor the application code or continue writing the next test case.

[ 10 ]

Chapter 1

TDD is a powerful technique, as it can give you more control on the application code
and design; however, it is a double-edged sword because if it is done incorrectly,
writing the tests can waste a lot of time and the schedule of the project can slip.
Finally, either you are using TDD or traditional unit testing technique. Don't forget
to make your tests automated, repeatable, easy to understand, incremental, easy to
run, and fast.

Complexities in testing JavaScript
applications

Testing JavaScript applications is complex and requires a lot of time and
effort. Testing JavaScript applications requires the tester to test the application
on different browsers (Internet Explorer, Firefox, Safari, Chrome, and so on).
This is because the JavaScript code that runs on a specific browser will not
necessarily work on another browser.
Testing existing JavaScript web applications (with many web pages) on new
browsers that are not supported by the application code is not a flexible process.
Supporting a new unsupported browser means allocating more time for testing the
application again on this new browser and for the new/regression defects to be fixed
by the developers. Let's see a simple Broken JavaScript example, which illustrates
this idea. In this example, the user enters his/her name and then clicks on the
Welcome button. After that the welcome message appears.
[ 11 ]

Unit Testing JavaScript Applications

The following code snippet shows the broken JavaScript example:
<!DOCTYPE html>
<html>
<head>
<title>Broken JavaScript Example</title>
<script type=»text/javascript»>
function welcome() {
var userName = document.getElementById(«userName»).value;
document.getElementById(«welcomeMessage»).innerText = «Welcome «
+ userName + «!»;
}
</script>
</head>
<body>
<h1>Broken JavaScript Example</h1>
<label>Please enter your name:</label>
<input id=»userName» type=»text» /><br/>
<input type=»button» onclick=»welcome()» value=»Welcome»></
input><br/><br/>
<div id=»welcomeMessage»/>
</body>
</html>

Downloading the example code
You can download the example code files for all Packt books you have
purchased from your account at http://www.PacktPub.com. If you
purchased this book elsewhere, you can visit http://www.PacktPub.
com/support and register to have the files e-mailed directly to you.

If you run the code shown in the previous code snippet, you will find that it works
fine in Internet Explorer (IE) and Safari while it does not work in Firefox (to be more
specific, this example works on Internet Explorer 8 and Safari 5.1, while it will not
work on Firefox 3.6). The reason behind this problem is that the innerText property
is not supported in Firefox. This is one of the hundreds of examples that show a code
that works in a specific browser while it does not work in another one.
As a result of these complexities, testing JavaScript code requires a good unit
testing tool, which provides mechanisms to overcome these complexities. The good
JavaScript unit testing tool should be able to execute the test cases across all of the
browsers, should have an easy setup, should have an easy configuration, and should
be fast in executing the test cases.
[ 12 ]

Chapter 1

Weather forecasting application

Now, let's move to the weather forecasting application. The weather forecasting
application is a Java web application that allows the users to check the current
weather in different cities in the world. The weather forecasting application contains
both synchronous and asynchronous (Ajax) JavaScript code, which we will test in the
later chapters of the book using the different JavaScript unit testing frameworks.
The weather forecasting application mainly contains three use cases:
• Log in to the application
• Register a user in the application
• Check the current weather in a specific city
The weather forecasting application is a Java web application. The server-side part of
the application is written using Java servlets (http://docs.oracle.com/javaee/6/
tutorial/doc/bnafd.html). If you are not familiar with Java servlets, do not
worry. This book focuses only on JavaScript unit testing; all you need to know about
these servlets is the functionality of each one of them, not the code behind it. The
functionality of each application servlet will be explained during when the JavaScript
code is explained, to show you the complete Ajax request life cycle with the server.
Another thing that needs to be mentioned is that the weather application pages
are .jsp files; however, 99 percent of their code is pure HTML code that is easy to
understand (the application pages code will be explained in detail in the next section).
The first screen of the application is the login screen in which the user enters his
username and password, as shown in the following screenshot:

[ 13 ]

Unit Testing JavaScript Applications

When the user clicks on the Login button, there is a JavaScript login client that
ensures that the username and the password are entered correctly. If the username
and the password are correct, they are submitted to the server, which validates them
if the user is registered in the application. If the user is registered in the application
then the user is redirected to the weather checking page; otherwise an error message
appears to the user.
The username field must not be empty and has to be in a valid e-mail address format.
The password field also must not be empty and has to contain at least one digit, one
capital, one small character, and at least one special character. The password length
has to be six characters or more.
In the weather checking page, the user can select one of the available cities from the
combobox, then click on the Get weather condition button to get the current weather
information of the selected city, as shown in the following screenshot:

In the user registration page, the user can register in the application by entering
his username and confirmed password, as shown in the following screenshot:

[ 14 ]

Chapter 1

When the user clicks on the Register button, the registration client's JavaScript object
ensures that the username and the passwords are entered correctly. The registration
client uses the same rules of the login client in username and password validations.
It also ensures that the confirmed password is the same as the entered password.
If the user's registration information is correct, the username and passwords are
submitted to the server. The user information is registered in the system after
performing server-side validations and checking that the user has not already
registered in the application. If the user is already registered in the system then
an error message appears to the user.

Exploring the application's HTML and
JavaScript code

The following code snippet shows the HTML code of the login form in the login.
jsp file. It is a simple form that has username and password fields with their labels,
messages, a registration link, and a login button.
<form class="box login" action="/weatherApplication/LoginServlet"
method="post">
<fieldset class="boxBody">
<label for="username">Username <span id="usernameMessage"
class="error"></span></label>
<input type="text" id="username" name="username"/>
<label for="password">Password <span id="passwordMessage"
class="error"></span></label>
<input type="password" id="password" name="password"/>
</fieldset>
<div id="footer">
[ 15 ]

Unit Testing JavaScript Applications
<label><a href="register.jsp">Register</a></label>
<input id="btnLogin" class="btnLogin" type="submit" value="Login"
onclick="return validateLoginForm();"/>
</div>
</form>

When the Login button is clicked, the validateLoginForm JavaScript function is
called. The following code snippet shows the validateLoginForm function in the
login.jsp file:
function validateLoginForm() {
var loginClient = new weatherapp.LoginClient();
var loginForm = {
"userNameField" :
"passwordField" :
"userNameMessage"
"passwordMessage"
};

"username",
"password",
: "usernameMessage",
: "passwordMessage"

return loginClient.validateLoginForm(loginForm);
}

The validateLoginForm function calls the LoginClient JavaScript object that
is responsible for validating the login form. It constructs a JavaScript Object
Notation (JSON) object that includes the username, password, username message,
and password message IDs, and then passes the constructed JSON object to the
validateLoginForm function of the LoginClient object.
The weather application customizes a CSS3 based style from the
blog CSS Junction:
http://www.cssjunction.com/freebies/simple-loginfrom-html5css3-template-free/

The following code snippet shows the validateLoginForm method of the
LoginClient object in the LoginClient.js file. It validates that the username
and the password fields are not empty and are compliant with the validation rules.
if (typeof weatherapp == "undefined" || !weatherapp) {
weatherapp = {};
}
weatherapp.LoginClient = function() {};
weatherapp.LoginClient.prototype.validateLoginForm =
[ 16 ]

Chapter 1
function(loginForm) {
if (this.validateEmptyFields(loginForm) &&
this.validateUserName(loginForm) &&
this.validatePassword(loginForm)) {
return true;
}
return false;
};

One of the recommended JavaScript's best practices is to use
namespaces; the application defines a JavaScript namespace in order
to avoid collisions with other JavaScript objects of similar names. The
following code defines a weatherapp namespace if it is not already
defined:
if (typeof weatherapp == "undefined" || !weatherapp) {
weatherapp = {};
}

The following code snippet shows the validateEmptyFields method of the
LoginClient object in the LoginClient.js file. It validates that the username
and the password fields are not empty and if any of these fields are empty,
an error message appears:
weatherapp.LoginClient.prototype.validateEmptyFields =
function(loginForm) {
var passwordMessageID = loginForm.passwordMessage;
var userNameMessageID = loginForm.userNameMessage;
var passwordFieldID = loginForm.passwordField;
var userNameFieldID = loginForm.userNameField;
document.getElementById(passwordMessageID).innerHTML = "";
document.getElementById(userNameMessageID).innerHTML = "";
if (! document.getElementById(userNameFieldID).value) {
document.getElementById(userNameMessageID).innerHTML = "(field is
required)";
return false;
}

[ 17 ]

Unit Testing JavaScript Applications
if (! document.getElementById(passwordFieldID).value) {
document.getElementById(passwordMessageID).innerHTML = "(field is
required)";
return false;
}
return true;
};

The following code snippet shows the validateUserName method of the
LoginClient object in the LoginClient.js file. It validates that the username
is in the form of a valid e-mail:
weatherapp.LoginClient.prototype.validateUserName =
function(loginForm) {
// the username must be an email...
var userNameMessageID = loginForm.userNameMessage;
var userNameFieldID = loginForm.userNameField;
var userNameRegex = /^[_A-Za-z0-9-]+(\.[_A-Za-z0-9-]+)*@
[A-Za-z0-9]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$/;
var userName = document.getElementById(userNameFieldID).value;
if(! userNameRegex.test(userName)) {
document.getElementById(userNameMessageID).innerHTML = "(format is
invalid)";
return false;
}
return true;
};

Using the regular expression /^[_A-Za-z0-9-]+(\.[_A-Za-z0-9-]+)*@
[A-Za-z0-9]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$/, the username is validated
against a valid e-mail form. If the username is not in a valid e-mail form then an
error message appears in the username message span.
The following code snippet shows the validatePassword method of the
LoginClient object in the LoginClient.js file. It validates if the password has
at least one digit, one capital character, one small character, at least one special
character, and also if it contains six characters or more:
weatherapp.LoginClient.prototype.validatePassword =
[ 18 ]

Chapter 1
function(loginForm) {
// the password contains at least one digit, one capital and small
character
// and at least one special character, and 6 characters or more...
var passwordMessageID = loginForm.passwordMessage;
var passwordFieldID = loginForm.passwordField;
var passwordRegex = /((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).
{6,20})/;
var password = document.getElementById(passwordFieldID).value;
if (! (passwordRegex.test(password) && password.length >= 6)) {
document.getElementById(passwordMessageID).innerHTML = "(format is
invalid)";
return false;
}
return true;
};

If the password is not compliant with the mentioned rules then an error message
appears in the password message span.
If the username and the password fields pass the JavaScript validation rules, the
login form submits its content to LoginServlet, which makes another server-side
validation and then redirects the user to the weather checking page if the validation
goes OK.
It is very important not to rely on the JavaScript client-side validation
only, because JavaScript can be disabled from the browser. So it is a
must to always make a server-side validation besides the client-side
validation.

The following code snippet shows the weather checking form of the weather
application located in the welcome.jsp file. It contains a combobox filled with the
Yahoo! Weather Where On Earth IDs (the WOEID is a unique reference identifier
assigned by Yahoo! to identify any place on Earth) of different cities in the world.
<h1>Welcome to the weather application</h1>
<FORM method="post">
<label class="label" for="postalCode">Select the Location: </label>
<select id="w" class="selectField">
<option value="1521894">Cairo, Egypt</option>
[ 19 ]

Unit Testing JavaScript Applications
<option
<option
<option
<option
<option
<option
</select>

value="906057">Stockholm, Sweden</option>
value="551801">Vienna, Austria</option>
value="766273">Madrid, Spain</option>
value="615702">Paris, France</option>
value="2459115">New York, USA</option>
value="418440">Lima, Peru</option>

<input type="button" class="button" onclick="invokeWeatherClient();"
value="Get weather condition"/>
<br/><br/>
<div id="weatherInformation" class="weatherPanel">
</div>
</FORM>

When the Get weather condition button is clicked, the invokeWeatherClient
function is called. The following code snippet shows the invokeWeatherClient
function code in the welcome.jsp file:
function invokeWeatherClient() {
var weatherClient = new weatherapp.WeatherClient();
var location = document.getElementById("w").value;
weatherClient.getWeatherCondition({
'location': location,
'resultDivID': 'weatherInformation'
},
weatherClient.displayWeatherInformation,
weatherClient.handleWeatherInfoError);
}

The invokeWeatherClient function calls the getWeatherCondition method of the
WeatherClient object. The first parameter of the getWeatherCondition method
is the weatherForm object, which is a JSON object containing the location WOEID
and the ID of the DIV element that receives the weather information HTML result
of the Yahoo! Weather Representational State Transfer (REST) service. The second
parameter represents the first callback, which is the displayWeatherInformation
method that is called if the getWeatherCondition call succeeds. The last parameter
represents the second callback, which is the handleWeatherInfoError method that
is called if the getWeatherCondition call fails.
The following code snippet shows getWeatherCondition of the
WeatherClient object in the WeatherClient.js file that sends an Ajax request
to WeatherProxyServlet with the w parameter that represents the WOEID.
[ 20 ]

Chapter 1

WeatherProxyServlet interacts with the Yahoo! Weather REST service in order to

fetch the current weather information:

if (typeof weatherapp == "undefined" || !weatherapp) {
weatherapp = {};
}
weatherapp.WeatherClient = function() {};
weatherapp.WeatherClient.xmlhttp;
weatherapp.WeatherClient.weatherForm;
weatherapp.WeatherClient.endpointURL = "";
weatherapp.WeatherClient.prototype.getWeatherCondition =
function(weatherForm, successCallBack, failureCallBack) {
if (window.XMLHttpRequest) {
this.xmlhttp = new XMLHttpRequest();
} else {
this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
var successCallBackLocal = successCallBack;
var failureCallBackLocal = failureCallBack;
var weatherClientLocal = this;
this.xmlhttp.onreadystatechange = function() {
weatherClientLocal.weatherInformationReady(successCallBackLocal,
failureCallBackLocal);
};
this.weatherForm = weatherForm;
if (typeof this.endpointURL == "undefined") {
this.endpointURL = "";
}
this.xmlhttp.open("GET",
this.endpointURL +
"/weatherApplication/WeatherProxyServlet?w=" + weatherForm.
location + "&preventCache=" + new Date().getTime(),
true);
this.xmlhttp.send();
};
[ 21 ]

Unit Testing JavaScript Applications
weatherapp.WeatherClient.prototype.weatherInformationReady =
function(successCallBack, failureCallBack) {
if (this.xmlhttp.readyState != 4) {
return;
}
if (this.xmlhttp.status != 200)
failureCallBack(this);

{

return;
}
if (this.xmlhttp.readyState == 4 && this.xmlhttp.status == 200) {
successCallBack(this);
}
};
weatherapp.WeatherClient.prototype.displayWeatherInformation =
function(weatherClient) {
var resultDivID = weatherClient.weatherForm.resultDivID;
document.getElementById(resultDivID).innerHTML = weatherClient.
xmlhttp.responseText;
};
weatherapp.WeatherClient.prototype.handleWeatherInfoError =
function(weatherClient) {
var resultDivID = weatherClient.weatherForm.resultDivID;
alert ("Error: " + weatherClient.xmlhttp.responseText);
document.getElementById(resultDivID).innerHTML = "Error: " +
weatherClient.xmlhttp.responseText;
};

The getWeatherCondition method first creates an XML HTTP request object using
new XMLHttpRequest() in case of IE7+, Firefox, Chrome, and Opera. In the case of
IE5 and IE6, the XML HTTP request object is created using an ActiveX object new
ActiveXObject("Microsoft.XMLHTTP").
The getWeatherCondition method then registers both, the success callback
(successCallBack) and the failure callback (failureCallBack) using the
weatherInformationReady method that is called for every Ajax readyState change.

[ 22 ]

Chapter 1

Finally, the getWeatherCondition method sends an asynchronous Ajax request
to WeatherProxyServlet. When the Ajax response comes from the server and
the operation is done successfully then the success callback is called, which is the
displayWeatherInformation method. In the case of operation failure (which can
happen, for example, if the passed WOEID is invalid or the Yahoo! Weather service is
down), the failure callback is called, which is the handleWeatherInfoError method.
The displayWeatherInformation method displays the returned weather
information HTML result from WeatherProxyServlet (which fetches the weather
information from the Yahoo! Weather REST service) in the weatherInformation div
element while the handleWeatherInfoError method displays the error message in
the same div element and also displays an alert with the error message.
It is assumed that you are familiar with Ajax programming. If you are
not familiar with Ajax programming, it is recommended to check the
following introductory Ajax tutorial on w3schools:
http://www.w3schools.com/ajax/default.asp

In order to prevent IE from caching Ajax GET requests, a random parameter is
appended using new Date().getTime(). In many JavaScript libraries, this can
be handled through the framework APIs. For example, in Dojo the preventCache
attribute of the dojo.xhrGet API can be used to prevent the IE Ajax GET caching.
The following code snippet shows the HTML code of the registration form in the

register.jsp file. It consists of a username and two password fields with their

corresponding labels, messages, login link, and a register button:
<form class="box register" method="post">
<fieldset class="boxBody">

<label for="username">Username (Email) <span id="usernameMessage"
class="error"></span></label>
<input type="text" id="username" name="username"/>
<label for="password1">Password <span id="passwordMessage1"
class="error"></span></label>
<input type="password" id="password1" name="password1"/>
<label for="password2">Confirm your password</label>
<input type="password" id="password2" name="password2"/>
</fieldset>
<div id="footer">
<label><a href="login.jsp">Login</a></label>
[ 23 ]

Unit Testing JavaScript Applications
<input id="btnRegister" class="btnLogin" type="button"
value="Register" onclick="registerUser();" />
</div>
</form>

When the Register button is clicked, the registerUser JavaScript function is called.
The following code snippet shows the code of the registerUser function in the
register.jsp file:
function registerUser() {
var registrationClient = new weatherapp.RegistrationClient();
var registrationForm = {
"userNameField" : "username",
"passwordField1" : "password1",
"passwordField2" : "password2",
"userNameMessage" : "usernameMessage",
"passwordMessage1" : "passwordMessage1"
};
if (registrationClient.validateRegistrationForm(registrationForm)) {
registrationClient.registerUser(registrationForm,
registrationClient.displaySuccessMessage,
registrationClient.handleRegistrationError);
}
}

The registerUser function is calling the RegistrationClient JavaScript object
that is responsible for validating and submitting the registration form using Ajax
to RegistrationServlet. registerUser constructs the registrationForm
JSON object, which includes the username, password1, password2, username
message, and password1 message IDs, and then passes the object to the
validateRegistrationForm method of the RegistrationClient object.
If the validation passes, it calls the registerUser method of the
RegistrationClient object. The first parameter of the registerUser method is
the registrationForm JSON object. The second parameter is the success callback,
which is the displaySuccessMessage method, while the last parameter is the failure
callback, which is the handleRegistrationError method.
The following code snippet shows the code of the validateRegistrationForm
method of the RegistrationClient object in the RegistrationClient.js file. It
uses the validation methods of LoginClient in order to validate the empty username
[ 24 ]

Chapter 1

and password fields, and to validate if the username and the password fields conform
to the validation rules. In addition to this, the validateRegistrationForm method
validates if the two entered passwords are identical:
if (typeof weatherapp == "undefined" || !weatherapp) {
weatherapp = {};
}
weatherapp.RegistrationClient = function() {};
weatherapp.RegistrationClient.xmlhttp;
weatherapp.RegistrationClient.endpointURL = "";
weatherapp.RegistrationClient.prototype.validateRegistrationForm =
function(registrationForm) {
var userNameMessage = registrationForm.userNameMessage;
var passwordMessage1 = registrationForm.passwordMessage1;
var userNameField = registrationForm.userNameField;
var passwordField1 = registrationForm.passwordField1;
var passwordField2 = registrationForm.passwordField2;
var password1 = document.getElementById(passwordField1).value;
var password2 = document.getElementById(passwordField2).value;
// Empty messages ...
document.getElementById(userNameMessage).innerHTML = "";
document.getElementById(passwordMessage1).innerHTML = "";
// create the loginClient object in order to validate fields ...
var loginClient = new weatherapp.LoginClient();
var loginForm = {};
loginForm.userNameField =
loginForm.userNameMessage
loginForm.passwordField =
loginForm.passwordMessage

userNameField;
= userNameMessage;
passwordField1;
= passwordMessage1;

// validate empty username and password fields.
if (! loginClient.validateEmptyFields(loginForm)) {
return false;
}
// validate that password fields have the same value...
[ 25 ]

Unit Testing JavaScript Applications
if (password1 != password2) {
document.getElementById(passwordMessage1).innerHTML = "(Passwords must
be identical)";
return false;
}
// check if the username is correct...
if (! loginClient.validateUserName(loginForm) ) {
document.getElementById(userNameMessage).innerHTML = "(format is
invalid)";
return false;
}
// check if the password is correct...
if (! loginClient.validatePassword(loginForm) ) {
document.getElementById(passwordMessage1).innerHTML = "(format is
invalid)";
return false;
}
return true;
};

The following code snippet shows the registerUser method code of the
RegistrationClient object in the RegistrationClient.js file. It creates
an Ajax POST request with the username and the passwords' (original password
and confirmed password) data and sends them asynchronously
to RegistrationServlet.
weatherapp.RegistrationClient.prototype.registerUser =
function(registrationForm, successCallBack, failureCallBack) {
var userNameField = registrationForm.userNameField;
var passwordField1 = registrationForm.passwordField1;
var passwordField2 = registrationForm.passwordField2;
var userName = document.getElementById(userNameField).value;
var password1 = document.getElementById(passwordField1).value;
var password2 = document.getElementById(passwordField2).value;
if (window.XMLHttpRequest) {
this.xmlhttp = new XMLHttpRequest();
} else {
this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
[ 26 ]

Chapter 1
var successCallBackLocal = successCallBack;
var failureCallBackLocal = failureCallBack;
var registrationClientLocal = this;
this.xmlhttp.onreadystatechange = function() {
registrationClientLocal.registrationReady(successCallBackLocal,
failureCallBackLocal);
};
if (typeof this.endpointURL == "undefined") {
this.endpointURL = "";
}
this.xmlhttp.open("POST",
this.endpointURL +
"/weatherApplication/RegistrationServlet",
true);
this.xmlhttp.setRequestHeader("Content-type","application/x-www-formurlencoded");
this.xmlhttp.send(userNameField + "=" + userName + "&" +
passwordField1 + "=" + password1 + "&" +
passwordField2 + "=" + password2);
};
weatherapp.RegistrationClient.prototype.registrationReady =
function(successCallBack, failureCallBack) {
if (this.xmlhttp.readyState != 4) {
return;
}
if (this.xmlhttp.status != 200)
failureCallBack(this);
return;
}

{

if (this.xmlhttp.readyState == 4 && this.xmlhttp.status == 200) {
successCallBack(this);
}
};
weatherapp.RegistrationClient.prototype.displaySuccessMessage =
function(registrationClient) {
alert("User registration went successfully ...");
};
weatherapp.RegistrationClient.prototype.handleRegistrationError =
[ 27 ]

Unit Testing JavaScript Applications
function(registrationClient) {
alert(registrationClient.xmlhttp.responseText);
};

RegistrationServlet validates the user data and ensures that the user did not

already register in the application. When the Ajax response comes from the server,
and the registration operation is completed successfully, the displaySuccessMessage
method is called. If the registration operation failed (for example, if the user ID is
already registered in the application), the handleRegistrationError method is
called. Both the displaySuccessMessage and the handleRegistrationError
methods display alerts to show the success and the failure registration messages.

Running the weather application

In order to run the weather application, you first need to download the
weatherApplication.war file from the book's website (www.packtpub.com).
Then you need to deploy the WAR file on Apache Tomcat 6. In order to install
Apache Tomcat 6, you need to download it from http://tomcat.apache.org/
download-60.cgi. Apache Tomcat 6.0 requires the Java 2 Standard Edition
Runtime Environment (JRE) Version 5.0 or later.
In order to install JRE, you need to download and install the J2SE Runtime
Environment as follows:
1. Download the JRE, release Version 5.0 or later, from http://www.oracle.
com/technetwork/java/javase/downloads/index.html.
2. Install the JRE according to the instructions included with the release.
3. Set an environment variable named JRE_HOME to the pathname of the
directory in which you installed the JRE, for example, c:\jre5.0 or /usr/
local/java/jre5.0.
After you download the binary distribution of Apache Tomcat 6, you need to
unpack the distribution in a suitable location on the hard disk. After this, you need
to define the CATALINA_HOME environment variable, which refers to the location of
the Tomcat distribution.
Now, you can start Apache Tomcat 6 by executing the following command
on Windows:
$CATALINA_HOME\bin\startup.bat

[ 28 ]

Chapter 1

Start as while in Unix, you can execute the following command:
$CATALINA_HOME/bin/startup.sh

In order to make sure that the Apache Tomcat 6 starts correctly, you need to type the
following URL in the browser:
http://localhost:8080/

After making sure that the Apache Tomcat 6 is running correctly, you can stop it by
executing the following command on Windows:
$CATALINA_HOME\bin\shutdown.bat

Start as while in Unix, you can execute the following command:
$CATALINA_HOME/bin/shutdown.sh

Now, we come to the step of the weather application deployment where you need to
get the weatherApplication.war file from the book resources. After getting the file,
copy the WAR file to the $CATALINA_HOME\webapps folder, then start the Apache
Tomcat 6 again.
In order to access the weather application, you can access it using the following URL:
http://localhost:8080/weatherApplication/login.jsp
For the sake of simplicity, there is a predefined username and password
that can be used to access the weather application; the username is
admin@123.com and the password is Admin@123. Another thing
that has to be mentioned is that the registered users are not stored in a
database; they are stored in the application scope, which means they
will be available as long as the application is not restarted.

Summary

In this chapter, you learned what unit testing is, the requirements of a good unit
test, and why we need unit testing. You got to know the difference between the
Test-Driven Development and the traditional unit testing. In the JavaScript world,
you understood the complexities of testing JavaScript code, and the requirements of
good JavaScript unit testing tools. At the end of this chapter, I explored with you the
weather web application use cases and its JavaScript code in detail, which we will
unit test in the later chapters. In the next chapter, you will learn how to work with
the Jasmine framework and how to use it for testing the weather application.

[ 29 ]

Jasmine
Jasmine is a powerful JavaScript unit testing framework. It provides a clean
mechanism for testing synchronous and asynchronous JavaScript code. Jasmine is
a behavior-driven development framework that provides descriptive test cases that
focus more on the business value than on the technical details. Because it is written
in a simple natural language, Jasmine tests can be read by non-programmers and can
provide a clear description when a single test succeeds or fails and also the reason
behind its failure. In this chapter, the framework will be illustrated in detail and will
be used to test the weather application that is discussed in Chapter 1, Unit Testing
JavaScript Applications.
Behavior-driven development (BDD) is an agile software development
technique introduced by Dan North that focuses on writing descriptive
tests from the business perspective. BDD extends TDD by writing
test cases that test the software behavior (requirements) in a natural
language that anyone (not necessarily a programmer) can read and
understand. The names of the unit tests are sentences that usually
start with the word "should" and they are written in the order of their
business value.

Configuration

In order to configure Jasmine, the first step is to download the framework from
https://github.com/pivotal/jasmine/downloads. Here, you will find the latest
releases of the framework. At the time of this writing, the latest release is v1.2.0,
which has been used in this book.

Jasmine

After unpacking jasmine-standalone-1.2.0.zip (or later), you will find the folder
structure shown in the following screenshot:

The src folder in the preceding screenshot contains the JavaScript source files
that you want to test, the spec folder contains the JavaScript testing files, while
SpecRunner.html is the test case runner HTML file. The lib folder contains the
framework files.
In order to make sure that everything is running OK, click on the SpecRunner.html
file; you should see passing specs, as shown in the following screenshot:

This structure is not rigid; you can modify it to serve the organization of your
application. For the purpose of testing the weather application, we will modify
it to cope with the structure of the application.

Writing your first Jasmine test

Before writing the first Jasmine test, we will need to understand the difference
between a suite and a spec (test specification) in Jasmine. A Jasmine suite is a
group of test cases that can be used to test a specific behavior of the JavaScript
[ 32 ]

Chapter 2

code (a JavaScript object or function). In Jasmine, the test suite begins with a call
to the Jasmine global function describe with two parameters. The first parameter
represents the title of the test suite, while the second parameter represents a function
that implements the test suite.
A Jasmine spec represents a test case inside the test suite. In Jasmine, a test case
begins with a call to the Jasmine global function it with two parameters. The first
parameter represents the title of the spec and the second parameter represents a
function that implements the test case.
A Jasmine spec contains one or more expectations. Every expectation represents
an assertion that can be either true or false. In order to pass the spec, all of the
expectations inside the spec have to be true. If one or more expectations inside a spec
is false, the spec fails. The following code snippet shows an example of a Jasmine test
suite and a spec with an expectation:
describe("A sample suite", function() {
it("contains a sample spec with an expectation", function() {
expect(true).toEqual(true);
});
});

Now, let's move to the SimpleMath JavaScript object, which is described in the
following code snippet. The SimpleMath JavaScript object is a simple mathematical
utility that performs factorial, signum, and average mathematical operations.
SimpleMath = function() {
};
SimpleMath.prototype.getFactorial = function (number) {
if (number < 0) {
throw new Error("There is no factorial for negative numbers");
}
else if (number == 1 || number == 0) {
// If number <= 1 then number! = 1.
return 1;
} else {
// If number > 1 then number! = number * (number-1)!
return number * this.getFactorial(number-1);
}
}

[ 33 ]

Jasmine
SimpleMath.prototype.signum = function (number) {
if (number > 0) {
return 1;
} else if (number == 0) {
return 0;
} else {
return -1;
}
}
SimpleMath.prototype.average = function (number1, number2) {
return (number1 + number2) / 2;
}

In the preceding code snippet, the SimpleMath object is used to calculate the factorial
of numbers. In mathematics, the factorial function of a nonnegative integer n, which
is denoted by n!, is the product of all positive integers less than or equal to n. For
example, 4! = 4 x 3 x 2 x 1 = 24. According to Wikipedia, the factorial function has the
following mathematical definition:

The SimpleMath object calculates the factorial of the number using the
getFactorial recursive function. It throws an Error exception when the passed
parameter to the getFactorial method is a negative number because there is no
factorial value for negative numbers.
In addition to calculating the factorial of numbers, it can get the signum of any
number using the signum method. In mathematics, the signum function extracts
the sign of a real number. According to Wikipedia, the signum function has the
following mathematical definition:

Finally, SimpleMath can calculate the average of two numbers using the average
method. The average value of two numbers can be calculated by dividing the sum
of the two numbers by 2.

[ 34 ]

Chapter 2

Now, let's start writing the specs using Jasmine. First of all, in order to test the
getFactorial method, let's have the three following test scenarios; we will test
calculating the factorial of:
• A positive number
• Zero
• A negative number
Boundary testing is a kind of testing that focuses on the boundary
or the limit conditions of the objects to be tested. These boundary
conditions can include the maximum value, minimum value, error
values, and inside/outside boundary values. In the factorial testing
example, the test scenarios apply this kind of testing by testing the
factorial API with a positive number, a negative number, and zero.

The following code snippet shows how to test the calculation of the factorial
of a positive number (3), 0, and a negative number (-10):
describe("SimpleMath", function() {
var simpleMath;
beforeEach(function() {
simpleMath = new SimpleMath();
});
describe("when SimpleMath is used to find factorial", function() {
it("should be able to find factorial for positive number",
function() {
expect(simpleMath.getFactorial(3)).toEqual(6);
});
it("should be able to find factorial for zero", function() {
expect(simpleMath.getFactorial(0)).toEqual(1);
});
it("should be able to throw an exception when the number is
negative", function() {
expect(
function() {
simpleMath.getFactorial(-10)
}).toThrow("There is no factorial for negative numbers");
});
});

});
[ 35 ]

Jasmine

The describe keyword declares a new test suite called "SimpleMath". beforeEach
is used for initialization of the specs inside the suite, that is, beforeEach is called
once before the run of each spec in the describe function. In the beforeEach
function, the simpleMath object is created using new SimpleMath().
In Jasmine, it is also possible to execute JavaScript code after the run
of each spec in the describe function, using the afterEach global
function. Having beforeEach and afterEach in Jasmine allows the
developer not to repeat setup and finalization code for each spec.

After initializing the simpleMath object, you can either create a direct spec using
the it keyword or create a child test suite using the describe keyword. For the
purpose of organizing the example, a new test suite is created for each group of tests
with similar functionalities. This is why an independent test suite is created to test
the functionality of the getFactorial test suite provided by the SimpleMath object
using the describe keyword.
In the first test scenario of the getFactorial test suite, the spec title is "should
be able to find factorial for positive number", and the expect function
calls simpleMath.getFactorial(3) and expects it to be equal to 6. If simpleMath.
getFactorial(3) returns a value other than 6, the test fails. We have many other
options (matchers) to use instead of toEqual. These matchers will be discussed in
more detail in the Jasmine matchers section.
In the second test scenario of the getFactorial test suite, the expect function
calls simpleMath.getFactorial(0) and expects it to be equal to 1. In the last test
scenario of the getFactorial test suite, the expect function calls simpleMath.
getFactorial(-10) and expects it to throw an exception with the message "There is
no factorial for negative numbers", using the toThrow matcher. The toThrow
matcher succeeds if the function expect throws an exception when executed.
After finalizing the getFactorial test suite, we come to a new test suite that tests
the functionality of the signum method provided by the SimpleMath object. The
following code snippet shows the signum test suite:
describe("when SimpleMath is used to find signum", function() {
it("should be able to find the signum for a positive number",
function() {
expect(simpleMath.signum(3)).toEqual(1);
});
it("should be able to find the signum for zero", function() {
expect(simpleMath.signum(0)).toEqual(0);
});
[ 36 ]

Chapter 2
it("should be able to find the signum for a negative number",
function() {
expect(simpleMath.signum(-1000)).toEqual(-1);
});
});

We have three test scenarios for the signum method, the first test scenario is about
getting the signum value for a positive number, the second test scenario is about
getting the signum value for zero, and the last test scenario is about getting the signum
value for a negative number. As indicated in the definition of the signum function,
it has to return +1 for any positive number, 0 for zero, and finally -1 for any negative
number. The following code snippet shows the average test suite:
describe("when SimpleMath is used to find the average of two
values", function() {
it("should be able to find the average of two values",
function() {
expect(simpleMath.average(3, 6)).toEqual(4.5);
});
});

In the average spec, the test ensures that the average is calculated correctly by trying
to calculate the average of two numbers, 3 and 6, and expecting the result to be 4.5.
Now, after writing the suites and the specs, it is the time to run the tests. In order to
run the tests, we need to do the following steps:
1. Place the simpleMath.js file in the src folder.
2. Place the simpleMathSpec.js file ,which contains the SimpleMath unit tests,
in the spec folder.
3. Edit the SpecRunner.html file as shown in the following code snippet:
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="shortcut icon" type="image/png"
href="lib/jasmine-1.2.0/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="lib/jasmine1.2.0/jasmine.css">
<script type="text/javascript" src="lib/jasmine1.2.0/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine1.2.0/jasmine-html.js"></script>

[ 37 ]

Jasmine
<!-- include spec files here... -->
<script type="text/javascript"
src="spec/simpleMathSpec.js"></script>
<!-- include source files here... -->
<script type="text/javascript"
src="src/simpleMath.js"></script>

As shown in the preceding code snippet, in the highlighted lines, <script

type="text/javascript" src="spec/simpleMathSpec.js"></script> is
added under the <!-- include spec files here... --> section, while <script
type="text/javascript" src="src/simpleMath.js"></script> is added under
the <!-- include source files here... --> section. After double-clicking on
SpecRunner.html, you will see the test results passed.

The nested describe blocks

Jasmine is flexible in nesting the describe blocks with specs at any level. This
means that, before executing a spec, Jasmine walks down executing each beforeEach
function in order, then executes the spec, and lastly walks up executing each
afterEach function.
The following code snippet is an example of the Jasmine's nested describe blocks:
describe("MyTest", function() {
beforeEach(function() {
alert("beforeEach level1");
});
describe("MyTest level2", function() {
beforeEach(function() {
alert("beforeEach level2");
});
describe("MyTest level3", function() {
beforeEach(function() {
alert("beforeEach level3");
});
it("is a simple spec in level3", function() {
alert("A simple spec in level3");
expect(true).toBe(true);
});
afterEach(function() {
alert("afterEach level3");
});
});
[ 38 ]


Related documents


PDF Document spencer   reliable javascript
PDF Document mcpeak   beginning javascript
PDF Document urakhchin serge full stack dev resume online
PDF Document programmingthewebunit5
PDF Document saleh   javascript unit testing
PDF Document registration flyer spring 2016


Related keywords