Test-Driven JavaScript Development - Pearsoncmg

Transcription

Test-Driven JavaScriptDevelopment

Developer’s Library SeriesVisit developers-library.com for a complete list of available productsThe Developer’s Library Series from Addison-Wesley providespracticing programmers with unique, high-quality references andtutorials on the latest programming languages and technologies theyuse in their daily work. All books in the Developer’s Library are written byexpert technology practitioners who are exceptionally skilled at organizingand presenting information in a way that’s useful for other programmers.Developer’s Library books cover a wide range of topics, from opensource programming languages and databases, Linux programming,Microsoft, and Java, to Web development, social networking platforms,Mac/iPhone programming, and Android programming.

Test-Driven JavaScriptDevelopmentChristian JohansenUpper Saddle River, NJ Boston Indianapolis San FranciscoNew York Toronto Montreal London Munich Paris MadridCapetown Sydney Tokyo Singapore Mexico City

Many of the designations used by manufacturers and sellers to distinguish their products areclaimed as trademarks. Where those designations appear in this book, and the publisher wasaware of a trademark claim, the designations have been printed with initial capital letters orin all capitals.Acquisitions EditorTrina MacDonaldThe author and publisher have taken care in the preparation of this book, but make noexpressed or implied warranty of any kind and assume no responsibility for errors oromissions. No liability is assumed for incidental or consequential damages in connection withor arising out of the use of the information or programs contained herein.Managing EditorJohn FullerThe publisher offers excellent discounts on this book when ordered in quantity for bulkpurchases or special sales, which may include electronic versions and/or custom covers andcontent particular to your business, training goals, marketing focus, and branding interests.For more information, please contact:U.S. Corporate and Government Sales(800) 382-3419corpsales@pearsontechgroup.comFor sales outside the United States please contact:Development EditorSonglin QiuProject EditorMadhu Bhardwaj,Glyph InternationalProject CoordinatorElizabeth RyanCopy EditorMike ReadIndexerRobert SwansonProofreaderDavid DanielsVisit us on the Web: informit.com/awTechnical ReviewersAndrea GiammarchiJoshua GrossJacob SeidelinLibrary of Congress Cataloging-in-Publication DataCover DesignerGary AdairInternational Salesinternational@pearson.comJohansen, Christian, 1982Test-driven JavaScript development / Christian Johansen.p. cm.Includes bibliographical references and index.ISBN-13: 978-0-321-68391-5 (pbk. : alk. paper)ISBN-10: 0-321-68391-9 (pbk. : alk. paper)1. JavaScript (Computer program language) I. Title.QA76.73.J39J64 2011005.13’3–dc222010027298c 2011 Pearson Education, Inc.Copyright All rights reserved. Printed in the United States of America. This publication is protected bycopyright, and permission must be obtained from the publisher prior to any prohibitedreproduction, storage in a retrieval system, or transmission in any form or by any means,electronic, mechanical, photocopying, recording, or likewise. To obtain permission to usematerial from this work, please submit a written request to Pearson Education, Inc.,Permissions Department, One Lake Street, Upper Saddle River, New Jersey 07458,or you may fax your request to (201) 236-3290.ISBN-13: 978-0-321-68391-5ISBN-10:0-321-68391-9Text printed in the United States on recycled paper at RR Donnelley in Crawfordsville,Indiana.Second printing, May 2012CompositorGlyph International

To Frøydis and Kristin, my special ladies.

This page intentionally left blank

ContentsPrefacexixAcknowledgmentsxxvAbout the AuthorxxviiPart ITest-Driven Development11. Automated Testing 31.1 The Unit Test 41.1.1 Unit Testing Frameworks 51.1.2 strftime for JavaScript Dates1.2 Assertions 91.2.1 Red and Green 101.3 Test Functions, Cases, and Suites 111.3.1 Setup and Teardown 131.4 Integration Tests 141.5 Benefits of Unit Tests 161.5.1 Regression Testing 161.5.2 Refactoring 171.5.3 Cross-Browser Testing 171.5.4 Other Benefits 171.6 Pitfalls of Unit Testing 181.7 Summary 1852. The Test-Driven Development Process 212.1 Goal and Purpose of Test-Driven Development 212.1.1 Turning Development Upside-Down 222.1.2 Design in Test-Driven Development 22vii

