Angularjs Testdriven DevelopmentTim Chaplin
download
https://ebookbell.com/product/angularjs-testdriven-development-
tim-chaplin-4985212
Explore and download more ebooks at ebookbell.com
2.
Here are somerecommended products that we believe you will be
interested in. You can click the link to download.
Angularjs Testdriven Development 1st Ed Bhm Robintarasiewicz
https://ebookbell.com/product/angularjs-testdriven-development-1st-ed-
bhm-robintarasiewicz-11793068
Angular Testdriven Development 2nd Edition Md Ziaul Haq
https://ebookbell.com/product/angular-testdriven-development-2nd-
edition-md-ziaul-haq-11861040
Mastering Angular Testdriven Development Ezchiel Amen Agbla
https://ebookbell.com/product/mastering-angular-testdriven-
development-ezchiel-amen-agbla-200671534
Angularjs Services Design Build And Test Services To Create A
Foundation For Your Angularjs Applications Jim Lavin
https://ebookbell.com/product/angularjs-services-design-build-and-
test-services-to-create-a-foundation-for-your-angularjs-applications-
jim-lavin-5476458
3.
Jets From YoungStars Ii Clues From High Angular Resolution
Observations 1st Edition Francesca Bacciotti
https://ebookbell.com/product/jets-from-young-stars-ii-clues-from-
high-angular-resolution-observations-1st-edition-francesca-
bacciotti-985726
Angularjs Web Application Development Blueprints Vinci Rufus
https://ebookbell.com/product/angularjs-web-application-development-
blueprints-vinci-rufus-55893708
Angularjs Maintaining Web Applications Rodrigo Branas Chandermani
https://ebookbell.com/product/angularjs-maintaining-web-applications-
rodrigo-branas-chandermani-23272318
Angularjs Succinctly Frederik Dietz
https://ebookbell.com/product/angularjs-succinctly-frederik-
dietz-36134704
Angularjs Brad Green Shyam Seshadri
https://ebookbell.com/product/angularjs-brad-green-shyam-
seshadri-4144268
Table of Contents
AngularJSTest-driven Development
Credits
About the Author
About the Reviewers
www.PacktPub.com
Support files, eBooks, discount offers, and more
Why subscribe?
Free access for Packt account holders
Preface
What this book covers
Who this book is for
Conventions
Reader feedback
Customer support
Downloading the example code
Errata
Piracy
Questions
1. Introduction to Test-driven Development
An overview of TDD
Fundamentals of TDD
Measuring success
Breaking down the steps
Measure twice cut once
Diving in
Setting up the test
Creating a development to-do list
Test first
Making it run
8.
Making it better
Testingtechniques
Testing with a framework
Testing doubles with Jasmine spies
Stubbing a return value
Testing arguments
Refactoring
Building with a builder
Self-test questions
Summary
2. The Karma Way
JavaScript testing tools
Karma
Protractor
JavaScript testing frameworks
Jasmine
Selenium
Mocha
Birth of Karma
The Karma difference
Importance of combining Karma with AngularJS
Installing Karma
Installation prerequisites
Configuring Karma
Customizing Karma’s configuration
Confirming Karma’s installation and configuration
Common installation/configuration issues
Testing with Karma
Confirming the Karma installation
Using Karma with AngularJS
Getting AngularJS
9.
Bower
Bower installation
Installing AngularJS
InstallingAngular mocks
Initializing Karma
Testing with AngularJS and Karma
A development to-do list
Testing a list of items
Test first
Assemble, Act, and Assert (3 A’s)
Make it run
Make it better
Adding a function to the controller
Test first
Assemble, Act, and Assert (3 A’s)
Make it run
Make it better
Self-test questions
Summary
3. End-to-end Testing with Protractor
An overview of Protractor
Origins of Protractor
End of life
The birth of Protractor
Life without Protractor
Protractor installation
Installation prerequisites
Installing Protractor
Installing WebDriver for Chrome
Customizing configuration
Confirming installation and configuration
10.
Common installation/configuration issues
HelloProtractor
TDD end-to-end
The pre-setup
The setup
Test first
Installing the test web server
Configuring Protractor
Getting down to business
Specification
The development to-do list
Test first
Assemble, Act, Assert (3 A’s)
Running the test
Make it run
Make it better
Cleaning up the gaps
Async magic
Loading a page before test execution
Assertion on elements that get loaded in promises
TDD with Protractor
Self-test questions
Summary
4. The First Step
Preparing the application’s specification
Setting up the project
Setting up the directory
Setting up Protractor
Setting up Karma
Setting up http-server
Top-down or bottom-up approach
11.
Testing a controller
Asimple controller test setup
Initializing the scope
Bring on the comments
Test first
Assemble
Act
Assert
Make it run
Adding the module
Adding the input
Controller
Make it pass
Make it better
Implementing the Submit button
Configuring Karma
Test first
Assemble
Act
Assert
Make it run
Make it better
Back up the test chain
Bind the input
Onwards and upwards
Test first
Assemble
Act
Assert
Make it run
Fixing the unit tests
12.
Make it better
Couplingof the test
Self-test questions
Summary
5. Flip Flop
Fundamentals
Protractor locators
CSS locators
Button and link locators
Angular locators
URL location references
Creating a new project
Setting up headless browser testing for Karma
Preconfiguration
Configuration
Walk-through of Angular routes
Setting up AngularJS routes
Defining directions
Configuring ngRoute
Defining the route controllers
Defining the route views
Assembling the flip flop test
Making the views flip
Asserting a flip
Making flip flop run
Making flip flop better
Searching the TDD way
Deciding on the approach
Walk-through of search query
The search query test
The search query HTML page
13.
The search application
Showme some results!
Creating the search result routes
Testing the search results
Assembling the search result test
Selecting a search result
Confirming a search result
Making the search result test run
Creating a location-aware test
Making the search result better
Confirming the route ID
Setting up the route ID unit test
Confirming the ID
Making the route parameter’s test run
Self-test questions
Summary
6. Telling the World
Before the plunge
Karma configuration
File watching
Using a bottom-up approach
Services
Publishing and subscribing messages
Emitting
Testing emit
Testing broadcast
Testing broadcast
Publishing and subscribing – the good and bad
The good
Communicating through events
Reducing coupling
14.
Harnessing the powerof events
The plan
Rebranding
Seeing recently viewed items
Test first
Assembling SearchController
Selecting a product
Expecting events to be published
Making the search controller run
Recently viewed unit test
Test first
Assembling RecentlyViewedController
Invoking a recently viewed item
Confirming RecentlyViewedController
Making RecentlyViewedController run
End-to-end testing
Test first
Assembling the recently viewed end-to-end test
Selecting a search result
Confirming recently viewed items
Making the recentlyViewedItems test pass
Making recently viewed items better
Creating a product cart
Publisher test first
Assembling searchDetailController
Invoking the saving of a product
Confirming the save event
Making the saveProduct test pass
Test for the subscriber first
Assembling the product cart test
Invoking a saved cart event
15.
Confirming the savedcart
Making the cart controller test run
End-to-end testing
Assembling the cart’s end-to-end test
Invoking a save to cart action
Confirming products have been saved
Making the cart’s end-to-end test pass
Self-test questions
Summary
7. Give Me Some Data
REST – the language of the Web
Getting started with REST
Testing asynchronous calls
Creating asynchronous calls in Karma
Creating asynchronous calls in Protractor
Making REST requests using AngularJS
Testing with AngularJS REST
Testing the product service
Testing $http with Karma
Mocking requests with Protractor
Displaying products with REST
Unit testing product requests
Setting up the project
Karma configuration
Using an API builder pattern
The product data service
The product data controller
Assembling the product controller test
Getting products
Asserting product data results
Making the product data tests run
16.
Testing middle-to-end
Test first
Assemblingthe product test
Getting products
Expecting product data results
Making the product data run
Testing end-to-end
Getting the product data
Self-test questions
Summary
A. Integrating Selenium Server with Protractor
Installation
Protractor configuration
Running Selenium
Let it run
Test first
Assemble
Assert
Make it run
Summary
B. Automating Karma Unit Testing on Commit
GitHub
Test setup
Test scripts
Setting the hook
Creating the hook
Adding a Travis configuration file
References
C. Answers
Chapter 1, Introduction to Test-driven Development
Chapter 2, The Karma Way
17.
Chapter 3, End-to-endTesting with Protractor
Chapter 4, The First Step
Chapter 5, Flip Flop
Chapter 6, Telling the World
Chapter 7, Give Me Some Data
Index
About the Author
TimChaplin lives and breathes software solutions and innovations. During the day, he
works with Fortune 100 enterprise applications, and in the evening, he perfects his craft by
contributing to and distributing open source software, writing, and constantly looking for
ways to increase his knowledge of technology and the world. At an early age, Tim began
developing software and has been hooked on it since. Tim is an established conference
speaker who has extensive experience in developing and leading AngularJS projects. He
has a wide background of JavaScript, C#, Java, and C++ languages. Tim specializes in
leading code quality and testing throughout all his applications. After attending California
State University, Chico, he has gone on to work in Shanghai, Los Angeles, and London.
I would like to thank my wife, Pierra, for always making me think and dream bigger. I
would also like to thank my family for their constant love and support. Pops, this one’s for
you babe.
23.
About the Reviewers
Md.Ziaul Haq is a senior software engineer from Dhaka, Bangladesh, who has been
working with the oDesk core platform development team as a senior JavaScript developer
since 2011. He likes to work mostly on the frontend, though he is a full-stack developer.
JavaScript is his passion and he likes to code in it all day long. He is well known as
jquerygeek in the web community.
Md. Ziaul started his career in 2005 as a software developer. He has work experience with
UNICEF locally and internationally, where he worked with UNICEF’s web CMS. He is
currently pursuing a master’s degree in computer science from United International
University, Dhaka, Bangladesh.
I would like to thank my wife, Richi, and my newborn son, Arabi, who is my inspiration.
Nive Jayasekar started programming in high school. In her last year of high school, she
won $10,500 at a Hackathon for building a mobile artificial-intelligence app. She has
interned at Facebook and LinkedIn, and will soon graduate from Carnegie Mellon
University with a degree in computer science and a minor in machine learning. She is
always interested in building game-changing products. She has 5 years of experience
building web and mobile applications using Python, AngularJS, Java, and Objective C.
I’d like to thank the people at Packt Publishing, Leena Purkait and Kirti Patil, for their
help in producing this book.
Tim Pie is a computer science and business administration double degree student at the
University of Waterloo, Ontario. He has gained a wide range of technical skills through
past projects and internships, including cloud computing, data mining, and full stack web
development. Tim’s current technical interest is focusing on building web applications
using modern web technologies, specifically HTML5 and web components.
I’d like to thank my parents for their constant support of my pursuits, while providing me
great advice along the way.
Andi Smith (@andismith) is a senior architect who specializes in frontend solutions at
ideas and innovation agency, AKQA.
Andi has over 15 years of experience building for the Web and has worked with clients
such as Nike, Ubisoft, Sainsburys, Barclays, Heineken, and MINI. He has also created a
number of open source plugins and sites such as Grunt Responsive Images
(http://www.andismith.com/grunt-responsive-images/) and Secrets of the Browser
Developer Tools (http://devtoolsecrets.com/).
Andi maintains a blog focused on frontend development at http://www.andismith.com/.
I would like to thank my wife, Amy, for all her love and support.
Support files, eBooks,discount offers, and
more
For support files and downloads related to your book, please visit www.PacktPub.com.
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.
https://www2.packtpub.com/books/subscription/packtlib
Do you need instant solutions to your IT questions? PacktLib is Packt’s online digital
book library. Here, you can search, access, and read Packt’s entire library of books.
26.
Why subscribe?
Fully searchableacross every book published by Packt
Copy and paste, print, and bookmark content
On demand and accessible via a web browser
27.
Free access forPackt account holders
If you have an account with Packt at www.PacktPub.com, you can use this to access
PacktLib today and view 9 entirely free books. Simply use your login credentials for
immediate access.
28.
Preface
The book willprovide the reader with a complete guide to the test-driven development
(TDD) approach for AngularJS. It will provide step-by-step, clear examples to continually
reinforce TDD best practices. The book will look at both unit testing with Karma and end-
to-end testing with Protractor. It will not only focus on how to use the tools, but also on
understanding the reason they were built, and why they should be used. Throughout, there
will be focus on when, where, and how to use these tools, constantly reinforcing the
principles of the TDD life cycle (test, execute, refactor).
29.
What this bookcovers
This book is basically split into two parts. The initial chapters focus on the TDD life cycle,
and how Karma and Protractor fit into the life cycle and development of an AngularJS
application. As we proceed, you’ll get a step-by-step approach to AngularJS TDD using
Karma and Protractor. Each of the chapters builds up on the previous one and introduces
how to test several different AngularJS components.
Chapter 1, Introduction to Test-driven Development, is an introduction to the concepts of
TDD and testing techniques.
Chapter 2, The Karma Way, explores the origins of Karma and why it is an essential tool
for any AngularJS project.
Chapter 3, End-to-end Testing with Protractor, introduces the simplicity of Protractor, an
end-to-end testing tool built specifically for AngularJS.
Chapter 4, The First Steps, covers the TDD journey and shows the fundamentals and tools
in action.
Chapter 5, Flip Flop, expands to include testing for multiple controllers, partial views,
location references, CSS, and HTML element building on the initial foundational aspects
learned in the previous chapter.
Chapter 6, Telling the World, dives into communicating across controllers, and testing
services and broadcasting.
Chapter 7, Give Me Some Data, dives into how to apply several of the concepts shown
previously, and extend them to pull data using an external API.
Appendix A, Integrating Selenium Server with Protractor, walks through setting up and
configuring Protractor to use a standalone Selenium server.
Appendix B, Automating Karma Unit Testing on Commit, covers how to set up Travis CI,
a platform for continuous integration, and setting up Karma to test your application.
30.
Who this bookis for
This book is for the developer who wants to go beyond the basic tutorials, and wants to
take the plunge into AngularJS development. This book is for the developer who has
experience with AngularJS and has walked through the basic tutorials but wants to
understand the wider context of when, why, and how to apply testing techniques and best
practices to create quality-clean code. To get the most out of this book, it is preferred that
the reader has basic understanding of HTML, JavaScript, and AngularJS.
31.
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, database table names, folder names, filenames, file extensions,
pathnames, dummy URLs, user input, and Twitter handles are shown as follows: “Create a
web page and import calculator.js for testing.”
A block of code is set as follows:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<script src="calculator.js"></script>
</body>
</html>
Any command-line input or output is written as follows:
$ node calculator.js
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: “Traditionally, tests were
run by having to manually launch a browser and check for results by continually hitting
the Refresh button.”
Note
Warnings or important notes appear in a box like this.
Tip
Tips and tricks appear like this.
32.
Reader feedback
Feedback fromour readers is always welcome. Let us know what you think about this
book—what you liked or disliked. Reader feedback is important for us as it helps us
develop titles that you will really get the most out of.
To send us general feedback, simply e-mail <feedback@packtpub.com>, and mention the
book’s title in 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.
33.
Customer support
Now thatyou are the proud owner of a Packt book, we have a number of things to help
you to get the most from your purchase.
34.
Downloading the examplecode
You can download the example code files from your account at http://www.packtpub.com
for all the Packt Publishing books you have purchased. 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.
35.
Errata
Although we havetaken 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 could 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/submit-errata,
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 to our website or added to any list of existing errata under the
Errata section of that title.
To view the previously submitted errata, go to
https://www.packtpub.com/books/content/support and enter the name of the book in the
search field. The required information will appear under the Errata section.
36.
Piracy
Piracy of copyrightmaterial 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.
37.
Questions
You can contactus 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.
38.
Chapter 1. Introductionto Test-driven
Development
AngularJS is at the forefront of client-side JavaScript testing. Every AngularJS tutorial
includes an accompanying test, and event test modules are part of the core AngularJS
package. The Angular team is focused on making testing fundamental to web
development.
This chapter introduces you to the fundamentals of test-driven development with
AngularJS including:
An overview of test-driven development (TDD)
The TDD life cycle: test first, make it run, make it better
Common testing techniques
39.
An overview ofTDD
TDD is not used only to develop software. The fundamental principles can be seen in
many industries. This section will explore the fundamentals of TDD and how they are
applied by a tailor.
40.
Fundamentals of TDD
Knowwhat to code before you code. This may sound cliché, but this is essentially what
TDD gives you. TDD begins by defining expectations, then makes you meet the
expectations, and finally forces you to refine the changes after the expectations have been
met.
Here are a couple of clear benefits of using TDD:
Knowing before you code: A test provides a clear vision of what code needs to do in
order to be successful. Setting up tests first allows focus on only components that
have been defined in tests.
Confidence in refactoring: Refactoring involves moving, fixing, and changing a
project. Tests protect the core logic from refactoring by ensuring that the logic
behaves independently of the code structure.
Documentation: Tests define expectations that a particular object or function must
meet. The expectation acts as a contract, and can be used to see how a method should
or can be used. This makes the code readable and easier to understand.
41.
Measuring success
TDD isnot just a software development practice. The fundamental principles are shared
by other craftsmen as well. One of these craftsmen is a tailor, whose success depends on
precise measurements and careful planning.
Breaking down the steps
Here are the high-level steps a tailor takes to make a suit:
1. Test first:
Determining the measurements for the suit
Having the customer determine the style and material they want for their suit
Measuring the customer’s arms, shoulders, torso, waist, and legs
2. Making the cuts:
Measuring the fabric and cut
Selecting the fabric based on the desired style
Measuring the fabric based on the customer’s waist and legs
Cutting the fabric based on the measurements
3. Refactoring:
Comparing the resulting product to the expected style, reviewing, and making
changes
Comparing the cut and look to the customer’s desired style
Making adjustments to meet the desired style
4. Repeating:
Test first: Determining the measurements for the pants
Making the cuts: Measuring the fabric and making the cuts
Refactor: Making changes based on the reviews
The preceding steps are an example of a TDD approach. The measurements must be taken
before the tailor can start cutting up the raw material. Imagine for a moment if the tailor
didn’t use a test-driven approach and didn’t use a measuring tape (testing tool). It would
be ridiculous if the tailor started cutting before measuring.
As a developer, do you “cut before measuring”? Would you trust a tailor without a
measuring tape? How would you feel about a developer who doesn’t test?
Measure twice cut once
The tailor always starts with measurements. What would happen if the tailor made cuts
before measuring? What would happen if the fabric was cut too short? How much extra
time would go into the tailoring? Measure twice, cut once.
Software developers can choose from an endless amount of approaches to use before
42.
starting developing. Onecommon approach is to work off a specification. A documented
approach may help in defining what needs to be built; however, without tangible criteria
for how to meet a specification, the actual application that gets developed maybe
completely different than the specification. With a TDD approach (test first, make it run,
and make it better), every stage of the process verifies that the result meets the
specification. Think about how a tailor continues to use a measuring tape to verify the suit
throughout the process.
TDD embodies a test-first methodology. TDD gives developers the ability to start with a
clear goal and write code that will directly meet a specification. Develop like a
professional and follow the practices that will help you write quality software.
43.
Diving in
It istime to dive into some actual code. This walk-through will take you through adding
the multiplication functionality to a calculator. Remember the TDD life cycle: test first,
make it run, and make it better.
Setting up the test
The initial calculator is in a file called calculator.js and is initialized as an object as
follows:
var calculator = {};
The test will be run through a web browser using a basic HTML page. Create a web page
and import calculator.js to test it. Save the web page as testRunner.html. To run the
test, open a browser and run testRunner.html. Here is the code for testRunner.html:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<script src="calculator.js"></script>
</body>
</html>
Now that the project is set up, the next step is to create the development to-do list.
Creating a development to-do list
A development to-do list helps organize and focus your tasks. It also provides a place to
write down ideas during the development process.
Here is the initial step for creating a development to-do list:
Add multiplication functionality: 3 * 3 = 9
The preceding list describes what needs to be done. It also provides a clear example of
how to verify multiplication: 3 * 3 = 9.
Test first
Although you can write the multiplication function quickly, remember that once the habit
of TDD is set in place, it will be just as quick to write the test and code. Here are the steps
for the first test:
1. Open calculator.js.
2. Create a new function to test multiplying 3 * 3:
function multipleTest1(){
//Test
44.
var result =calculator.multiply(3,3);
//Assert Result is expected
if (result === 9) {
console.log('Test Passed');
}
else{
console.log('Test Failed');
}
};
The test calls a multiply function, which still needs to be defined. It then asserts that the
results are as expected by displaying a pass or fail message. Remember, in TDD, you are
looking at the use of the method and explicitly writing how it should be used. This allows
you to define the interface through a use case, as opposed to only looking at the limited
scope of the function being developed.
The next step in the TDD life cycle will be focused on making the test run.
Making it run
This step is about making the test run, just as the tailor did with the suit. The
measurements were taken during the test step, and now the application can be molded to
fit the measurements. Here are the steps to run the test:
1. Open the browser with testRunner.html.
2. Open the JavaScript developer Console window.
The test throws an error, as shown in the following screenshot:
The error thrown is expected as the calculator application calls a function that hasn’t been
created yet: calculator.multiply.
In TDD, the focus is on adding the smallest change to get a test to pass. There is no need
to actually implement the multiplication logic. This may seem unintuitive. The point is
once a passing test exists, it should always pass. When a method contains fairly complex
logic, it is easier to run a passing test against it to ensure it meets the expectations.
What is the smallest change that can be made to make the test pass? By returning the
expected value of 9, the test should pass. Although this won’t add the multiply function,
it will confirm the application wiring. In addition, after you have passed the test, making
future changes will be easy as you have to simply keep the test passing!
Now, add the multiply function and have it return the required value 9:
45.
var calculator ={
multiply : function(){
return 9;
}
};
In the browser, the JavaScript console reruns the test. The result should be as follows:
Yes! The test passed. Time to cross out the first item from the to-do list:
Add multiplication functionality: 3 * 3 = 9
Now that there is a passing test, the next step will be to remove the hardcoded value in the
multiply function.
Making it better
The refactoring step needs to remove the hardcoded return value of the multiply
function. The required logic is as follows:
var calculator = {
multiply : function(amount1,amount2){
return amount1 * amount2;
}
};
Rerun the tests and confirm the test passes. Excellent! Now the multiply function is
complete. Here is the full code for the calculator and test:
var calculator = {
multiply : function(amount1,amount2){
return amount1* amount2;
}
};
var multipleTest1 = function (){
var result = calculator.multiply(3,3);
if (result === 9) {
console.log('Test Passed');
}
else{
console.log('Test Failed');
}
};
Testing techniques
It isimportant to understand some fundamental techniques and approaches to testing. This
section will walk you through a couple of examples of techniques that will be leveraged in
this book. This includes:
Testing doubles with Jasmine spies
Refactoring
Building patterns
In addition, here are additional terms that will be used:
Function under test: This is the function being tested. It is also referred to as system
under test, object under test, and so on.
The 3 A’s (Arrange, Act, and Assert): This is a technique used to set up tests, first
described by Bill Wake (http://xp123.com/articles/3a-arrange-act-assert/). The 3 A’s
will be discussed further in Chapter 2, The Karma Way.
48.
Testing with aframework
Although a simple web page can be used to perform tests, as seen earlier in this chapter, it
is much easier to use a testing framework. A testing framework provides methods and
structures to test. This includes a standard structure to create and run tests, the ability to
create assertions/expectations, the ability to use test doubles, and more. This book uses
Jasmine as the test framework. Jasmine is a behavior-driven testing framework. It is
highly compatible with testing AngularJS applications. In Chapter 2, The Karma Way, we
will take a more in-depth look at Jasmine.
49.
Testing doubles withJasmine spies
A test double is an object that acts and is used in place of another object. Take a look at
the following object that needs to be tested:
var objectUnderTest = {
someFunction : function(){}
};
Using a test double, you can determine the number of times someFunction gets called.
Here is an example:
var objectUnderTest = {
someFunction : function(){}
};
jasmine.spyOn(objectUnderTest,'someFunction');
objectUnderTest.someFunction ();
objectUnderTest.someFunction();
console.log(objectUnderTest.someFunction.count);
The preceding code creates a test double using a Jasmine spy (jasmine.spyOn). The test
double is then used to determine the number of times someFunction gets called. A
Jasmine test double offers the following features and more:
The count of calls on a function
The ability to specify a return value (stub a return value)
The ability to pass a call to the underlying function (pass through)
Throughout this book, you will gain further experience in the use of test doubles.
Stubbing a return value
The great thing about using a test double is that the underlying code of a method does not
have to be called. With a test double, you can specify exactly what a method should return
for a given test. Here is an example function:
var objectUnderTest = {
someFunction : function(){ return 'stub me!'; }
};
The preceding object (objectUnderTest) has a function (someFunction) that needs to be
stubbed. Here is how you can stub the return value using Jasmine:
jasmine.spyOn(objectUnderTest,'someFunction')
.and
.returnValue('stubbed value');
Now, when objectUnderTest.someFunction is called, stubbed value will be returned.
Here is how the preceding stubbed value can be confirmed using console.log:
var objectUnderTest = {
50.
someFunction : function(){return 'stub me!'; }
};
//before the return value is stubbed
Console.log(objectUnderTest.someFunction());
//displays 'stub me'
jasmine.spyOn(objectUnderTest,'someFunction')
.and
.returnValue('stubbed value');
//After the return value is stubbed
Console.log(objectUnderTest.someFunction());
//displays 'stubbed value'
Testing arguments
A test double provides insights into how a method is used in an application. As an
example, a test might want to assert what arguments a method was called with or the
number of times a method was called. Here is an example function:
var objectUnderTest = {
someFunction : function(arg1,arg2){}
};
Here are the steps to test the arguments the preceding function is called with:
1. Create a spy so that the arguments called can be captured:
jasmine.spyOn(objectUnderTest,'someFunction');
2. Then to access the arguments, do the following:
//Get the arguments for the first call of the function
var callArgs = objectUnderTest.someFunction.call.argsFor(0);
console.log(callArgs);
//displays ['param1','param2']
3. Here is how the arguments can be displayed using console.log:
var objectUnderTest = {
someFunction : function(arg1,arg2){}
};
//create the spy
jasmine.spyOn(objectUnderTest,'someFunction');
//Call the method with specific arguments
objectUnderTest.someFunction('param1','param2');
//Get the arguments for the first call of the function
var callArgs = objectUnderTest.someFunction.call.argsFor(0);
console.log(callArgs);
//displays ['param1','param2']
52.
Refactoring
Refactoring is theact of restructuring, rewriting, renaming, and removing code in order to
improve the design, readability, maintainability, and overall aesthetic of a piece of code.
The TDD life cycle step of “making it better” is primarily concerned with refactoring.
This section will walk you through a refactoring example. Here is an example of a
function that needs to be refactored:
var abc = function(z){
var x = false;
if(z > 10)
return true;
return x;
}
This function works fine and does not contain any syntactical or logical issues. The
problem is that the function is difficult to read and understand. Refactoring this function
will improve the naming, structure, and definition. The exercise will remove the
masquerading complexity and reveal the function’s true meaning and intention. Here are
the steps:
1. Rename the function and variable names to be more meaningful, that is, rename x
and z so that they make sense:
var isTenOrGreater = function(value){
var falseValue = false;
if(value > 10)
return true;
return falseValue;
}
2. Now, the function can easily be read and the naming makes sense.
3. Remove unnecessary complexity. In this case, the if conditional statement can be
removed completely:
var isTenOrGreater = function(value){
return value > 10;
};
4. Reflect on the result.
At this point, the refactor is complete, and the function’s purpose should jump out at
you. The remaining question that should be asked is “why does this method exist in
the first place?”.
This example only provided a brief walk-through of the steps that can be taken to identify
issues in code and how to improve them. Other examples will be used throughout this
book.
53.
Building with abuilder
The builder pattern uses a builder object to create another object. Imagine an object with
ten properties. How will test data be created for every property? Will the object have to be
recreated in every test?
A builder object defines an object to be reused across multiple tests. The following code
snippet provides an example of the use of this pattern. This example will use builder
object in the validate method:
var book = {
id : null,
author : null,
dateTime : null
};
The book object has three properties: id, author, and dateTime. From a testing
perspective, you would want the ability to create a valid object, that is, one that has all the
fields defined. You may also want to create an invalid object with missing properties, or
you may want to set certain values in the object to test the validation logic, that is,
dateTime is an actual date.
Here are the steps to create a builder for the dateTime object:
1. Create a builder function:
var bookBuilder = function();
2. Create a valid object within the builder:
var bookBuilder = function(){
var _resultBook = {
id: 1,
author: 'Any Author',
dateTime: new DateTime()
};
}
3. Create a function to return the built object:
var bookBuilder = function(){
var _resultBook = {
id: 1,
author: "Any Author",
dateTime: new DateTime()
};
this.build = function(){
return _resultBook;
}
}
4. Create another function to set the _resultBook author field:
var bookBuilder = function(){
54.
var _resultBook ={
id: 1,
author: 'Any Author',
dateTime: new DateTime()
};
this.build = function(){
return _resultBook;
};
this.setAuthor = function(author){
_resultBook.author = author;
};
};
5. Make the function fluent so that calls can be chained:
this.setAuthor = function(author){
_resultBook.author = author;
return this;
};
6. A setter function will also be created for dateTime:
this.setDateTime = function(dateTime){
_resultBook.dateTime = dateTime;
return this;
};
Now, bookBuilder can be used to create a new book as follows:
var builtBook = bookBuilder.setAuthor('Tim Chaplin')
.setDateTime(new Date())
.build();
The preceding builder can now be used throughout your tests to create a single consistent
object. Here is the complete builder for your reference:
var bookBuilder = function(){
var _resultBook = {
id: 1,
author: 'Any Author',
dateTime: new DateTime()
};
this.build = function(){
return _resultBook;
};
this.setAuthor = function(author){
_resultBook.author = author;
return this;
};
this.setDateTime = function(dateTime){
_resultBook.dateTime = dateTime;
return this;
};
"When Madam wasa young grandmother, was she as beautiful as
she is now?"
"Why, yes, I fancy so," replied Toto. "Only she wasn't a
grandmother then, you know."
"How so?" inquired Bruin. "What else could she be? You never
were anything but a boy, were you?"
"Oh, no, of course not!" said Toto. "But that is different. When
Granny was young, she was a girl, you see."
"I don't believe it!" said the bear, stoutly. "I—do—not—believe it!
I saw a girl once—many years ago; it squinted, and its hair was
frowzy, and it wore a hideous basket of flowers on its head,—a
dreadful creature! Madam never can have looked like that!"
At this moment a knock was heard at the door. Toto flew to open
it, and with a beaming face ushered in the old hermit, who entered
leaning on his stick, with his crow perched on one shoulder and the
hawk on the other.
Then, what greetings followed! What introductions! What bows
and courtesies, and whisking of tails and flapping of wings! The
hermit's bow in greeting to the old lady was so stately that Master
Coon was consumed with a desire to imitate it; and in so doing, he
stepped back against the nose of the tea-kettle and burned himself,
which caused him to retire suddenly under the table with a
smothered shriek. (But the kettle was glad.) And the hawk and the
pigeon, the raccoon and the crow, the hermit and the bear, all shook
paws and claws, and vowed that they were delighted to see each
other; and what is more, they really were delighted, which is not
always the case when such vows are made.
Now, when all had become well acquainted, and every heart was
prepared to be merry, they sat down to supper; and the supper was
not one which was likely to make them less cheerful. For there was
chicken and ham, and, oh, such a mutton-pie! You never saw such a
57.
pie; the standingcrust was six inches high, and solid as a castle
wall; and on that lay the upper-crust, as lightly as a butterfly resting
on a leaf; while inside was store of good mutton, and moreover
golden eggballs and tender little onions, and gravy as rich as all the
kings of the earth put together. Ay! and besides all that there was
white bread like snow, and brown bread as sweet as clover-
blossoms, and jam and gingerbread, and apples and nuts, and
pitchers of cream and jugs of buttermilk. Truly, it does one's heart
good to think of such a supper, and I only wish that you and I had
been there to help eat it. However, there was no lack of hungry
mouths, with right good-will to keep their jaws at work, and for a
time there was little conversation around the table, but much joy
and comfort in the good victuals.
The good grandmother ate little herself, though she listened with
pleasure to the stirring sound of knives and forks, which told her
that her guests were well and pleasantly employed. Presently the
hermit addressed her, and said:—
"Honored Madam, you will be glad to know that there has been a
great change in the weather during the past week. Truly, I think the
spring is at hand; for the snow is fast melting away, the sun shines
with more than winter's heat, and the air to-day is mild and soft."
At these words there was a subdued but evident excitement
among the company. The raccoon and the squirrel exchanged swift
and significant glances; the birds, as if by one unconscious impulse,
ruffled their feathers and plumed themselves a little. But boy Toto's
face fell, and he looked at the bear, who, for his part, scratched his
nose and looked intently at the pattern on his plate.
"It has been a long, an unusually long, season," continued the
hermit, "though doubtless it has seemed much shorter to you in
your cosey cottage than to me in my lonely cavern. But I have lived
the forest-life long enough to know that some of you, my friends,"
and he turned with a smile to the forest-friends, "must be already
58.
longing to hearthe first murmur of the greenwood spring, and to
note in tree and shrub the first signs of awakening life."
There was a moment of silence, during which the raccoon shifted
uneasily on his seat, and looked about him with restless, gleaming
eyes. Suddenly the silence was broken by a singular noise, which
made every one start. It was a long-drawn sound, something
between a snort, a squeal, and a snore; and it came from—where
did it come from?
"Was it you?" said one.
"No! was it you?"
"It seemed to come," said the hawk, who sat facing the fire,
"from the wall near the fireplace."
At this moment the sound was heard again, louder and more
distinct, and this time it certainly did come from the wall,—or rather
from the cupboard in the wall, near the fireplace.
"Yaw-haw! yaw-ah-hee!" Then came a muffled, scuffling sound,
and finally a shrill peevish voice cried, "Let me out! let me out, I say!
Coon, I know your tricks; let me out, or I'll tell Bruin this minute!"
The bear burst into a volcanic roar of laughter, which made the
hermit start and turn pale in spite of himself, and going to the
cupboard he drew out the unhappy woodchuck, hopelessly
entangled in his worsted covering, from which he had been vainly
struggling to free himself.
Oh, how they all laughed! It seemed as they would never have
done laughing; while every moment the woodchuck grew more
furious,—squeaking and barking, and even trying to bite the mighty
paw which held him. But the wood-pigeon had pity on him, and with
a few sharp pulls broke the worsted net, and begged Bruin to set
him down on the table. This being done, Master Chucky found his
nose within precisely half an inch of a most excellent piece of dried
59.
beef, upon whichhe fell without more ado, and stayed not to draw
breath till the plate was polished clean and dry.
That made every one laugh again, and altogether they were very
merry, and fell to playing games and telling stories, leaving the
woodchuck to try the keen edge of his appetite upon every dish on
the table. By-and-by, however, this gentleman could eat no more; so
he wiped his paws and whiskers, brushed his coat a little, and then
joined in the sport with right good-will.
It was a pleasant sight to see the great bear blindfolded, chasing
Toto and Coon from one corner to another, in a grand game of
blindman's buff; it was pleasant to see them playing leap-frog, and
spin-the-platter, and many a good old-fashioned game besides.
Then, when these sat down to rest and recover their breath, what a
treat it was to see the four birds dance a quadrille, to the music of
Toto's fiddle! How they fluttered and sidled, and hopped and bridled!
How gracefully Miss Mary courtesied to the stately hawk; and how
jealous the crow was of this rival, who stood on one leg with such a
perfect grace!
Ah! altogether that was a party worth going to. And when late in
the evening it broke up, and the visitors started on their homeward
walk, all declared it was the merriest time they had yet had together,
and all wished that they might have many more such times. And yet
each one knew in his heart,—and grieved to know,—that it was the
last, and that the end was come.
60.
Y
CHAPTER XV.
ES, theend was come! The woodchuck sounded, the next
morning, the note which had for days been vibrating in the
hearts of all the wild creatures, but which they had been loth to
strike, for Toto's sake.
"Come!" he said. "It is time we were off. I don't know what you
are all thinking of, to stay on here after you are awake. I smelt the
wet earth and the water, and the sap running in the trees, even in
that dungeon where you had put me. The young reeds will soon be
starting beside the pool, and it is my work to trim them and thin
them out properly; besides, I am going to dig a new burrow, this
year. I tell you I must be off."
And the squirrel with a chuckle, and the wood-pigeon with a sigh,
and the raccoon with a strange feeling which he hardly understood,
but which was not all pleasure, echoed the words, "We must be off!"
Only the bear said nothing, for he was in the wood-shed, splitting
kindling-wood with a fury of energy which sent the chips flying as if
he were a saw-mill.
So it came to pass that on a soft, bright day in April, when the
sun was shining sweetly, and the wind blew warm from the south,
and the buds were swelling on willow and alder, the party of friends
stood around the door of the little cottage, exchanging farewells,
half merry, half sad, and wholly loving.
"After all, it is hardly good-by!" said the squirrel, gayly. "We shall
be here half the time, just as we were last summer; and the other
half, Toto will be in the forest. Eh, Bruin?"
But Bruin rubbed his nose with his right paw, and said nothing.
61.
"And you willcome to the forest, too, dear Madam!" cried the
raccoon, "will you not? You will bring the knitting and the
gingerbread, and we will have picnics by the pool, and you will learn
to love the forest as much as Toto does. Won't she, Bruin?"
But Bruin rubbed his nose with his left paw, and still said nothing.
"And when my nest is made, and my little ones are fledged,"
cooed the wood-pigeon in her tender voice, "their first flight shall be
to you, dear Madam, and their first song shall tell you that they love
you, and that we love you, every day and all day. For we do love
you; don't we, Bruin?"
But the bear only looked helplessly around him, and scratched his
head, and again said nothing.
"Well," said Toto, cheerily, though with a suspicion of a quiver in
his voice, "you are all jolly good fellows, and we have had a merry
winter together. Of course we shall miss you sadly, Granny and I;
but as you say, Cracker, we shall all see each other every day; and I
am longing for the forest, too, almost as much as you are."
"Dear friends," said the blind grandmother, folding her hands
upon her stick, and turning her kindly face from one to the other of
the group,—"dear friends, merry and helpful companions, this has
indeed been a happy season that we have spent together. You have,
one and all, been a comfort and a help to me, and I think you have
not been discontented yourselves; still, the confinement has of
course been strange to you, and we cannot wonder that you pine for
your free, wildwood life. Coon, give me your paw! it is a mischievous
paw, but it has never played any tricks on me, and has helped me
many and many a time. My little Cracker, I shall miss your merry
chatter as I sit at my spinning-wheel. Mary, and Pigeon Pretty, let me
stroke your soft feathers once more, by way of 'good-by.'
Woodchuck, I have seen little of you, but I trust you have enjoyed
your visit, in your own way.
62.
"And now, lastof all, Bruin! my good, faithful Bruin! come here
and let me shake your honest, shaggy paw, and thank you for all
that you have done for me and for my boy." She paused, but no
answer came.
"Why, where is Bruin?" cried Toto, starting and looking round;
"surely he was here a minute ago. Bruin! Bruin! where are you?"
But no deep voice was heard, roaring cheerfully, "Here, Toto boy!"
No shaggy form came in sight. Bruin was gone.
"He has gone on ahead, probably," said the raccoon; "he said
something, this morning, about not liking to say good-by. Come, you
others, we must follow our leader. Good-by, dear Madam! See you
to-morrow, Toto!"
"Good-by!"
"Good-by!"
"Good-by!" cried all the others.
And with many a backward glance, and many a wave of paw, or
tail, or fluttering wing, the party of friends took their way to the
forest home.
Boy Toto stood with his hands in his pockets, looking after them
with bright, wide-open eyes. He did not cry,—it was a part of Toto's
creed that boys did not cry after they had left off petticoats,—but he
felt that if he had been a girl, the tears might have come in spite of
him. So he stared very hard, and puckered his mouth in a silent
whistle, and felt of the marbles in his pockets,—for that is always a
soothing and comforting thing to do.
"Toto, dear," said his grandmother, "do you think our Bruin is
really gone, without saying a word of farewell to us?"
"So it seems!" said the boy, briefly.
63.
"I am verymuch grieved!" cried the old lady, putting her
handkerchief to her sightless eyes,—"very, very much grieved! If it
had been Coon, now, I should not have been so much surprised; but
for Bruin, our faithful friend and helper, to leave us so, seems—"
"Hello!" cried Toto, starting suddenly, "what is that noise?"
Both listened, and, lo! on the quiet air came the sharp crashing
sound of an axe.
"He's there!" cried the boy. "He isn't gone! I'll go—" and with that
he went, as if he had been shot out of a catapult.
Rushing into the wood-shed, he caught sight of the well-beloved
shaggy figure, just raising the axe to deliver a fearful blow at an
unoffending log of wood. Flinging his arms round it (the figure, not
the axe nor the log), he gave it such a violent hug that bear and boy
sat down suddenly on the ground, while the axe flew to the other
end of the shed.
"Oh, Bruin, Bruin!" cried Toto, "we thought you were gone,
without saying a word to us. How could you frighten us so?"
The bear rubbed his nose confusedly, and muttered something
about "a few more sticks in case of cold weather."
But here Toto burst out laughing in spite of himself, for the shed
was piled so high with kindling-wood that the bear sat as it were at
the bottom of a pit whose sides of neatly split sticks rose high above
his head.
"You old goose!" cried the boy. "There's kindling-wood enough
here to last us ten years, at the very least. Come away! Granny
wants you. She thought—"
"There will be more butter to make, now, Toto, since that new
calf has come," said the bear, breaking in with apparent irrelevance.
"I suppose there will!" said the boy, staring. "What of it?"
64.
"And that pigis getting too big for you to manage," continued
Bruin, in a serious tone. "He was impudent to me the other day, and
I had to take him up by the tail and swing him, before he would
apologize. Now, you couldn't take him up by the tail, Toto, much less
swing him, and there is no use in your deceiving yourself about it."
"Of course I couldn't!" cried Toto. "No one could, except you, old
monster. But what are you thinking about that for, now? Come
along, I tell you! Granny will think you are gone, after all." And
catching the bear by the ear, he led him back in triumph to the
cottage-door, crying, "Granny, Granny! here he is! Now give him a
good scolding, please, for frightening us so."
But the grandmother never scolded. She only stroked the shaggy
black fur, and said, "Bruin, dear! my good, faithful, true-hearted
Bruin! I could not bear to think that you had left me without saying
good-by. That hurt me very much. But you would not have done it,
would you, Bruin? We ought to have known you better."
The bear looked about him distractedly, and bit his paw severely,
as if to relieve his feelings. "Yes I would!" he cried. "At least, if I
meant to say good-by. I wouldn't say it, because I couldn't. But I
don't mean to say it,—I mean I don't mean to do it. If you don't
want me in the house,—being large and clumsy, as I am well aware,
and ugly too,—I can sleep out by the pump, and come in to do the
work. But I cannot leave the boy, please, dear Madam, nor you. And
the calf wants attention, and that pig ought to be swung at least
once a week, and—and—"
But there was no need of further speech, for Toto's arms were
clinging round his neck, and Toto's voice was shouting exclamations
of delight; and the grandmother was shaking his great black paw,
and calling him her best friend, her dearest old Bruin, and telling him
that he should never leave them.
And, in fact, he never did leave them. He settled down quietly in
the little cottage, and washed and churned, baked and brewed,
65.
milked the cowand kept the pig in order. Happy was the good bear,
and happy was Toto, in those pleasant days. For every afternoon,
when the work was done, they welcomed one or all of their forest
friends; or else they sought the green, beloved forest themselves,
and sat beside the fairy pool, and wandered in the cool green mazes
where all was sweetness and peace, with rustle of leaves and
murmur of water, and chirp of bird and insect. But evening found
them always at the cottage door again, bringing their woodland
joyousness to the blind grandmother, making the kitchen ring with
laughter as they related the last exploits of the raccoon or the
squirrel, or described the courtship of the parrot and the crow.
And if you had asked any of the three, as they sat together in the
porch, who was the happiest person in the world, why, Toto and the
Grandmother would each have answered, "I!" But Bruin, who had
never studied grammar, and knew nothing whatever about his
nominatives and his accusatives, would have roared with a thunder-
burst of enthusiasm,
"Me!!!"
University Press: John Wilson & Son, Cambridge.
Transcriber's Notes:
Obvious punctuation errors repaired.
The remaining corrections made are listed below
and also indicated by dotted lines under the
66.
corrections. Scroll themouse over the word and the
original text will appear.
Page 44, illustration caption, "Wah-song! Wah-
song!" changed to "Wah-Song! Wah-Song!" (Golden
Dragon. "Wah-Song! Wah-Song! Awake!")
Page 194, "gigantie" changed to "gigantic"
(statement, the gigantic)
67.
*** END OFTHE PROJECT GUTENBERG EBOOK TOTO'S MERRY
WINTER ***
Updated editions will replace the previous one—the old editions will
be renamed.
Creating the works from print editions not protected by U.S.
copyright law means that no one owns a United States copyright in
these works, so the Foundation (and you!) can copy and distribute it
in the United States without permission and without paying
copyright royalties. Special rules, set forth in the General Terms of
Use part of this license, apply to copying and distributing Project
Gutenberg™ electronic works to protect the PROJECT GUTENBERG™
concept and trademark. Project Gutenberg is a registered trademark,
and may not be used if you charge for an eBook, except by following
the terms of the trademark license, including paying royalties for use
of the Project Gutenberg trademark. If you do not charge anything
for copies of this eBook, complying with the trademark license is
very easy. You may use this eBook for nearly any purpose such as
creation of derivative works, reports, performances and research.
Project Gutenberg eBooks may be modified and printed and given
away—you may do practically ANYTHING in the United States with
eBooks not protected by U.S. copyright law. Redistribution is subject
to the trademark license, especially commercial redistribution.
START: FULL LICENSE
PLEASE READ THISBEFORE YOU DISTRIBUTE OR USE THIS WORK
To protect the Project Gutenberg™ mission of promoting the free
distribution of electronic works, by using or distributing this work (or
any other work associated in any way with the phrase “Project
Gutenberg”), you agree to comply with all the terms of the Full
Project Gutenberg™ License available with this file or online at
www.gutenberg.org/license.
Section 1. General Terms of Use and
Redistributing Project Gutenberg™
electronic works
1.A. By reading or using any part of this Project Gutenberg™
electronic work, you indicate that you have read, understand, agree
to and accept all the terms of this license and intellectual property
(trademark/copyright) agreement. If you do not agree to abide by all
the terms of this agreement, you must cease using and return or
destroy all copies of Project Gutenberg™ electronic works in your
possession. If you paid a fee for obtaining a copy of or access to a
Project Gutenberg™ electronic work and you do not agree to be
bound by the terms of this agreement, you may obtain a refund
from the person or entity to whom you paid the fee as set forth in
paragraph 1.E.8.
1.B. “Project Gutenberg” is a registered trademark. It may only be
used on or associated in any way with an electronic work by people
who agree to be bound by the terms of this agreement. There are a
few things that you can do with most Project Gutenberg™ electronic
works even without complying with the full terms of this agreement.
See paragraph 1.C below. There are a lot of things you can do with
Project Gutenberg™ electronic works if you follow the terms of this
agreement and help preserve free future access to Project
Gutenberg™ electronic works. See paragraph 1.E below.
70.
1.C. The ProjectGutenberg Literary Archive Foundation (“the
Foundation” or PGLAF), owns a compilation copyright in the
collection of Project Gutenberg™ electronic works. Nearly all the
individual works in the collection are in the public domain in the
United States. If an individual work is unprotected by copyright law
in the United States and you are located in the United States, we do
not claim a right to prevent you from copying, distributing,
performing, displaying or creating derivative works based on the
work as long as all references to Project Gutenberg are removed. Of
course, we hope that you will support the Project Gutenberg™
mission of promoting free access to electronic works by freely
sharing Project Gutenberg™ works in compliance with the terms of
this agreement for keeping the Project Gutenberg™ name associated
with the work. You can easily comply with the terms of this
agreement by keeping this work in the same format with its attached
full Project Gutenberg™ License when you share it without charge
with others.
1.D. The copyright laws of the place where you are located also
govern what you can do with this work. Copyright laws in most
countries are in a constant state of change. If you are outside the
United States, check the laws of your country in addition to the
terms of this agreement before downloading, copying, displaying,
performing, distributing or creating derivative works based on this
work or any other Project Gutenberg™ work. The Foundation makes
no representations concerning the copyright status of any work in
any country other than the United States.
1.E. Unless you have removed all references to Project Gutenberg:
1.E.1. The following sentence, with active links to, or other
immediate access to, the full Project Gutenberg™ License must
appear prominently whenever any copy of a Project Gutenberg™
work (any work on which the phrase “Project Gutenberg” appears,
or with which the phrase “Project Gutenberg” is associated) is
accessed, displayed, performed, viewed, copied or distributed:
71.
This eBook isfor the use of anyone anywhere in the United
States and most other parts of the world at no cost and with
almost no restrictions whatsoever. You may copy it, give it away
or re-use it under the terms of the Project Gutenberg License
included with this eBook or online at www.gutenberg.org. If you
are not located in the United States, you will have to check the
laws of the country where you are located before using this
eBook.
1.E.2. If an individual Project Gutenberg™ electronic work is derived
from texts not protected by U.S. copyright law (does not contain a
notice indicating that it is posted with permission of the copyright
holder), the work can be copied and distributed to anyone in the
United States without paying any fees or charges. If you are
redistributing or providing access to a work with the phrase “Project
Gutenberg” associated with or appearing on the work, you must
comply either with the requirements of paragraphs 1.E.1 through
1.E.7 or obtain permission for the use of the work and the Project
Gutenberg™ trademark as set forth in paragraphs 1.E.8 or 1.E.9.
1.E.3. If an individual Project Gutenberg™ electronic work is posted
with the permission of the copyright holder, your use and distribution
must comply with both paragraphs 1.E.1 through 1.E.7 and any
additional terms imposed by the copyright holder. Additional terms
will be linked to the Project Gutenberg™ License for all works posted
with the permission of the copyright holder found at the beginning
of this work.
1.E.4. Do not unlink or detach or remove the full Project
Gutenberg™ License terms from this work, or any files containing a
part of this work or any other work associated with Project
Gutenberg™.
1.E.5. Do not copy, display, perform, distribute or redistribute this
electronic work, or any part of this electronic work, without
prominently displaying the sentence set forth in paragraph 1.E.1
72.
with active linksor immediate access to the full terms of the Project
Gutenberg™ License.
1.E.6. You may convert to and distribute this work in any binary,
compressed, marked up, nonproprietary or proprietary form,
including any word processing or hypertext form. However, if you
provide access to or distribute copies of a Project Gutenberg™ work
in a format other than “Plain Vanilla ASCII” or other format used in
the official version posted on the official Project Gutenberg™ website
(www.gutenberg.org), you must, at no additional cost, fee or
expense to the user, provide a copy, a means of exporting a copy, or
a means of obtaining a copy upon request, of the work in its original
“Plain Vanilla ASCII” or other form. Any alternate format must
include the full Project Gutenberg™ License as specified in
paragraph 1.E.1.
1.E.7. Do not charge a fee for access to, viewing, displaying,
performing, copying or distributing any Project Gutenberg™ works
unless you comply with paragraph 1.E.8 or 1.E.9.
1.E.8. You may charge a reasonable fee for copies of or providing
access to or distributing Project Gutenberg™ electronic works
provided that:
• You pay a royalty fee of 20% of the gross profits you derive
from the use of Project Gutenberg™ works calculated using the
method you already use to calculate your applicable taxes. The
fee is owed to the owner of the Project Gutenberg™ trademark,
but he has agreed to donate royalties under this paragraph to
the Project Gutenberg Literary Archive Foundation. Royalty
payments must be paid within 60 days following each date on
which you prepare (or are legally required to prepare) your
periodic tax returns. Royalty payments should be clearly marked
as such and sent to the Project Gutenberg Literary Archive
Foundation at the address specified in Section 4, “Information
73.
about donations tothe Project Gutenberg Literary Archive
Foundation.”
• You provide a full refund of any money paid by a user who
notifies you in writing (or by e-mail) within 30 days of receipt
that s/he does not agree to the terms of the full Project
Gutenberg™ License. You must require such a user to return or
destroy all copies of the works possessed in a physical medium
and discontinue all use of and all access to other copies of
Project Gutenberg™ works.
• You provide, in accordance with paragraph 1.F.3, a full refund of
any money paid for a work or a replacement copy, if a defect in
the electronic work is discovered and reported to you within 90
days of receipt of the work.
• You comply with all other terms of this agreement for free
distribution of Project Gutenberg™ works.
1.E.9. If you wish to charge a fee or distribute a Project Gutenberg™
electronic work or group of works on different terms than are set
forth in this agreement, you must obtain permission in writing from
the Project Gutenberg Literary Archive Foundation, the manager of
the Project Gutenberg™ trademark. Contact the Foundation as set
forth in Section 3 below.
1.F.
1.F.1. Project Gutenberg volunteers and employees expend
considerable effort to identify, do copyright research on, transcribe
and proofread works not protected by U.S. copyright law in creating
the Project Gutenberg™ collection. Despite these efforts, Project
Gutenberg™ electronic works, and the medium on which they may
be stored, may contain “Defects,” such as, but not limited to,
incomplete, inaccurate or corrupt data, transcription errors, a
copyright or other intellectual property infringement, a defective or
74.
damaged disk orother medium, a computer virus, or computer
codes that damage or cannot be read by your equipment.
1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for
the “Right of Replacement or Refund” described in paragraph 1.F.3,
the Project Gutenberg Literary Archive Foundation, the owner of the
Project Gutenberg™ trademark, and any other party distributing a
Project Gutenberg™ electronic work under this agreement, disclaim
all liability to you for damages, costs and expenses, including legal
fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR
NEGLIGENCE, STRICT LIABILITY, BREACH OF WARRANTY OR
BREACH OF CONTRACT EXCEPT THOSE PROVIDED IN PARAGRAPH
1.F.3. YOU AGREE THAT THE FOUNDATION, THE TRADEMARK
OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL
NOT BE LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT,
CONSEQUENTIAL, PUNITIVE OR INCIDENTAL DAMAGES EVEN IF
YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH DAMAGE.
1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you
discover a defect in this electronic work within 90 days of receiving
it, you can receive a refund of the money (if any) you paid for it by
sending a written explanation to the person you received the work
from. If you received the work on a physical medium, you must
return the medium with your written explanation. The person or
entity that provided you with the defective work may elect to provide
a replacement copy in lieu of a refund. If you received the work
electronically, the person or entity providing it to you may choose to
give you a second opportunity to receive the work electronically in
lieu of a refund. If the second copy is also defective, you may
demand a refund in writing without further opportunities to fix the
problem.
1.F.4. Except for the limited right of replacement or refund set forth
in paragraph 1.F.3, this work is provided to you ‘AS-IS’, WITH NO
OTHER WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED,
75.
INCLUDING BUT NOTLIMITED TO WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR ANY PURPOSE.
1.F.5. Some states do not allow disclaimers of certain implied
warranties or the exclusion or limitation of certain types of damages.
If any disclaimer or limitation set forth in this agreement violates the
law of the state applicable to this agreement, the agreement shall be
interpreted to make the maximum disclaimer or limitation permitted
by the applicable state law. The invalidity or unenforceability of any
provision of this agreement shall not void the remaining provisions.
1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation,
the trademark owner, any agent or employee of the Foundation,
anyone providing copies of Project Gutenberg™ electronic works in
accordance with this agreement, and any volunteers associated with
the production, promotion and distribution of Project Gutenberg™
electronic works, harmless from all liability, costs and expenses,
including legal fees, that arise directly or indirectly from any of the
following which you do or cause to occur: (a) distribution of this or
any Project Gutenberg™ work, (b) alteration, modification, or
additions or deletions to any Project Gutenberg™ work, and (c) any
Defect you cause.
Section 2. Information about the Mission
of Project Gutenberg™
Project Gutenberg™ is synonymous with the free distribution of
electronic works in formats readable by the widest variety of
computers including obsolete, old, middle-aged and new computers.
It exists because of the efforts of hundreds of volunteers and
donations from people in all walks of life.
Volunteers and financial support to provide volunteers with the
assistance they need are critical to reaching Project Gutenberg™’s
goals and ensuring that the Project Gutenberg™ collection will
76.
remain freely availablefor generations to come. In 2001, the Project
Gutenberg Literary Archive Foundation was created to provide a
secure and permanent future for Project Gutenberg™ and future
generations. To learn more about the Project Gutenberg Literary
Archive Foundation and how your efforts and donations can help,
see Sections 3 and 4 and the Foundation information page at
www.gutenberg.org.
Section 3. Information about the Project
Gutenberg Literary Archive Foundation
The Project Gutenberg Literary Archive Foundation is a non-profit
501(c)(3) educational corporation organized under the laws of the
state of Mississippi and granted tax exempt status by the Internal
Revenue Service. The Foundation’s EIN or federal tax identification
number is 64-6221541. Contributions to the Project Gutenberg
Literary Archive Foundation are tax deductible to the full extent
permitted by U.S. federal laws and your state’s laws.
The Foundation’s business office is located at 809 North 1500 West,
Salt Lake City, UT 84116, (801) 596-1887. Email contact links and up
to date contact information can be found at the Foundation’s website
and official page at www.gutenberg.org/contact
Section 4. Information about Donations to
the Project Gutenberg Literary Archive
Foundation
Project Gutenberg™ depends upon and cannot survive without
widespread public support and donations to carry out its mission of
increasing the number of public domain and licensed works that can
be freely distributed in machine-readable form accessible by the
widest array of equipment including outdated equipment. Many
77.
small donations ($1to $5,000) are particularly important to
maintaining tax exempt status with the IRS.
The Foundation is committed to complying with the laws regulating
charities and charitable donations in all 50 states of the United
States. Compliance requirements are not uniform and it takes a
considerable effort, much paperwork and many fees to meet and
keep up with these requirements. We do not solicit donations in
locations where we have not received written confirmation of
compliance. To SEND DONATIONS or determine the status of
compliance for any particular state visit www.gutenberg.org/donate.
While we cannot and do not solicit contributions from states where
we have not met the solicitation requirements, we know of no
prohibition against accepting unsolicited donations from donors in
such states who approach us with offers to donate.
International donations are gratefully accepted, but we cannot make
any statements concerning tax treatment of donations received from
outside the United States. U.S. laws alone swamp our small staff.
Please check the Project Gutenberg web pages for current donation
methods and addresses. Donations are accepted in a number of
other ways including checks, online payments and credit card
donations. To donate, please visit: www.gutenberg.org/donate.
Section 5. General Information About
Project Gutenberg™ electronic works
Professor Michael S. Hart was the originator of the Project
Gutenberg™ concept of a library of electronic works that could be
freely shared with anyone. For forty years, he produced and
distributed Project Gutenberg™ eBooks with only a loose network of
volunteer support.
78.
Project Gutenberg™ eBooksare often created from several printed
editions, all of which are confirmed as not protected by copyright in
the U.S. unless a copyright notice is included. Thus, we do not
necessarily keep eBooks in compliance with any particular paper
edition.
Most people start at our website which has the main PG search
facility: www.gutenberg.org.
This website includes information about Project Gutenberg™,
including how to make donations to the Project Gutenberg Literary
Archive Foundation, how to help produce our new eBooks, and how
to subscribe to our email newsletter to hear about new eBooks.
79.
Welcome to ourwebsite – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.
More than just a book-buying platform, we strive to be a bridge
connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.
Join us on a journey of knowledge exploration, passion nurturing, and
personal growth every day!
ebookbell.com