O'Reilly logo

Testable JavaScript by Mark Ethan Trostler

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Preface

You have to test your code, so why not make the process as easy and painless as possible? Client-side JavaScript is especially difficult to test properly, as we have very little control over the environment within which our code runs. Multiple operating systems, multiple versions of operating systems, multiple browsers, and multiple versions of browsers, not to mention plug-ins, extensions, different languages, zoom levels, and who knows what else, all conspire to hinder the performance of our applications. These permutations slow down, break, crash, and eat our applications for lunch. It’s a jungle out there! Server-side JavaScript gives us significantly more control, as by and large, we control the execution environment. However, Rhino and Node.js applications do not have the full gamut of mature tools, the testing procedures, and the ecosystem that other languages do. Further, the asynchronous nature of Node.js complicates testing. It is interesting that a language so intertwined with asynchronous execution has essentially zero built-in support for that mode of execution.

Regardless, testing—especially JavaScript testing—is complicated. Our best shot at taming this complexity is to take full control of what we actually do control: the code. Code exists across a continuum, from someone else’s to yours on one axis and from legacy to nonlegacy on the other.

What is legacy code? I’m a fan of Michael Feathers’s definition in his excellent book, Working Effectively with Legacy Code (Prentice Hall): legacy code is code without tests. This code either will not survive or will never be touched by anyone. When the time comes to touch legacy code, it gets rewritten. Take a look at your current project; any code that does not have tests will likely be rewritten. Probably not by the author of the code, but by whoever is now tasked with dealing with it—either enhancing or bug-fixing it. Unless tests are written, this is dead code that will have to be rewritten. The code may be spectacular, but the only way it will survive is if it never causes bugs and if no one ever requests enhancements or new features for it. Even then, how happy are you to ship production code with no tests? Even if the code “worked” before, are you content to keep rolling the dice? Is your company, which owns the code, content to keep rolling the dice? Typically the piper must be paid, and this code will just get rewritten. It’s too bad the company had to pay to have this possibly spectacular code written twice, but such is the case with legacy code.

As you can see in the matrix shown in Figure P-1, it is very easy for any legacy code you’ve written to fall into someone else’s hands and be rewritten. That path is typically less painful than bringing someone else’s legacy code up to speed with tests. It is very easy to move from side to side in this matrix, as code changes hands constantly, moving into and out of your purview with great agility. Moving “down” is the hardest path for code to take; writing tests for existing code is a job no one wants to do, and most people will go to impressively great lengths to avoid it—typically resulting in a complete rewrite.

Current versus legacy code
Figure P-1. Current versus legacy code

Unfortunately, moving up in this matrix happens with some regularity. Code that starts out with tests can lapse into legacy code if it falls into the wrong hands. Vigilance is required to keep tests up to date as more enhancements and features are bolted on, but this is a much simpler process than writing tests for code without any (or with very few) tests.

The Goal of This Book

This book aims to keep your JavaScript code in the lower-right quadrant of Figure P-1 by taking a holistic approach to development. It’s not just about “writing tests” or “testing first,” but rather understanding how the choices you make while coding, good and bad, will affect you (your code and your employment) down the road.

Starting with the good habits of structuring your code syntactically and semantically for testability, writing the right tests at the right times, running them regularly, and monitoring their results will keep you in that lower-left sweet spot in the matrix.

This book attempts to bridge the gap between sane development practices and JavaScript. JavaScript is a weird little language. Starting with its senseless name, JavaScript was originally used by nonprogrammers to add some interactivity to web pages. Even if “real” programmers used the language in its early days, the dynamics between the language, the DOM, and the browser environment took some getting used to.

As more and more professional programmers started working with the language, best practices began to be understood and codified. Tools for working with JavaScript, debuggers, test harnesses, IDE support, and more began to emerge. The language itself was modified using lessons learned from several years out in the wild. JavaScript was starting to grow, and grow up. But a lot of weirdness remains, and more powerful tools are still around the corner.

With the advent of server-side JavaScript via Node.js, PhantomJS and other applications can now be written entirely in JavaScript. Not too long ago, that was not only impossible, but also thought to be insane. No one is laughing now!

This book attempts to pull together lessons learned from decades of study and experience with testing and quality assurance (QA), and to apply those lessons to JavaScript. Almost all the examples and code snippets in this book are written in JavaScript (some Perl has snuck in there!).

Who This Book Is For

This book’s primary target audience is people who encounter JavaScript professionally. Beginning, intermediate, or guru-level developers are all welcome, as this book has something for everyone.