viiiContents2.2The Process 232.2.1 Step 1: Write a Test 242.2.2 Step 2: Watch the Test Fail 252.2.3 Step 3: Make the Test Pass 262.2.3.1 You Ain’t Gonna Need It 262.2.3.2 Passing the Test for String.prototype.trim 272.2.3.3 The Simplest Solution that Could Possibly Work 272.2.4 Step 4: Refactor to Remove Duplication 282.2.5 Lather, Rinse, Repeat 292.3 Facilitating Test-Driven Development 292.4 Benefits of Test-Driven Development 302.4.1 Code that Works 302.4.2 Honoring the Single Responsibility Principle 302.4.3 Forcing Conscious Development 312.4.4 Productivity Boost 312.5 Summary 313. Tools of the Trade 333.1 xUnit Test Frameworks 333.1.1 Behavior-Driven Development 343.1.2 Continuous Integration 343.1.3 Asynchronous Tests 353.1.4 Features of xUnit Test Frameworks 353.1.4.1 The Test Runner 353.1.5 Assertions 363.1.6 Dependencies 373.2 In-Browser Test Frameworks 373.2.1 YUI Test 383.2.1.1 Setup 383.2.1.2 Running Tests 403.2.2 Other In-Browser Testing Frameworks 403.3 Headless Testing Frameworks 413.3.1 Crosscheck 423.3.2 Rhino and env.js 423.3.3 The Issue with Headless Test Runners 423.4 One Test Runner to Rule Them All 423.4.1 How JsTestDriver Works 433.4.2 JsTestDriver Disadvantages 443.4.3 Setup 443.4.3.1 Download the Jar File 443.4.3.2 Windows Users 453.4.3.3 Start the Server 453.4.3.4 Capturing Browsers 46

ixContents3.4.3.5 Running Tests 463.4.3.6 JsTestDriver and TDD 483.4.4 Using JsTestDriver From an IDE 493.4.4.1 Installing JsTestDriver in Eclipse 493.4.4.2 Running JsTestDriver in Eclipse 503.4.5 Improved Command Line Productivity 513.4.6 Assertions 513.5 Summary 524. Test to Learn 554.1 Exploring JavaScript with Unit Tests 554.1.1 Pitfalls of Programming by Observation 584.1.2 The Sweet Spot for Learning Tests 594.1.2.1 Capturing Wisdom Found in the Wild4.1.2.2 Exploring Weird Behavior 594.1.2.3 Exploring New Browsers 594.1.2.4 Exploring Frameworks 604.2 Performance Tests 604.2.1 Benchmarks and Relative Performance 604.2.2 Profiling and Locating Bottlenecks 684.3 Summary 69Part IIJavaScript for Programmers715. Functions 735.1 Defining Functions 735.1.1 Function Declaration 735.1.2 Function Expression 745.1.3 The Function Constructor 755.2 Calling Functions 775.2.1 The arguments Object 775.2.2 Formal Parameters and arguments5.3 Scope and Execution Context 805.3.1 Execution Contexts 815.3.2 The Variable Object 815.3.3 The Activation Object 825.3.4 The Global Object 825.3.5 The Scope Chain 835.3.6 Function Expressions Revisited 845.4 The this Keyword 875.4.1 Implicitly Setting this 885.4.2 Explicitly Setting this 895.4.3 Using Primitives As this 895.5 Summary 917959

x6. Applied Functions and Closures 936.1 Binding Functions 936.1.1 Losing this: A Lightbox Example 936.1.2 Fixing this via an Anonymous Function 956.1.3 Function.prototype.bind 956.1.4 Binding with Arguments 976.1.5 Currying 996.2 Immediately Called Anonymous Functions 1016.2.1 Ad Hoc Scopes 1016.2.1.1 Avoiding the Global Scope 1016.2.1.2 Simulating Block Scope 1026.2.2 Namespaces 1036.2.2.1 Implementing Namespaces 1046.2.2.2 Importing Namespaces 1066.3 Stateful Functions 1076.3.1 Generating Unique Ids 1076.3.2 Iterators 1096.4 Memoization 1126.5 Summary 1157. Objects and Prototypal Inheritance 1177.1 Objects and Properties 1177.1.1 Property Access 1187.1.2 The Prototype Chain 1197.1.3 Extending Objects through the Prototype Chain 1217.1.4 Enumerable Properties 1227.1.4.1 Object.prototype.hasOwnProperty 1247.1.5 Property Attributes 1267.1.5.1 ReadOnly 1267.1.5.2 DontDelete 1267.1.5.3 DontEnum 1267.2 Creating Objects with Constructors 1307.2.1 prototype and [[Prototype]] 1307.2.2 Creating Objects with new 1317.2.3 Constructor Prototypes 1327.2.3.1 Adding Properties to the Prototype 1327.2.4 The Problem with Constructors 1357.3 Pseudo-classical Inheritance 1367.3.1 The Inherit Function 1377.3.2 Accessing [[Prototype]] 1387.3.3 Implementing super 1397.3.3.1 The super Method 140Contents

