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

Test Driven JavaScript Development- P23 ppt

20 192 0
Tài liệu đã được kiểm tra trùng lặp

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

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 192,08 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Listing 15.85 Expecting the message form to clear message "test should clear form after publish": function { var el = this.element.getElementsByTagName"input"[0]; el.value = "NP: A visi

Trang 1

Listing 15.85 Expecting the message form to clear message

"test should clear form after publish": function () { var el = this.element.getElementsByTagName("input")[0];

el.value = "NP: A vision of misery";

this.controller.handleSubmit(this.event);

assertEquals("", el.value);

}

Ideally, we would not clear the form until we know for sure the message was sent Unfortunately, the cometClient does not support adding a success callback

at this point, so the best we can do is clearing it immediately after having sent it and hope for the best The proper fix would include adding a third options argument

to cometClient and wait for success Listing 15.86 shows the message form controller’s updated handleSubmit

Listing 15.86 Clearing the message after publishing it

function handleSubmit(event) { /* */

input.value = "";

}

It would also be nice if the message form gave focus to the input field immedi-ately upon initializing it I will leave doing so as an exercise

15.6.2 Notes on Deployment

Copy over the message form and message list controllers to chapp’s public di-rectory and reload your browser The application should now be slightly smoother

to use

Simply copying files to deploy them is cumbersome and error prone Addi-tionally, serving the application with 15 individual script files is not optimal for performance If you installed Ruby and RubyGems to use the jstestdriver

and jsautotest tools in Chapter 3, Tools of the Trade, then you have a JavaScript

and CSS concatenator and minifier at your fingertips Listing 15.87 shows the three required commands to install Juicer, which will conveniently package your scripts for deployment

Trang 2

434 TDD and DOM Manipulation: The Chat Client

Listing 15.87 Installing Juicer and YUI Compressor

$ gem install juicer

$ juicer install yui_compressor

Run from the root of the Node.js application, the command in Listing 15.88 will produce a single file, chat.min.js, containing the entire client-side application

Listing 15.88 Using Juicer to compress files

juicer merge -s -f -o public/js/chat.min.js \

public/js/function.js \ public/js/object.js \ public/js/tdd.js \ public/js/observable.js \ public/js/form_controller.js \ public/js/user_form_controller.js \ public/js/json2.js \

public/js/url_params.js \ public/js/ajax.js \

public/js/request.js \ public/js/poller.js \ public/js/comet_client.js \ public/js/message_list_controller.js \ public/js/message_form_controller.js \ public/js/chat_client.js

The final result is a 14kB JavaScript file containing a fully operational chat room

Served with gzip compression, the total download should be about 5kB

Juicer is also able to find dependencies declared inside script files, meaning that

we can jot down each file’s dependencies inside comments in them and then simply

run “juicer merge chat.js” to produce the complete file, including the dependencies

More information on Juicer is available from the book’s website.4

15.7 Summary

In this chapter we have been able to pull together a lot of the code developed

throughout this book to create a fully functional, entirely JavaScript based

browser-based chat application And we did it all using test-driven development, right from

the very start

4 http://tddjs.com

From the Library of WoweBook.Com

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

Trang 3

The key aspect of this chapter has been unit testing DOM manipulation, and structuring the outermost application layer in a sensible way As we’ve discussed numerous times already, well factored software easily lends itself to unit testing, and the GUI—the DOM—is no exception to this rule

By employing the Model View Presenter/Passive View pattern, we were able

to identify reusable components in the view and implement the chat client in a modular way, resulting in very loosely coupled modules that were easy to test in isolation Developing these components using TDD was straightforward because each distinct unit had a well-defined responsibility Dividing a hard problem into several smaller problems is a lot more manageable than trying to solve it all in one go

An interesting aspect about a pattern such as Model View Presenter is that there are numerous ways to apply it to the problem domain of client-side JavaScript For instance, in many cases a portion of the DOM will represent the model because JavaScript widgets frequently manipulate the data already found on the page

The chat client was the final test-driven example, and we have reached the end

of Part III, Real-World Test-Driven Development in JavaScript In the final part of

the book we’ll draw some lessons from the past five chapters as we dive deeper into stubbing and mocking, and finally identify some guidelines for writing good unit tests

Trang 4

This page intentionally left blank

From the Library of WoweBook.Com

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

Trang 5

Part IV Testing Patterns

Trang 6

This page intentionally left blank

From the Library of WoweBook.Com

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

Trang 7

16

Mocking and Stubbing

While using test-driven development to develop five sample projects, we’ve become intimately familiar with the stubFn function We have used it as a tool to both inspect interaction between objects, as well as isolating interfaces under test

But what exactly is a stub? We are about to find out as we dive a little deeper into

the topic of using test doubles, objects that look like the real thing but really are

bleak impersonations used to simplify tests

In this chapter we will look at the general theory of using test doubles, and get to know a few common types of test doubles a little better Because we have

already used stubs extensively in tests throughout Part III, Real-World Test-Driven Development in JavaScript, we will relate the discussion to previous examples We

will also look at a more capable stubbing and mocking library and see how such a thing can be used in place of stubFn and other homegrown helpers to simplify some of the tests we have written so far

16.1 An Overview of Test Doubles

A test double is an object that supports the same API, or at least the parts of it relevant to a given test, as the real thing, but does not necessarily behave the same way Test doubles are used to both isolate interfaces and make tests more convenient;

making tests faster, avoiding calls to inconvenient methods, or spying on method calls in place of assertions on direct or indirect output

Trang 8

The terminology used in this chapter is mostly adapted from Gerard Meszaros book “xUnit Test Patterns,” [7] slightly adjusted to the world of JavaScript In

addition to the names and definitions of different types of test doubles, I will use

“system under test” to describe the code being tested

16.1.1 Stunt Doubles

Gerard Meszaros compares test doubles to Hollywood’s stunt doubles Some movie

scenes require dangerous stunts, physically demanding feats or other behavior that

the leading actor is either not willing or able to perform In such cases, a stunt

double is hired to do the job The stunt double need not be an accomplished actor,

he simply needs to be able to catch on fire or fall off a cliff without being mortally

wounded; and he needs to look somewhat like the leading actor, at least from a

distance

Test doubles are just like stunt doubles They take on the job when it’s incon-venient to use the leading star (production code); all we require from them is that

the audience (system under test) cannot tell it apart from the real deal

16.1.2 Fake Object

The stubs we’ve been using aggressively throughout the example projects in Part III,

Real-World Test-Driven Development in JavaScript, are one form of test doubles.

They appear to behave like real objects, but their actions are pre-programmed to

force a certain path through the system under test Additionally, they record data

about their interaction with other objects, available in the test’s verification stage

Another kind of test double is the fake object A fake object provides the same

functionality as the object it replaces and can be seen as an alternative

implementa-tion, only its implementation is considerably simpler For example, when working

with Node.js the file system can easily become inconvenient from a testing

perspec-tive Constantly accessing it can make tests slower, and keeping a lot of test data

on disk requires cleanup We can alleviate these problems by implementing an

in-memory file system that supports the same API as Node’s fs module and use this

in tests

Fakes differ from stubs in that stubs are usually created and injected into the system from individual tests on a per-need basis Fakes are more comprehensive

replacements, and are usually injected into the system as a whole before running

any tests Tests are usually completely unaware of the fakes because they behave

just like the objects they mirror, only significantly simplified In the Node.js file

system example we can imagine a complete implementation of the fs module as

From the Library of WoweBook.Com

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

Trang 9

an in-memory file system The test setup can then make sure to place the fake implementation ahead of the built-in one on the load path Neither individual tests nor production code will be aware that require("fs") actually loads a simplified in-memory file system

16.1.3 Dummy Object

A dummy object, as its name suggests, is usually just an empty object or function

When testing functions that expect several parameters, we are often only concerned with one of them at a time If the function we’re testing throws errors for missing

or wrongly typed arguments, we can pass it a dummy to “shut it up” while we focus

on behavior not related to the argument in question

As an example, consider the test in Listing 16.1 from Chapter 15, TDD and DOM Manipulation: The Chat Client The test verifies that the message list controller sets

the element’s scrollTop equal to the value of its scrollHeight However, the method also appends a new DOM element to the view element, and throws an exception if it does not have an appendChild method For the purpose of this test we use a dummy to pass the test on appendChild to get to the behavior we want to test

Listing 16.1 Using a dummy function

"test should scroll element down": function () { var element = {

appendChild: stubFn(), scrollHeight: 1900 };

this.controller.setView(element);

this.controller.addMessage({ user:"me",message:"Hey" });

assertEquals(1900, element.scrollTop);

}

16.2 Test Verification

Unit tests have four stages; setup, often divided between a shared setUp method and test specific configuration of objects; exercise, in which we call the function(s)

to test; verification, in which we assert that the result of the exercise stage coincides with our expectations; and finally tear down, which never happens inside a test, but

rather in a dedicated and shared tearDown method

Trang 10

Before we get into the nitty-gritty of stubs, mocks, and the difference between them, we will explore our options at the verification stage As we will see shortly,

veri-fication strategy is a central issue when making the choice between stubs and mocks

16.2.1 State Verification

Many of the tests in Part III, Real-World Test-Driven Development in JavaScript,

determine success by asserting that certain objects have a specific state after some

function was called As an example, consider Listing 16.2 from Chapter 15, TDD

and DOM Manipulation: The Chat Client, which expects the user form controller

to set the currentUser property of the model object It passes a dummy model

object to the controller, and then inspects the object’s currentUser object to

verify its behavior

Listing 16.2 Inspecting an object’s state to verify test

"test should set model.currentUser": function () {

var model = {};

var event = { preventDefault: stubFn() };

var input = this.element.getElementsByTagName("input")[0];

input.value = "cjno";

this.controller.setModel(model);

this.controller.setView(this.element);

this.controller.handleSubmit(event);

assertEquals("cjno", model.currentUser);

}

The fact that the last line inspects a property of an object passed to the system

under test to verify its success is called state verification State verification leads to

intuitive tests that clearly describe the outcome of using some part of the system In

this case, if the input field contains a username when the controller handles a submit

event, we expect it to transfer this username to the model object’s currentUser

property The test does not say anything about how this should happen, thus it is

completely detached from the implementation of handleSubmit

16.2.2 Behavior Verification

In many cases, testing the direct output of a test is not as simple as in Listing 16.2

For instance, keeping with the chat client example, the message form controller is

in charge of publishing messages from the client to the server through the model

object Because there is no server in the tests, we cannot simply ask it for the message

From the Library of WoweBook.Com

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

Trang 11

we expected it to receive To test this, we used a stub, as seen in Listing 16.3 Rather than inspecting some object’s state to verify its results, this test stubs the model’s publishmethod and then proceeds by asserting that it was called

Listing 16.3 Inspecting a function’s behavior to verify test

"test should publish message": function () { var controller = Object.create(messageController);

var model = { notify: stubFn() };

controller.setModel(model);

controller.handleSubmit();

assert(model.notify.called);

assertEquals("message", model.notify.args[0]);

assertObject(model.notify.args[1]);

}

This test contrasts with the previous one that used state verification It does

not check whether the message was stored somewhere, instead it uses behavior verification; it verifies that the model’s publish method was called with the correct

arguments Having already tested the Comet client to be used in production, we know that the message will be handled correctly if publish is called this way

16.2.3 Implications of Verification Strategy

The chosen verification strategy directly influences how a test reads, which is obvious from looking at the two tests above Less clear is the fact that the verification strategy also influences production code, as well as its relationship to the tests

Behavior verification taps into the system’s implementation by expecting certain function calls to take place On the other hand, state verification is a mere obser-vation on the (direct or indirect) input/output relationship This means that using behavior verification extensively couples the test code tighter to the system, which

in turn limits our ability to change its implementation, e.g., through refactoring, without also having to change the tests

16.3 Stubs

Stubs are test doubles with pre-programmed behavior They may return a specific value, regardless of received arguments, or throw an exception Because stubs are used in place of real objects and functions, they are also used as a measure to avoid bumping into inconvenient interfaces

Ngày đăng: 03/07/2014, 05:20

TỪ KHÓA LIÊN QUAN