1. Trang chủ
  2. » Công Nghệ Thông Tin

Phát triển Javascript - part 9 pdf

10 262 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 2,37 MB

Nội dung

ptg 3.5 Summary 53 Although we visited the topics of test coverage reports and continuous integra- tion in this chapter, no setup or examples were given for such tools. On the book’s website 1 you will find a guide to running the Coverage plugin for JsTestDriver as well as a guide on how to run JsTestDriver tests in the open source continuous integration server Hudson. In the next chapter we will have a look at some other ways to utilize unit tests before we move on to Part II, JavaScript for Programmers. 1. http://tddjs.com From the Library of WoweBook.Com Download from www.eBookTM.com ptg This page intentionally left blank From the Library of WoweBook.Com Download from www.eBookTM.com ptg 4 Test to Learn I n the previous three chapters we have seen how automated tests can help improve quality of code, guide design and drive development. In this chapter we will use automated tests to learn. As small executable code examples, unit tests make a perfect learning resource. Isolating a specific aspect of an interface in a unit test is a great way to learn more about how it behaves. Other types of automated tests can help our understanding of both the language and specific problems. Benchmarks are a valuable tool to measure relative performance, and can guide decisions about how to solve a specific problem. 4.1 Exploring JavaScript with Unit Tests Quickly executing JavaScript, as in executing a few lines of script to explore the behavior of some object, is fairly simple. Most modern browsers ship with a console that servesthispurpose just fine.Additionally,there are severaloptions for JavaScript command line interfaces when the browser environment is not of particular interest. Although this sort of one-off coding session can help our understanding of an inter- face, it suffers from the same problems that manual application testing does. There is no way to repeat a given experiment, there is no record of previously run experi- ments, and there is no simple way of repeating an experiment in multiple browsers. In Chapter 1, Automated Testing, we introduced unit tests as a means to solve the problems brought on by manual testing. Surely, unit tests can help us solve 55 From the Library of WoweBook.Com Download from www.eBookTM.com ptg 56 Test to Learn the same problems when we want to simply explore an interface to learn. For this purpose we can write learning tests, i.e., unit tests written with the goal of learning, not with the goal of testing the exercised interface per se. As an example, let us take the built-in Array.prototype.splice method for a spin. This method accepts two or more arguments: an index to start with, a number of items to remove, and optional elements to insert into the array. We are curious as to whether or not this method alters the original array. We could look up the answer, or we could simply ask JavaScript to tell us, as the learning test in Listing 4.1 shows. To run the test, set up a JsTestDriver project as explained in Chapter 3, Tools of the Trade, with a test directory and save the test in a file in that directory. Add a configuration file that loads test/*.js. Listing 4.1 Expecting Array.prototype.splice to modify the array TestCase("ArrayTest", { "test array splice should not modify array": function () { var arr = [1, 2, 3, 4, 5]; var result = arr.splice(2, 3); assertEquals([1, 2, 3, 4, 5], arr); } }); Because we don’t really know what the answer is, we roll with the assumption that the splice method is not destructive. Note how this contrasts with traditional unit testing—when testing production code we should always write assertions on firm expectation about the result. However, we are now learning by observing what the implementation can tell us, and so whatever answer we are assuming before run- ning the test is not of grave importance. Running the test proves us wrong anyway: “expected [1, 2, 3, 4, 5] but was [1, 2].” So we have learned something new. To record our findings, Listing 4.2 updates the test to state what we now know to be true. Listing 4.2 Array.prototype.splice modifies the receiving array TestCase("ArrayTest", { "test array splice should modify array": function () { var arr = [1, 2, 3, 4, 5]; var result = arr.splice(2, 3); assertEquals([1, 2], arr); } }); From the Library of WoweBook.Com Download from www.eBookTM.com ptg 4.1 Exploring JavaScript with Unit Tests 57 Note how both the wording and the assertion changed. Because we have dis- covered that the method in question is in fact destructive, we now wonder: Does it also return the result? Listing 4.3 investigates. Listing 4.3 Expecting Array.prototype.splice to return the spliced array "test array splice should return modified array": function () { var arr = [1, 2, 3, 4, 5]; var result = arr.splice(2, 3); assertEquals(arr, result); } Running this test proves us wrong yet again: “expected [1, 2] but was [3, 4, 5].” Apparently, the splice method returns the removed items. Time to update the wording of our test, as seen in Listing 4.4. Listing 4.4 Expecting Array.prototype.splice to return removed items "test array splice should return removed items": function () { var arr = [1, 2, 3, 4, 5]; var result = arr.splice(2, 3); assertEquals([3, 4, 5], result); } Rather than playing with an array and the splice method in a browser console, we put the test in a file. With a minimum of added overhead, we now have a repeatable experiment that documents what we just learned, perfect for later review. Using the JsTestDriver test runner, we could even send this test out to an army of browsers to verify that the two tests run consistently across browsers. Testing built-in functionality like thismightseem to contradict our usualattitude toward unit testing: never to test code that we didn’t write ourselves, and to mock or stub external dependencies while testing. These are still valuable pieces of advice, but they do not apply to learning tests. Learning tests aren’t a part of production code; they live in a separate repository, a personal repository, and they help us document our knowledge and our learning experience. Still, in contrast to traditional applications of unit testing, learning tests cannot be successfully collaborated on. Everyone should keep their own suite of learning tests. The reason for this advice is simply that it is not the tests themselves that From the Library of WoweBook.Com Download from www.eBookTM.com ptg 58 Test to Learn provides the highest value. Writing the test, including all the surrounding thought process is what we primarily learn from. Keeping the tests allows us to revisit a given exercise at a later time, or run it in a newly discovered browser to see if our experience still serves us well. Browsing through a suite of learning tests written by someone else might provide a few nuggets of information, but is unlikely to embed knowledge inside our brains the same way writing learning tests does. 4.1.1 Pitfalls of Programming by Observation When we talk about JavaScript, we are really talking about several dialects; Mozilla’s JavaScript ™ , Microsoft’s JScript and Webkit’s JavaScriptCore to name a few. These all stem from the original JavaScript language invented by Netscape and have their common ground in ECMA-262, or ECMAScript, a standardization of that very language. Because we are really targeting several dialects when writing client side scripts, there is no canonical source of information of the kind we retrieved in the two learning tests from the previous section. In other words, there is no authoritative interpreter to ask for information about JavaScript—they all have their bugs and quirks, they all have their proprietary extensions, and their mar- ket share is more evenly distributed than ever (even though Microsoft still domi- nates with Internet Explorer versions 6-8 combined)—meaning there are no single browser that can be considered “best,” our scripts will have to run in all of them anyway. Because there are so many versions of the language in active use, we cannot blindly trust the results of some statements run in a single browser alone. And when results differ between browsers, which one do we trust? When in doubt, we need to consult the source—the ECMA-262 specification. When browsers behave differently on language features, we need to consult the spec they are trying to im- plement to understand properly what the correct answer is. Only when we know how something is intended to work can we get to work fixing it, either by over- writing the built-in implementation for misbehaving browsers, or by providing an abstraction. By writing the two learning tests in the previous section, we learned a few things about the Array.prototype.splice method by observing the results of running our test in a browser. Drawing conclusions based on a small sample of observations can prove to be a dangerous approach, especially when dealing with browser differences. White-space matching in regular expressions using \s is an example of how observations may lead us astray.Until recently, no implementation correctly matched all white-space characters defined by ECMA-262. Tests for certain white-space From the Library of WoweBook.Com Download from www.eBookTM.com ptg 4.1 Exploring JavaScript with Unit Tests 59 characters would fail in all browsers, possibly leading us to conclude that the \s character class isn’t supposed to match the specific character being tested. 4.1.2 The Sweet Spot for Learning Tests Even though we should exercise healthy skepticism toward “programming by ob- servation,” the learning test can be a very helpful tool aiding our understanding of the JavaScript language and the environments in which it runs. In this section we will run through a few cases where learning tests can help more efficiently accelerate and maintain learning and knowledge of the language. 4.1.2.1 Capturing Wisdom Found in the Wild The learning test is the perfect tool to capture some wisdom picked up while reading someone else’s code, an article or a book. This might not be something entirely new; it could be as simple as a clever trick. Writing it down as a test case provides several benefits. For one, it puts us through actually writing the code down, helping us remember whatever it is we are picking up. Second, having the code isolated in the test allows us to more easily play with it and run it in several browsers. It also means we can copy and modify it a few times over, bending it to learn more from it. As always, keeping the test with the rest of the learning tests provides documentation that can be reviewed at a later point. 4.1.2.2 Exploring Weird Behavior Every now and then we stumble upon weird bugs or other unexpected behavior when developing production code. Failing to draw the necessary lessons from such experiences will set us at risk for repeating the mistake. Isolating the nature of the bug in a learning test can help us control iffy situations and become aware of them when they arise, helping us spot them as they’re forming rather than when they’re causing bugs in the code. 4.1.2.3 Exploring New Browsers Keeping a suite of learning tests can kick start exploration of a newly released browser. Does the browser change any behaviors we have come to rely on? Does it fix any bugs we are currently working our way around? I’m not saying we should manually maintain comprehensive test suites for ECMA-262, the DOM, and other interfaces, but running a suite of learning tests in a newly released browser means we can check how our accumulated experience holds up, and it might immediately teach us something new. From the Library of WoweBook.Com Download from www.eBookTM.com ptg 60 Test to Learn 4.1.2.4 Exploring Frameworks Third party interfaces such as “Ajax libraries” make excellent targets for a few learning tests. The learning tests provide neutral ground to play with a library, and we get to try out the interfaces in a more free form than we probably would if we simply dropped the framework right into the application. In fact, doing a little bit of experimenting with a given framework can even influence the decision on whether or not to use it in an application. Many libraries have lots of initial appeal, but fail to live up to their expectations in practice. Exercising them in a sandboxed environment allows us to get a better feel of using them, and we are free to push them through hoops we know they will need to handle when introduced to a real production environment. Again, performing this kind of experiment in structured files rather than a console environment means we can refer to them at any point, perhaps to compare a set of libraries that was tested. 4.2 Performance Tests Another type of automated test thatcan teach us a whole lot are benchmarks that test relative performance. Most problems can be solved in many ways, and sometimes it is not obvious which solution is best. For example, as we will see in Chapter 7, Objects and Prototypal Inheritance, there are different ways of creating JavaScript objects, mainly the pseudo-classical way, using JavaScript’s constructors, and the functional approach, using closures. How do we choose which strategy to employ? Personal preference usually plays a part insuch choices, as does testability, flexibility, and performance. Depending on the use case, the performance aspect can prove to be very important. 4.2.1 Benchmarks and Relative Performance Whenever we have two or more ways to solve a given problem, a benchmark can indicate how one solution performs relative to alternatives; hence “relative perfor- mance.” A benchmark is a very simple concept: • Create a new Date(). • Exercise the code alternative to measure. • Create a new Date(); subtract the start date to find total time. • Repeat for all alternatives. • Compare results. Exercising the code alternative to measure usually needs to be done many times in a loop to improve accuracy of the measurement. Additionally, Windows XP and From the Library of WoweBook.Com Download from www.eBookTM.com ptg 4.2 Performance Tests 61 Windows Vista complicates the matter by providing browsers with timers that only update every 15ms. This means that fast-running tests can be hugely inaccurate, the best approach is to run tests for at least 500ms or so. Listing 4.5 shows a function that we will use to runsomebenchmarks to measure relative performance. Save it in lib/benchmark.js. Listing 4.5 A benchmark runner var ol; function runBenchmark(name, test) { if (!ol) { ol = document.createElement("ol"); document.body.appendChild(ol); } setTimeout(function () { var start = new Date().getTime(); test(); var total = new Date().getTime() - start; var li = document.createElement("li"); li.innerHTML = name + ": " + total + "ms"; ol.appendChild(li); }, 15); } Listing 4.6 uses this function to measure relative performance of different loop- ing styles. Save the file in benchmarks/loops.js. Listing 4.6 Benchmarking loops var loopLength = 500000; // Populate an array to loop var array = []; for (var i = 0; i < loopLength; i++) { array[i] = "item" + i; } function forLoop() { for (var i = 0, item; i < array.length; i++) { item = array[i]; } } From the Library of WoweBook.Com Download from www.eBookTM.com ptg 62 Test to Learn function forLoopCachedLength() { for (var i = 0, l = array.length, item; i < l; i++) { item = array[i]; } } function forLoopDirectAccess() { for (var i = 0, item; (item = array[i]); i++) { } } function whileLoop() { var i = 0, item; while (i < array.length) { item = array[i]; i++; } } function whileLoopCachedLength() { var i = 0, l = array.length, item; while (i < l) { item = array[i]; i++; } } function reversedWhileLoop() { var l = array.length, item; while (l ) { item = array[l]; } } function doubleReversedWhileLoop() { var l = array.length, i = l, item; while (i ) { item = array[l - i - 1]; } } From the Library of WoweBook.Com Download from www.eBookTM.com . severaloptions for JavaScript command line interfaces when the browser environment is not of particular interest. Although this sort of one-off coding session can help our understanding of an inter- face,. about JavaScript, we are really talking about several dialects; Mozilla’s JavaScript ™ , Microsoft’s JScript and Webkit’s JavaScriptCore to name a few. These all stem from the original JavaScript. characters defined by ECMA-262. Tests for certain white-space From the Library of WoweBook.Com Download from www.eBookTM.com ptg 4.1 Exploring JavaScript with Unit Tests 59 characters would fail

Ngày đăng: 04/07/2014, 22:20