JavaScript may not be the only language you use, but you write or test significantly sized chunks of it. Someone is paying you (hopefully good!) money to wrangle JavaScript to run either in the browser or, lucky you, on the server. If you are dealing with JavaScript every day with an application of any size, this book is right up your alley.

This book is also for you if you are on a QA or tools team that must test JavaScript—Chapter 3 through Chapter 7 are right in your wheelhouse. This books aims to make testing as easy as possible, and then automate all of it. Hopefully, this book will make people’s lives easier. That’s just how I roll.

If you write only a little JavaScript, this book still has lots of good information for you—especially the chapters on complexity (Chapter 2), event-based architectures (Chapter 3), and debugging (Chapter 7). The remaining chapters still have good information within, mind you! But they probably do not directly address your pain points. A lot of pain has led to my writing this book—I’ve learned from previous mistakes and hard work, and so should you! Learning good habits from the start will make you much more productive and happy.

Who This Book Is Not For

Sadly, this book is not for everyone. If you already are interested in learning JavaScript, you should learn the basics of the language elsewhere and then come back to this book. If you write clean, bug-free, fully documented and commented code with an automated build continuously running all of your unit and integration tests with full code coverage reports and automatically deploy into staging environments, well, there might not be a lot for you here. Maybe take a quick look at Chapter 7 in case you ever have to do any debugging, or check out Chapter 6 for kicks.

If you do not use JavaScript often, you can probably put this book down right now.

Who I Am

I am a relatively recent convert to JavaScript, coming to it after a very long and enjoyable time with Perl. Oh sure, I have used the language for 10+ years, but I never really took it very seriously. Making things “blink” and performing other seemingly useless UI tricks were all it appeared to be good for.

Douglas Crockford really opened my eyes to the depth and sophistication of the language with his excellent “Crockford on JavaScript” talks, available on YouTube on the YUI Library channel. If you need to be further convinced, or if some of your friends are giving you grief about JavaScript, watching these videos will effect a change in mindset.

I spent two and a half years as a frontend developer on Yahoo! Mail, which was rewritten from scratch during my tenure. I am confident that our team experienced every possible problem, issue, trouble, disaster, and triumph in utilizing client-side JavaScript during the rewrite. Most of the content of this book was drawn directly from that experience.

I am currently a Software Engineer in Test at Google, putting all of those hard-earned lessons to good use, and I hope you can too.

What You Will Learn from This Book

There are two things to learn from this book: the right way and the wrong way! There is, of course, a continuum between “right” and “wrong.” After you have finished reading this book, I would like you to be coding or looking at someone else’s code and know why you feel good or feel bad about it. If you can already tell what is wrong with code, good for you. When I look at code for the first time, either what I wrote or what someone else wrote, I get a good or bad feeling pretty quickly: either I understand the code almost immediately (a good feeling), or my eyes glaze over (a bad feeling). It is nice to be able to articulate to the author issues with his code; hopefully Chapter 2 will give you not only a more concrete sense of good versus bad, but also a shared vocabulary to communicate back to the author.

Writing unit tests for client-side JavaScript can be daunting. That means too many people don’t do it. This is not OK, for a variety of reasons spelled out in this book (and in many others!). But rather than wagging a finger, this book provides you with the tools and code to get you started. Getting started is the hardest part, and this book will get you on your way to writing unit tests for client-side JavaScript.

Clearly, just writing tests is not enough. You also have to run them. Ideally, you can run them easily at any time in your development environment, and also as part of an automated build process. What about code coverage for those tests? What about integration tests, performance tests, and load tests? What about code coverage for all of those tests? How about a continuous build environment that takes care of all of that for you? Finally, how can you structure your code such that all of this testing and automation is easier rather than harder? How about running tests on both client- and server-side JavaScript?

These topics and more (debugging, anyone?) are all covered within this book, so get ready for a wild ride through the world of JavaScript development. The overriding theme is writing and maintaining “testable” code.

Content

This book will tackle testable code in several steps. First we will investigate complexity. Then we will look at an architecture choice that attempts to limit complexity and coupling. With that as our foundation, we will move on to testing, both at the functional level and at the application level. We will gain a thorough understanding of code coverage and debugging, and then finish it all off with a healthy dose of automation. By the end of the book, you will have a fuller grasp of the “what” and the “how” of testable JavaScript.

Chapter 1, Testable JavaScript

The overrriding theme of this book is writing and maintaining “testable” code. But what is testable code? Why should we strive to write it? And how do we do so? We will begin by exploring all of these questions, and taking a look at some popular development methodologies and how they relate to testable code. Ultimately, whatever practice you may choose to follow, the key to writing testable code lies in keeping it small, clear, uncomplicated, and loosely coupled.”