xiContents7.3.3.2 Performance of the super Method 1437.3.3.3 A super Helper Function 1437.4 Encapsulation and Information Hiding 1457.4.1 Private Methods 1457.4.2 Private Members and Privileged Methods 1477.4.3 Functional Inheritance 1487.4.3.1 Extending Objects 1497.5 Object Composition and Mixins 1507.5.1 The Object.create Method 1517.5.2 The tddjs.extend Method 1537.5.3 Mixins 1577.6 Summary 1588. ECMAScript 5th Edition 1598.1 The Close Future of JavaScript 1598.2 Updates to the Object Model 1618.2.1 Property Attributes 1618.2.2 Prototypal Inheritance 1648.2.3 Getters and Setters 1668.2.4 Making Use of Property Attributes 1678.2.5 Reserved Keywords as Property Identifiers8.3 Strict Mode 1718.3.1 Enabling Strict Mode 1718.3.2 Strict Mode Changes 1728.3.2.1 No Implicit Globals 1728.3.2.2 Functions 1728.3.2.3 Objects, Properties, and Variables8.3.2.4 Additional Restrictions 1748.4 Various Additions and Improvements 1748.4.1 Native JSON 1758.4.2 Function.prototype.bind 1758.4.3 Array Extras 1758.5 Summary 1769. Unobtrusive JavaScript 1779.1 The Goal of Unobtrusive JavaScript 1779.2 The Rules of Unobtrusive JavaScript 1789.2.1 An Obtrusive Tabbed Panel 1799.2.2 Clean Tabbed Panel Markup 1819.2.3 TDD and Progressive Enhancement9.3 Do Not Make Assumptions 1839.3.1 Don’t Assume You Are Alone 1839.3.1.1 How to Avoid 183182170174

xiiContents9.3.2Don’t Assume Markup Is Correct 1839.3.2.1 How to Avoid 1849.3.3 Don’t Assume All Users Are Created Equal9.3.3.1 How to Avoid 1849.3.4 Don’t Assume Support 1849.4 When Do the Rules Apply? 1849.5 Unobtrusive Tabbed Panel Example 1859.5.1 Setting Up the Test 1869.5.2 The tabController Object 1879.5.3 The activateTab Method 1909.5.4 Using the Tab Controller 1929.6 Summary 19618410. Feature Detection 19710.1 Browser Sniffing 19810.1.1 User Agent Sniffing 19810.1.2 Object Detection 19910.1.3 The State of Browser Sniffing 20010.2 Using Object Detection for Good 20010.2.1 Testing for Existence 20110.2.2 Type Checking 20110.2.3 Native and Host Objects 20210.2.4 Sample Use Testing 20410.2.5 When to Test 20610.3 Feature Testing DOM Events 20710.4 Feature Testing CSS Properties 20810.5 Cross-Browser Event Handlers 21010.6 Using Feature Detection 21310.6.1 Moving Forward 21310.6.2 Undetectable Features 21410.7 Summary 214Part IIIReal-World Test-Driven Development in JavaScript11. The Observer Pattern 21911.1 The Observer in JavaScript 22011.1.1 The Observable Library 22011.1.2 Setting up the Environment 22111.2 Adding Observers 22211.2.1 The First Test 22211.2.1.1 Running the Test and Watching It Fail11.2.1.2 Making the Test Pass 223217222

xiiiContents11.2.2 Refactoring 22511.3 Checking for Observers 22611.3.1 The Test 22611.3.1.1 Making the Test Pass 22711.3.1.2 Solving Browser Incompatibilities 22811.3.2 Refactoring 22911.4 Notifying Observers 23011.4.1 Ensuring That Observers Are Called 23011.4.2 Passing Arguments 23111.5 Error Handling 23211.5.1 Adding Bogus Observers 23211.5.2 Misbehaving Observers 23311.5.3 Documenting Call Order 23411.6 Observing Arbitrary Objects 23511.6.1 Making the Constructor Obsolete 23611.6.2 Replacing the Constructor with an Object 23911.6.3 Renaming Methods 24011.7 Observing Arbitrary Events 24111.7.1 Supporting Events in observe 24111.7.2 Supporting Events in notify 24311.8 Summary 24612. Abstracting Browser Differences: Ajax 24712.1 Test Driving a Request API 24712.1.1 Discovering Browser Inconsistencies 24812.1.2 Development Strategy 24812.1.3 The Goal 24812.2 Implementing the Request Interface 24912.2.1 Project Layout 24912.2.2 Choosing the Interface Style 25012.3 Creating an XMLHttpRequest Object 25012.3.1 The First Test 25112.3.2 XMLHttpRequest Background 25112.3.3 Implementing tddjs.ajax.create 25312.3.4 Stronger Feature Detection 25412.4 Making Get Requests 25512.4.1 Requiring a URL 25512.4.2 Stubbing the XMLHttpRequest Object 25712.4.2.1 Manual Stubbing 25712.4.2.2 Automating Stubbing 25812.4.2.3 Improved Stubbing 26112.4.2.4 Feature Detection and ajax.create263

xivContents12.512.612.712.812.4.3 Handling State Changes 26312.4.4 Handling the State Changes 26512.4.4.1 Testing for Success 265Using the Ajax API 26912.5.1 The Integration Test 26912.5.2 Test Results 27012.5.3 Subtle Trouble Ahead 27112.5.4 Local Requests 27312.5.5 Testing Statuses 27412.5.5.1 Further Status Code Tests 276Making POST Requests 27712.6.1 Making Room for Posts 27712.6.1.1 Extracting ajax.request 27812.6.1.2 Making the Method Configurable 27812.6.1.3 Updating ajax.get 28012.6.1.4 Introducing ajax.post 28112.6.2 Sending Data 28212.6.2.1 Encoding Data in ajax.request 28312.6.2.2 Sending Encoded Data 28412.6.2.3 Sending Data with GET Requests 28512.6.3 Setting Request Headers 287Reviewing the Request API 288Summary 29213. Streaming Data with Ajax and Comet 29313.1 Polling for Data 29413.1.1 Project Layout 29413.1.2 The Poller: tddjs.ajax.poller 29513.1.2.1 Defining the Object 29613.1.2.2 Start Polling 29613.1.2.3 Deciding the Stubbing Strategy 29813.1.2.4 The First Request 29913.1.2.5 The complete Callback 30013.1.3 Testing Timers 30313.1.3.1 Scheduling New Requests 30413.1.3.2 Configurable Intervals 30613.1.4 Configurable Headers and Callbacks 30813.1.5 The One-Liner 31113.2 Comet 31413.2.1 Forever Frames 31413.2.2 Streaming XMLHttpRequest 31513.2.3 HTML5 31513.3 Long Polling XMLHttpRequest 315

xvContents13.3.1 Implementing Long Polling Support 31613.3.1.1 Stubbing Date 31613.3.1.2 Testing with Stubbed Dates 31713.3.2 Avoiding Cache Issues 31913.3.3 Feature Tests 32013.4 The Comet Client 32113.4.1 Messaging Format 32113.4.2 Introducing ajax.CometClient 32313.4.3 Dispatchin

2.2.2 Step 2: Watch the Test Fail 25 2.2.3 Step 3: Make the Test Pass 26 2.2.3.1 You Ain’t Gonna Need It 26 2.2.3.2 Passing the Test for String.prototype.trim 27 2.2.3.3 The Simplest Solution that Could Possibly Work 27 2.2.4 Step 4: Refactor to Remove Duplication 28 2.2.5 Lather, Rinse, Repeat 29 2.3 Facilitating Test-Driven Development 29