Chapter 2, Complexity

Complexity is the root of many problems, not just testability. These problems include understandability and maintainability, both of which are key metrics for quality code. Some systems and applications are inherently complex; in fact, most applications are complex, but there are right ways and wrong ways to handle and express that complexity. Obviously, breaking down the more complex pieces into smaller, simpler chunks is a big first step. Reducing coupling and fan-out are other ways to manage complexity. We will investigate all of these methods, and more, in our quest for testable JavaScript.

Chapter 3, Event-Based Architectures

After our discussion on complexity, we will dive deeper into event-based architecture. This application-level architecture can greatly reduce complexity and coupling while providing easy ways to break down your application into smaller, more self-sufficient pieces. Regardless of whether your application is server-side, client-side, or (most likely) both, an event-based architecture solves many of the problems enumerated in Chapter 2. Even if it is not suitable as the overall architecture for every application, there certainly is a place within the overall structure to leverage event-based architecture concepts and practices.

Chapter 4, Unit Tests

There is a lot of controversy about unit testing. How important is it? Unit tests do not find every bug. Like all other tools, unit tests are a piece of the testability puzzle. Describing code as “testable” does not imply that tests are available for that code; rather, it implies that writing tests for that code would be straightforward. Unit tests are special in that they typically are the only kind of test developers write. They are also invasive, requiring that you isolate the code under test and execute it separately from the application. This makes unit testing potentially difficult, as being able to run code at the level of a single method, in isolation, can be very difficult. A large part of Testable JavaScript ensures that your code can be executed in isolation, thereby making unit tests much simpler to write. While unit tests do not find every bug (or even most bugs), the ones they do find are worth the effort involved in running them. It is also important that your test code adheres to the same high standards and principles as the code you are testing.

Chapter 5, Code Coverage

Code Coverage typically goes hand in hand with unit testing. Code coverage can be a good measure of unit-test efficacy; however, we will see that this is not always the case. Yet code coverage is not just for unit testing anymore! All kinds of testing can benefit from code coverage measurements, including integration, manual, and performance testing. We will investigate the good and the bad of code coverage metrics and how to generate, view, and make sense of them.

Chapter 6, Integration, Performance, and Load Testing

Of course, there is much more to testing than just unit testing. Integration, manual, performance, functional, and other types of testing all play an important role in finding and ferreting out bugs. Regardless of who does the testing—the developers, the QA team, or even your unwitting users—these other kinds of tests will get done whether you like it or not. The ability to easily test the application as a whole is also vitally important. Modularizing functionality allows the test code to be more clearly linked to the implemented functionality, which helps developers fix bugs faster. Using code coverage during these tests can quickly show executed code during black-box testing. Plenty of great JavaScript-based tools are available for developers to leverage for integration and performance testing, and we will take a closer look at some of those tools to give you an idea of what can be accomplished.

Chapter 7, Debugging

The code we write is not perfect, no matter how perfect it seems when we first write it. Our code will have bugs. Lots of bugs. Stuff you thought about and stuff you did not even conceive of will bedevil your code. Your tests, someone else’s tests, or a user using your application will find the bugs. Bugs found by tests are by far the easiest to fix, which is another great reason to maximize your tests. Bugs found by users running production code are far more difficult to track down. The upshot is that you will have to debug not only your code but also someone else’s. I’ll share some tips and tricks for debugging both Node.js and browser code. Get a good debugging environment set up, because you will be doing a lot of it.

Chapter 8, Automation

Finally, doing things manually over and over again is not only not sustainable but also not very fun. Writing software is one of the most manual processes in the world, but testing and maintaining software does not have to be. Running tests, generating code coverage reports, performing static analysis, minifying and compressing code, and deploying and rolling back code to and from production and other environments should all be part of an automated process. Automation ensures that whatever happens, success or failure, it will happen quickly and, more importantly, in a way that can be repeated. You will fail. Tests will fail, a production launch will fail, and things will go wrong that will have absolutely nothing to do with your code. That is life. It is critical that you can recover from those failures (along with the failures you have caused) as quickly and seamlessly as possible.

If You Like (or Don’t Like) This Book

If you like—or don’t like—this book, by all means, please let people know. Amazon reviews are one popular way to share your happiness (or lack of happiness), or you can leave reviews at the book’s website.

That website also provides a link to errata, giving you a way to let us know about typos, errors, and other problems with the book. These errata will be visible on the page immediately, and we’ll confirm them after checking them out. O’Reilly can also fix errata in future printings of the book and on Safari, making for a better reader experience pretty quickly.

Recap

Writing testable code will make your life, and the lives of all who follow you, much, much easier. From fewer bugs to more easily fixed ones, from easier testing to simpler debugging, testable JavaScript is your gateway to sanity.

This book attempts to show you the path toward that sanity. After reading the entire book you will have a very good understanding of what writing and maintaining testable JavaScript actually entails. But that is only the beginning. You must fit these practices and patterns into your daily life as a developer. You must resist the temptation to be “lazy” and not write tests, when you are instead just kicking the can down the road for either yourself or someone else to clean up your mess. Testable JavaScript is code that will last. If you are currently writing legacy code, do yourself and your employer a favor and start writing current code. I hope you will find that not only is that effort not difficult, but it can also be extremely rewarding, and maybe even fun!

How to Contact Us

We have tested and verified the information in this book to the best of our ability, but you may find that features have changed (or even that we have made a few mistakes!). Please let us know about any errors you find, as well as your suggestions for future editions, by writing to:

O’Reilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
800-998-9938 (in the U.S. or Canada)
707-829-0515 (international/local)
707-829-0104 (fax)

We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at http://oreil.ly/Testable-JavaScript.

To comment or ask technical questions about this book, send email to .

For more information about our books, courses, conferences, and news, see our website at http://www.oreilly.com.

Find us on Facebook: http://facebook.com/oreilly

Follow us on Twitter: http://twitter.com/oreillymedia

Watch us on YouTube: http://www.youtube.com/oreillymedia

Conventions Used in This Book

The following typographical conventions are used in this book:

Italic

Indicates new terms, URLs, email addresses, filenames, and file extensions.

Constant width

Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords.

Constant width bold

Shows commands or other text that should be typed literally by the user.

Constant width italic

Shows text that should be replaced with user-supplied values or by values determined by context.

Tip

This icon signifies a tip, suggestion, or general note.

Caution

This icon indicates a warning or caution.

Using Code Examples

This book is here to help you get your job done. In general, if this book includes code examples, you may use the code in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing a CD-ROM of examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission.

We appreciate, but do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “Testable JavaScript by Mark Ethan Trostler (O’Reilly). Copyright 2013 ZZO Associates, 978-1-449-32339-4.”

If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at .

Safari® Books Online

Note

Safari Books Online (www.safaribooksonline.com) is an on-demand digital library that delivers expert content in both book and video form from the world’s leading authors in technology and business.

Technology professionals, software developers, web designers, and business and creative professionals use Safari Books Online as their primary resource for research, problem solving, learning, and certification training.

Safari Books Online offers a range of plans and pricing for enterprise, government, and education, and individuals.

Members have access to thousands of books, training videos, and prepublication manuscripts in one fully searchable database from publishers like O’Reilly Media, Prentice Hall Professional, Addison-Wesley Professional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, John Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, Adobe Press, FT Press, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett, Course Technology, and hundreds more. For more information about Safari Books Online, please visit us online.

Thanks!

A Big Thank You to everyone who helped me cobble together this book, starting with my former employer, Yahoo!, which green-lighted the time I needed to spend writing during “business hours”; thanks Julia and Randy! Also a Big Shout-Out to the amazing frontend team working on Yahoo! Mail, especially those based in Rancho Bernardo—I’m looking at you, Brian, Jeff, Hung, Dan, Mily, Steve, and Scott. Thanks to my manager here at Google, Matt Evans, and the rest of our team for letting me continue to pursue my goal of developing sane software.

A special thank you to everyone who contributes to open source technology. It is amazing that there continue to be fewer reasons to use commercial software, which is quite ironic for someone in the commercial software business. It is clear that money is not the be-all and end-all motivator, and it warms the cockles of my heart that passion trumps money every time. Hey employers, the intersection of your employees’ passion and your product is how quality work actually gets done!

Thanks to Doug Crockford and his excellent series of talks that inspired me to take JavaScript seriously.

Big props to Shelley Powers and Davis Frank for reviewing the book and providing lots of great feedback that improved the book greatly. Of course, the buck stops with me, so if anything is not up to snuff it is all on me. JavaScript is changing rapidly and the language and tools are constantly evolving, but I hope the concepts presented in this book (if not all of the tools) will remain relevant for a long time. Poor Audrey Doyle had the unfortunate task of copyediting this manuscript, and she knocked it out of the park! I now have proof that at least one person read the book cover to cover—thanks Audrey!

Finally, much love to my family—Walter, Inslee, and especially Michelle, who has had to live with the gestation process of this book for too long. Now, on to the next one?

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required