0% found this document useful (0 votes)
27 views54 pages

67589508

The document provides information about various ebooks available for download, including 'Design Patterns for High-Quality Automated Tests' by Anton Angelov and other titles related to automated testing and best practices. It outlines the contents of the featured ebook, which covers high-quality test attributes, strategies for optimizing tests, and various design patterns for enhancing test maintainability and reusability. Additionally, it includes links to purchase or download the ebooks instantly in different formats.

Uploaded by

etnikbrowes
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views54 pages

67589508

The document provides information about various ebooks available for download, including 'Design Patterns for High-Quality Automated Tests' by Anton Angelov and other titles related to automated testing and best practices. It outlines the contents of the featured ebook, which covers high-quality test attributes, strategies for optimizing tests, and various design patterns for enhancing test maintainability and reusability. Additionally, it includes links to purchase or download the ebooks instantly in different formats.

Uploaded by

etnikbrowes
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 54

Download the Full Ebook and Access More Features - ebooknice.

com

(Ebook) Design Patterns for High-Quality Automated


Tests: High-Quality Test Attributes and Best
Practices by Unknown ISBN 9798715639523,
8715639525, B08XWFM6V8
https://ebooknice.com/product/design-patterns-for-high-
quality-automated-tests-high-quality-test-attributes-and-
best-practices-33794754

OR CLICK HERE

DOWLOAD EBOOK

Download more ebook instantly today at https://ebooknice.com


Instant digital products (PDF, ePub, MOBI) ready for you
Download now and discover formats that fit your needs...

Start reading on any device today!

(Ebook) Design Patterns for High-Quality Automated Tests:


High-Quality Test Attributes and Best Practices by Anton
Angelov
https://ebooknice.com/product/design-patterns-for-high-quality-
automated-tests-high-quality-test-attributes-and-best-
practices-33794750
ebooknice.com

(Ebook) Selenium design patterns and best practices build


a powerful, stable, and automated test suite using
Selenium WebDriver by Kovalenko, Dima ISBN 9781783982707,
9781783982714, 1783982705, 1783982713
https://ebooknice.com/product/selenium-design-patterns-and-best-
practices-build-a-powerful-stable-and-automated-test-suite-using-
selenium-webdriver-11860656
ebooknice.com

(Ebook) Biota Grow 2C gather 2C cook by Loucas, Jason;


Viles, James ISBN 9781459699816, 9781743365571,
9781925268492, 1459699815, 1743365578, 1925268497
https://ebooknice.com/product/biota-grow-2c-gather-2c-cook-6661374

ebooknice.com

(Ebook) Meta-algorithmics : patterns for robust, low cost,


high quality systems by Simske, Steven J ISBN
9781118343364, 9781118626696, 9781118626702,
9781118626726, 1118343360, 1118626699, 1118626702,
https://ebooknice.com/product/meta-algorithmics-patterns-for-robust-
1118626729
low-cost-high-quality-systems-5428854

ebooknice.com
(Ebook) High-Quality, High-Volume Spay and Neuter and
Other Shelter Surgeries by Sara White (editor) ISBN
9781118517208, 1118517202
https://ebooknice.com/product/high-quality-high-volume-spay-and-
neuter-and-other-shelter-surgeries-11363604

ebooknice.com

(Ebook) The Understanding by Design Guide to Creating


High-Quality Units by Grant Wiggins, Jay McTighe ISBN
9781416611493, 1416611495
https://ebooknice.com/product/the-understanding-by-design-guide-to-
creating-high-quality-units-42300862

ebooknice.com

(Ebook) JUNOS High Availability: Best Practices for High


Network Uptime by James Sonderegger, Orin Blomberg, Kieran
Milne, Senad Palislamovic ISBN 9780596523046, 0596523041
https://ebooknice.com/product/junos-high-availability-best-practices-
for-high-network-uptime-4681878

ebooknice.com

(Ebook) Best Management Practices for Forestry: Protecting


Maine's Water Quality by Morten Moesswilde, Maine Forest
Service
https://ebooknice.com/product/best-management-practices-for-forestry-
protecting-maine-s-water-quality-52333634

ebooknice.com

(Ebook) Reading for Life: High Quality Literacy


Instruction for All by Lyn Stone ISBN 9781138590908,
9781138590922, 1138590908, 1138590924
https://ebooknice.com/product/reading-for-life-high-quality-literacy-
instruction-for-all-34774830

ebooknice.com
Design Patterns
For High-Quality
Automated Tests
C# Edition
High-Quality Test Attributes
and Best Practices

Anton Angelov
Automate The Planet
Table of Contents
Credits
About the Author
About the Reviewers
Acknowledgements
Foreword
Preface
What this book covers
Chapter 1. Defining High-Quality Test Attributes
Chapter 2. Optimizing and Refactoring Legacy Flaky Tests
Chapter 3. Strategies for Speeding-up the Tests
Chapter 4. Test Readability
Chapter 5. Enhancing the Test Maintainability and Reusability
Chapter 6. API Usability
Chapter 7. Building Extensibility in Your Test Library
Chapter 8. Assessment System for Tests’ Architecture Design
Chapter 9. Benchmarking for Assessing Automated Test Components
Performance
Chapter 10. Test Data Preparation and Configuring Test Environments
Appendix 1. Defining the Primary Problems that Test Automation Frameworks
Solve
Appendix 2. Most Exhaustive CSS Selectors Cheat Sheet
Appendix 3. Most Exhaustive XPath Selectors Cheat Sheet
Who Is This Book For?
Before You Get Started
Conventions
Reader feedback
Errata
Piracy
Questions
Chapter 1. Defining High-Quality Test Attributes
Different Types of Tests
Unit Tests
Integration Tests
System Tests
What Is a Test Automation Framework?
What Is a Software Library?
What Is a Framework?
Test Automation Framework
Is Selenium WebDriver a Test Framework?
SOLID Principles
SRP – Single Responsibility Principle
OCP – Open/Closed Principle
LSP – Liskov Substitution Principle
ISP – Interface Segregation Principle
DIP – Dependency Inversion Principle
High-Quality Test Attributes
What Is a Design Pattern?
Test Maintainability and Reusability
Test Readability
API Usability
Extensibility
Learning Curve
Summary
Questions
Further Reading
Chapter 2. Optimizing and Refactoring Legacy Flaky Tests
Writing the First Real-World Automated Test
First Automated Test Case Explanation
Second Automated Test Case Explanation
Third Automated Test Case Explanation
First Automated Test Code
Second Automated Test Code
Third Automated Test Code
Reasons for Failures
Refactoring Tests
Implicit VS Explicit Waits
Implementing Explicit Waits in Tests
DRY- Do Not Repeat Yourself Principle
Decorator Design Pattern for Fixing WebDriver Unstable Actions
Decorator Design Pattern
Decorator Design Pattern Implementation for IWebElement
Decorator Design Pattern Implementation for IWebDriver
Decorator Design Pattern in Tests
Test Independence- Isolation Principle
Refactoring Tests to Follow Test Independence- Isolation Principle
Summary
Questions
Further Reading
Chapter 3. Strategies for Speeding-up the Tests
Instrumenting the Test Code to Find Possible Points for Optimization
Optimize Login
How to Wait for Asynchronous Requests to Finish in Tests?
Waiting for All AJAX Requests Completion
Optimize Browser Initialization- Observer Design Pattern
Observer Design Pattern
Observer Design Pattern Implementation
Configure Browser Behavior via Attribute
Isolated Browser Initialization for Each Test
Black Hole Proxy Approach
Implementing the Black Hole Proxy
Summary
Questions
Further Reading
Chapter 4. Test Readability
Page Object Model Design Pattern
Create First Page Object Model
New Way for Creating Page Objects
Page Object Model Usage in Tests
Handling Common Page Elements and Actions
Defining Common Page Elements
Non-DRY Page Objects
First Version of Reusing Common Elements
Creating Common Page Section Page Objects
Page Sections Usage in Page Objects - Version One
Page Sections Usage in Page Objects - Version Two
Page Sections Usage in Tests
High Quality Code - Use Meaningful Names
General Naming Guidelines
Naming Classes
Naming New Versions of Existing API
Naming the Methods - The Right Way
General Naming Guidelines
Using Meaningful Method Names
Best Practices for Method Parameters
Follow Coding Standards - Tools
Enforcing Coding Standards Using EditorConfig
Summary
Questions
Further Reading
Chapter 5. Enhancing the Test Maintainability and
Reusability
Navigatable Page Objects- Template Method Design Pattern
Non-refactored Version Page Objects
Create Separate Base Classes for Navigatable and Non-navigatable Pages
Template Method Design Pattern
Template Method Design Pattern Implementation
Using Composition Principle
Non-refactored Version Page Objects without Composition
Elements and Assertions - Composition Implementation
Using Composition in Tests
Reuse Test Workflows - Facade Design Pattern
Test Workflows Naive Implementation
Facade Design Pattern
Facade Design Pattern Implementation
Combining Facade with Template Method Design Pattern
Purchase Facade with Template Methods
Concrete Facade Implementation
Summary
Questions
Further Reading
Chapter 6. API Usability
Interface Segregation principle for WebDriver Decorator
WebDriver Decorator Current Implementation
Splitting Driver Interface into Smaller Interfaces
Smaller Interfaces Usage in Page Objects
Use Page Objects Through Singleton Design Pattern
Singleton Design Pattern
Singleton Design Pattern Implementation
Thread-safe Singleton Implementation
Singleton Design Pattern Usage in Tests
App Design Pattern for Creating Page Objects
App Design Pattern Implementation
App Usage in Tests
Fluent API Page Objects
Fluent API Implementation
Using Fluent API in Tests
Page Objects Elements Access Styles
Exposing Elements Through Properties
Accessing Elements Through Elements Public Property
Hiding Elements in Tests
Hiding Element Unnecessary Details
Summary
Questions
Further Reading
Chapter 7. Building Extensibility in Your Test Library
Building Extensibility for Finding Elements through Strategy Design
Pattern
Vanilla WebDriver Finding of Elements
Strategy Design Pattern
Reviewing Current Version for Finding Elements
Creating Elements Find Strategies
Refactoring IElementFindService
Improving Elements Find Strategies API Usability
Building Extensibility for Waiting for Elements through Strategy
Design Pattern
Vanilla WebDriver Waiting for Elements
Reviewing Current Version of Elements Waiting
Creating Elements Wait Strategies
Creating IElementWaitService
Adding Waiting for Elements to ElementFinderService
Adding Extensibility Points through EventFiringWebDriver
Vanilla EventFiringWebDriver Example
Integrating EventFiringWebDriver
Summary
Questions
Further Reading
Chapter 8. Assessment System for Test Architecture Design
Assessment System Introduction
What Problem Are We Trying to Solve?
Criteria Definitions
1. Readability
2. Maintainability
3. Reusability
4. API Usability
5. Extensibility
6. Code Complexity Index
7. Learning Curve
8. KISS*
Steps to Apply
Assessment System Usage Examples
Tests without Page Objects Assessment
Architecture Design Overview
1. Readability
2. Maintainability
3. Reusability
4. API Usability
5. Extensibility
6. Code Complexity Index
7. Learning Curve
Test Design Index
Tests with Page Objects Assessment
Architecture Design Overview
1. Readability
2. Maintainability
3. Reusability
4. API Usability
5. Extensibility
6. Code Complexity Index
7. Learning Curve
Test Design Index
Tests with Facades Assessment
Architecture Design Overview
1. Readability
2. Maintainability
3. Reusability
4. API Usability
5. Extensibility
6. Code Complexity Index
7. Learning Curve
Test Design Index
Final Assessment
Summary
Questions
1. ​Can you list the seven criteria of the assessment system?
2. ​What aspects of the code do you evaluate for the
Reusability criterion?
3. ​Why is the learning curve a critical evaluation point?
4. ​What is the Test Design Index?
5. ​How do you calculate code metrics in Visual Studio IDE?
6. ​What steps do you need to follow to apply the
assessment system in your project?
Further Reading
Chapter 9. Benchmarking for Assessing Automated Test
Components Performance
What Is Benchmarking?
Benchmarking Your .NET Code with BenchmarkDotNet
Main Features
BenchmarkDotNet Example
Benchmark Button Click Solutions
Button Benchmark Experiment
Benchmark Reports, Profiling, Disassembly Diagnoser
Benchmark HTML and CSV Reports
Benchmark Disassembler
Benchmark Profiling
Optimized Browser Initialization Benchmark Integration
Updates in Observer Classes
Summary
Questions
Further Reading
Chapter 10. Test Data Preparation and Test Environments
Stop Hard-coding Input Data
Problematic Test Data
Configuration Transformations
Creating URL Settings
Creating WebDriver Timeouts Settings
Creating Default Billing Info Settings
Environmental Variables
Introducing Test Fixtures
Using AutoFixture for Generating Data
Using MSTest Data Driven Tests
Using an API as a Source of Fixture Data
Using Data Stubs
Building Test Data Reset/Creating App Use Case
Creating DB Access Layer
Generic Repository Design Pattern
Generic Repository Design Pattern in C#
Creating Users Factory
Using an API or DB for Verification
Summary
Questions
Further Reading
Appendix 1. Defining the Primary Problems that Test
Automation Frameworks Solve
Sub Problem 1 - Repetition and People Boredom, Less Reliable
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 2 - Engineers Are Not So Good with Numbers, Accuracy
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 3 - Time for Feedback- Release
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 4 - Regression Issues Prevention
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 5 - Skipping Part of the Scope
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 6 - Ping-pong
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 7 - Money, Time, Effort for Maintaining Test Artifacts
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 8 - Various App Configurations
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 9 - Low QA Team Morale
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 10 - Questioned Professionalism
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 11 - Provide Evidence What You Did
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 12 - Holidays and Sickness, 24 Hours
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 13 - Some Things Cannot be Tested Manually
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 14 – Consistency
Automation Solution Hypothesis
Subsequent Problems
Sub-Problem 15 - Faster Scale-up and Scale-down
Automation Solution Hypothesis
Subsequent Problems
Summary
Further Reading
Appendix 2. Most Exhaustive CSS Selectors Cheat Sheet
Element Selectors
Contextual Selectors
Attribute Selectors
Useful n Values
Pseudo-class Selectors that Apply to Siblings
Pseudo-class Selectors for Link and User States
Further Reading
Appendix 3. Most Exhaustive XPath Selectors Cheat Sheet
Contextual Selectors
Attribute Selectors
XPath Methods
Axis Navigation
Math Methods
String Methods
Further Reading
Bibliography
Credits
Copyright @ 2020 Automate The Planet
All rights reserved. No part of this book may be reproduced, stored
in a retrieval system, or transmitted in any form or by any means,
without the prior written permission of the author, except in the case
of brief quotations embedded in critical articles or reviews. Every
effort has been made in the preparation of this book to ensure the
accuracy of the information presented. However, the information
contained in this book is sold without warranty, either express or
implied. Neither the author, nor Automate The Planet, and its dealers
and distributors will be held liable for any damages caused or
alleged to be caused directly or indirectly by this book.
The author has endeavored to provide trademark information about
all the companies and products mentioned in this book by the
appropriate use of capitals. However, the author cannot guarantee
the accuracy of this information.
First published: February 2020
Production reference: Published by Automate The Planet Ltd.
Address: https://www.automatetheplanet.com/
Book cover design and formatting by Martin Kolev
About the Author
Anton Angelov is CTO and Co-founder of Automate The Planet,
inventor of BELLATRIX Test Automation Framework, and MEISSA
Distributed Test Runner. Anton has 10 years of experience in the
field of automated testing. He designs and writes scalable test
automation solutions and tools. He consults and trains companies
regarding their automated testing efforts. Part of his job is to lead a
team of passionate engineers helping companies succeed with their
test automation using the company's BELLATRIX tooling. He is most
famous for his blogging at Automate The Planet and many given
conference talks.
About the Reviewers
Ventsislav Ivanov is a Software Engineer in Tests at Innovative
Lab and Automate The Planet. He starts his automation career from
his hobby - browser games. For the last 6 years in the IT industry,
he has achieved a lot like QA Engineer, Developer, and Lecturer. He
has more than 1000 hours like a lecturer in various disciplines - C#
Fundamentals, C# OOP, C# Web, Java OOP, High-Quality Code, and
of course, Quality Assurance. He is a proud creator and lead of the
main Quality Assurance course in one of the best Bulgarian's
academies, where more than 1000+ people certified like QA
engineers and more than 500+ like automation engineers. He has
specialized mainly in web automated testing. He has been
responsible for building the entire QA process in his past company,
from teaching developers unit testing to designing the whole product
Continuous Integration. His motto is: "Instead of feeling
embarrassed about your “bad” code or proud of your “good” code,
you should judge your code by how well it succeeds at achieving
your goals."
A few words for the book:
Amazing amount of design patterns and best practices in so little
space. I don't know how to describe who this book is for. If you are
an experienced automation engineer who wants to improve his/her
company's test automation framework, this book is a must-read. If
you are a beginner in automation, this book can be an excellent start
because of the ladder which you need to climb until reading it.
Nikolay Avramov is currently a Developer in Test at Innovative Lab
and Automate The Planet. For the past 8 years, he worked as a
Quality Assurance Engineer in Telerik (later acquired by Progress
software). He started his career in the field from ground zero- from
taking part of the Telerik Academy to becoming a Principal QA
Engineer/Ninja that is in the heart of any innovation or automation
initiative. Through his work he has developed, and integrated
multiple custom frameworks and tools used across the entire
company. To name a few- a custom Load and Performance Testing
Framework; System Testing Framework; Web Service Testing
Framework; Visual Verification Framework; and many other
productivity tools that assured the highest quality of all web and
business assets of the company. He found his true passion in
Automation while looking for a ‘weapon’ to fight the boring, non-
productive daily tasks that a QA faces. By reducing the manual
repeatable jobs, he believes that we can make the world a better
place, full of happy and productive people that are ready to tackle
the next challenging task, instead of the next pile of test scenarios
to cover by hand. He is a passionate technology geek and
automation specialist that strives to implement optimizations and
achieve high-quality standards in any aspect of life.
A few words for the book:
The book will pretty much give you the building blocks you need and
will take you through the natural process of building a fully fletched
test automation framework within a few hour’s read. The beginning
and the end of the book shows the difference between the rookie
QA enthusiast and the automation ninja. This knowledge will give
you the power to make educated decisions for common problems in
test automation and will give you the right arguments to defend
them in front of any manager or developer.
Acknowledgements
The content of the book is a result of the accumulated knowledge of
many great engineers and IT professionals. Many good programmers
tend to be very egocentric, believing that they are always right and
know everything. Often, they forget how they got their knowledge.
It is a result not only of our efforts but a mixture of personal
effort/work, luck, living, and working environment. I believe that in
this phase of my life, I know enough "tips and tricks" so that I can
write a book. However, this wouldn't be possible without the help of
all the people that took some part of my life. The example and hard
work of my parents, high school, and university teachers. Later, the
many mentors and colleagues I had in the past companies I worked
for. I am a person who is a natural learner and regularly reads new
books and watches video courses/lectures. I wouldn't be as good as
I am now without the valuable information in these materials.
As some of you know, for the past several years, I am regularly
writing articles on my website- Automate The Planet. The book
accumulates a big part of the knowledge I tried to share during the
years. But the site wouldn't be what it is without your support! So, I
would like to say, "Thank You!" to all fans and readers. The same is
valid for all the talks I gave to many international DEV and QA
conferences. Thank you for being there, hearing and trying to apply
the ideas I talk about.
Most importantly, I want to say BIG THANK YOU to my colleagues
Nikolay Avramov and Ventsislav Ivanov for agreeing to help me with
the technical and editorial tasks. Without their help and support, the
quality of the book wouldn't be the same!
My partner in crime at Automate The Planet Martin Kolev is
responsible for all visual enhancements, the marketing, and many
other tedious tasks around the project.
Thanks to all my friends that were there for me and hearing me
talking geek stuff about the book.
Foreword
Since I usually skip the Foreword chapters of other books, I will try
to be short. My core belief is that to achieve high-quality test
automation that brings value- you need to understand core
programming concepts such as SOLID and the usage of design
patterns. After you master them, the usual career transition is into
more architecture roles, such as choosing the best possible
approaches for solving particular test automation challenges. This is
the essence of the book. No more “Hello world” examples but
some serious literature about test automation practices!
Preface
Design Patterns for High-Quality Automated Tests will help you write
better tests!

What this book covers


Chapter 1. Defining High-Quality Test
Attributes
I think many terms are misunderstood and engineers are using them
without fully understanding them, such as a library, framework, test
framework. I believe this is the basic knowledge that all test
engineers should have. The reader will learn about the top-quality
attributes each test library should strive to have, which we will
discuss in much more detail in the next chapters. Moreover, since we
want to treat the test code as production one, we will talk about
SOLID principles and how we can incorporate them into the
development of the tests.
Chapter 2. Optimizing and Refactoring Legacy
Flaky Tests
We will discuss the Hermetic test pattern where each test should be
isolated from others. Will learn about the Adapter design pattern
where some of the unstable behaviours of WebDriver will be
wrapped in a class and fixed. The same pattern will be used to
improve the WebDriver API for locating elements and making it
easier to use. Finally, we will talk about random run order principle
where the tests should be able to run no matter their order.
Chapter 3. Strategies for Speeding-up the
Tests
After the tests are stabilized and always passing the next step is to
improve their speed. One of the approaches will be login to a
website through cookies instead of using the UI. Next, the readers
will see how to reuse the WebDriver browser instead of restarting it
all the time earning more than 40% decrease in test execution time.
We will talk about how to handle asynchronous requests. And last
but not least we will mention the “Black Hole Proxy” approach
isolating 3rd party services’ requests, while further improving the
speed of the automated tests.
Chapter 4. Test Readability
Learn how to hide nitty-gritty low-level WebDriver API details in the
so-called page objects, making the tests much more readable. Also,
the readers will see how to create two different types of page
objects depending on their needs. In the second part of the chapter,
we will talk about coding standards - naming the variables and
methods right, as well as placing the correct comments. At the end
of the section, we will discuss various tools that can help us to
enforce all these standards.
Chapter 5. Enhancing the Test Maintainability
and Reusability
We will talk about how to reuse more code across page objects by
using the Template Method design pattern. Also, we will see a 3rd
type of page object model where the assertions and elements will be
used as properties instead of coming from base classes, which will
introduce the benefits of the composition over the inheritance
principle. In the second part of the chapter, we will discuss how to
reuse common test workflows through the Facade design pattern. At
the end of the section, we will talk about an enhanced version of the
pattern where we can test different versions of the same web page
(new and old).
Chapter 6. API Usability
In this chapter, we will learn how to make the test library API easy
to use, learn, and understand. First, we will talk about different
approaches on how to use already developed page object models
through the Singleton design pattern or Factory design pattern. After
that, we will look at another approach called Fluent API or Chaining
Methods. At the end of the section, we will discuss whether it is a
good idea to expose the page objects elements to the users of your
test library.
Chapter 7. Building Extensibility in Your Test
Library
If you create a well-designed library, most probably other teams can
start using it too, so you need to be sure that your library is easily
extensible. It should allow everyone to modify it and add new
features to it without causing you to spend tons of time rewriting
existing logic or making already written tests to fail. In this chapter,
the reader will learn how to improve extensibility for finding
elements by creating custom selectors through the Strategy design
pattern. After that, we will investigate ways on how we can add
additional behaviors to existing WebDriver actions via Observer
design pattern or built-in EventFiringWebDriver.

Chapter 8. Assessment System for Tests’


Architecture Design
In this chapter, we will look into an assessment system that can help
you decide which design solution is better- for example, to choose
one between 5 different versions of page objects. We will talk about
the various criteria of the system and why they are essential. In the
second part of the section, we will use the system to evaluate some
of the design patterns we used previously and assign them ratings.

Chapter 9. Benchmarking for Assessing


Automated Test Components Performance
The evaluation of core quality attributes is not enough to finally
decide which implementation is better or not. The test execution
time should be a key component too. In this chapter, we will
examine a library that can help us measure the performance of our
automated tests’ components.
Chapter 10. Test Data Preparation and
Configuring Test Environments
One of the essential parts of each automated test is the test data
which we use in it. It is important that the data is relevant and
accessible. In the chapter, we will discuss how we can create such
data through fixtures, APIs, DB layers or custom tools. Also, we will
review how to set up the right way the environment in which the
tests run.

Appendix 1. Defining the Primary Problems


that Test Automation Frameworks Solve
In the first appendix chapter, we will define the problems that the
automation framework is trying to solve. To determine what is
needed to deliver high-quality software, we need to understand
what the issues are in the first place.

Appendix 2. Most Exhaustive CSS Selectors


Cheat Sheet
A big part of the job of writing maintainable and stable web
automation is related to finding the proper element's selectors. Here
will look into a comprehensive list of CSS selectors.

Appendix 3. Most Exhaustive XPath Selectors


Cheat Sheet
The other types of very useful locators are the XPath ones. Knowing
them in detail can help you significantly improve the stability and the
readability of your tests.

Who Is This Book For?


The book is not a getting started guide. If you don't have any prior
programming experience in writing automated tests through
WebDriver, this book won't be very useful to you. I believe it might
be invaluable for the readers that have a couple of years of
experience and whose job is to create/maintain test automation
frameworks, or to write high-quality reliable automated tests.
The book is written in C#. However, I believe that you can use the
approaches and practices in every OOP language. If you have a Java
background, you will get everything you need, don't worry.
Even if you don't get all the concepts from the first read, try to use
and incorporate some of them, later you can return and reread
them. I believe with the accumulation of experience using high-
quality practices- you will become a hard-core test automation ninja!

Before You Get Started


You need to have prior experience in OOP programming language
such as C#, Java, etc. I believe that you can get the book's ideas
just from reading the presented code. However, it is recommended
to download and run all of the solutions on your machine. To do so,
clone the book GitHub
repository- https://github.com/AutomateThePlanet/Design-Patterns-
for-High-Quality-Automated-Tests-CSharp-Edition
To be able to build and execute the code, you will need a C# IDE,
such as Visual Studio or Visual Studio Code. Also, you will need to
install .NET Core 3.1+.

Conventions
In this book, you will find several 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 are shown as follows: IDialogService
A block of code is set as follows:
[IterationCleanup()]
public void IterationCleanup()
{
BenchmarkCleanup(() =>
{
// execute custom setup code here
});
}

Important words or code snippets are shown in bold .


NOTE

Definition

Reader feedback
Feedback from the readers is always welcome. Let me know what
you think about the book, what you liked or disliked. To do so, email
me at [email protected]

Errata
Although I have taken every care to ensure the accuracy of the
content mistakes do happen. If you find a mistake in the book,
whatever in the text or in the code- I would be grateful if you would
report it. If you find any errata, please email them to
[email protected]

Piracy
Piracy of copyright material on the Internet is an ongoing problem
across all media. If you come across any illegal copies of the work,
in any form, on the Internet, please provide me with the location
address or website name immediately so that I can pursue a
remedy.
I appreciate your help in protecting the ability to bring you valuable
content!

Questions
You can contact me at [email protected] if you are
having any problems with any aspect of the book, and I will do my
best to address it.
Also, there is an exclusive forum for the book, where you can create
discussions or ask questions. Feel free to submit the errata there.
Navigate to- https://www.automatetheplanet.com/forums/book-
desing-patterns-csharp/
Chapter 1. Defining High-
Quality Test Attributes
This book is not only about teaching you how to use some design
patterns or practices but furthermore to solve particular test
automation problems. Some of them are flaky tests that are hard to
maintain, read, or learn to write new tests, and the list goes on. The
goal of the book is to help you write more stable and faster tests.
With the many practices and design patterns presented in the book,
you will be able to write automated tests that need less time to
figure out what went wrong, to be easier to support new
requirements, ease the new team members to learn to use your
code and much more.
At the beginning of the chapter, we will go through a short list of the
types of tests. Then, I will define what exactly is a test automation
framework and some other related terms. To be able to write high-
quality automated tests, more knowledge is needed than just
knowing how to program in a certain language or use a specific
framework. To solve these problems, our automated tests should
have some core high-quality test attributes. Also, we will talk about
related programming principles abbreviated SOLID.
The following topics will be covered in this chapter:
Different Types of Tests
What Is a Test Automation Framework? Definitions
SOLID Principles
High-Quality Test Attributes

Different Types of Tests


In short, I will define the different types of tests. This will help us
understand where the WebDriver automated tests belong in the
development cycle.

Unit Tests
These are, by definition, the smallest test units. This type of test is
written to test an individual object and even individual methods of
an object. Unit testing is highly necessary because it prevents bugs
from creeping in at the lowest level. These tests rarely use any real
test data and often solely rely on generated data.

Integration Tests
They consist of several modules of code put together, allowing them
to pass data between each other. The purpose of these types of
tests is to make sure that all modules integrate and work with each
other. They are also known as component integration tests or
integration tests in the small . Usually, in this kind of testing, we
still isolate third-party services such as network, file system, and OS.
Another related term is integration test in the large , which
usually engineers use to mention integration tests that don't isolate
3rd party dependencies.

System Tests
System Testing is the testing of a complete and fully integrated
software product. It is the highest level of testing. It executes
against production (PROD) or production-like environment, such as
staging (STA). Like integration tests, the system test also tries to
verify that all the components, including third-party services, can
communicate well with each other.
These tests can verify our software's functional or non-functional
requirements. Some engineers refer to these tests as End-to-end
tests, UI tests, User Scenario tests. However, I believe there is a
slight difference between the terms.
Our WebDriver automated tests fall into the system testing category.
If all actions and assertions are performed against the user interface
(UI), they should be called UI tests . End-to-end (user scenario)
test would be called a test that is verifying a real User scenario,
from the beginning to the end of the User interaction with the
system under test. No shortcuts or mocks are used - the test mimics
human interaction entirely. In the case of system tests, we are
speaking of highly optimized tests that are focusing on verifying a
particular functionality of the system. For example- instead of
logging in each time through the UI or registering new users, we can
use internal APIs or DB directly to do so. The same is valid for the
performed assertions. If you use these low levels, then it is
inappropriate to call these types of tests- UI tests.

What Is a Test Automation


Framework?
During many conversations at conferences, I realized that many
developers have a wrong conception about what is a test automation
framework. Here, I will try to give you a definition, but before that,
we need to go through some related terms such as what is a
software library, a framework, API, test framework, and others. You
need to understand the latter to get what the test automation
framework is.

What Is a Software Library?


Before we can define what a software library is, we need to go over
what an application is.
The first conclusion we can make after this definition is that our
automated tests are also applications. Over the years, there have
been many discussions over the internet and at conferences whether
test engineers should be able to program rather than using UI tools
for automated testing. I firmly believe that the new age test
automation QA should be a good programmer, treating his/her code
as production code - following high-quality code standards.

Many engineers believe that in their companies, they have built


custom test frameworks. But in fact, they have libraries or groups of
methods and classes that can help them to write automated tests.
The API of the library defines how the users will access its methods,
what arguments they need to pass and so on. It is a convention of
how the library is accessed and used, while the actual
implementation is handled in the library itself. The API is a
specification of what we can use whereas the software library is the
actual implementation of this specification. It should follow the
prescribed rules. We can have multiple implementations of the same
API. For example, Java and Scala implement the Java API. Both are
later compiled to bytecode, which enables Java developers to use
the same methods. The same is valid for the .NET world where C#
and VB.NET are different languages, but both implement the .NET
API and compile to MSIL. As .NET developers, we have the same
methods and types in both programming languages.

What Is a Framework?
Software frameworks have these distinctive features
that separate them from libraries:

Inversion of control - the framework controls the flow, and it


cannot be modified as in the case of standard software libraries.
Default behavior - the software framework provides useful default
actions.
Extensibility - as a user you can customize and modify some of its
actions to fit your needs which usually cannot be done for software
libraries.
Non-modifiable framework code - you can customize or tweak
some of the actions of the framework, but you cannot change its
code.
Examples include ASP.NET and .NET for Windows development,
Cocoa for Mac OS X, Cocoa Touch for iOS, and the Android
Application Framework for Android.
As said in the definition, unit testing frameworks help us to write
more quickly tests for verifying the functionality of our methods and
classes. Moreover, we can execute these tests in an automated way
so that we don't need to repeat them manually over and over again.
They are usually well integrated into most popular programming
IDEs. We can run our tests from there plus quickly review the
results.
Examples for unit testing frameworks are NUnit, MSTest, JUnit,
TestNG.

Test Automation Framework


Now, let’s finally understand what the test automation framework is.

Is Selenium WebDriver a Test


Framework?
Many QAs refer to Selenium WebDriver as a test automation
framework, but they cannot be more wrong. Here is the official
definition on their website.
“Selenium automates browsers. That's it! What you do with that
power is entirely up to you.”
Primarily, it is for automating web applications for testing purposes,
but is certainly not limited to just that. Boring web-based
administration tasks can (and should!) be automated as well.”
Based on the definition, we can conclude that Selenium
WebDriver is mostly known as the standard for web automation
testing, but actually, it is a tool for controlling browsers that we later
use in our software libraries and frameworks to write automated
tests.

SOLID Principles
Before we can define the high-quality test attributes, we should
mention some of the well- known object-oriented programming
principles. Throughout the book I will mention some of them. SOLID
is a mnemonic acronym for five design principles. The goal of these
principles is to help us in writing more understandable, flexible, and
maintainable software. SOLID stands for:
SRP – Single Responsibility Principle
OCP – Open/Closed Principle
LSP – Liskov Substitution Principle
ISP – Interface Segregation Principle
DIP – Dependency Inversion Principle
SRP – Single Responsibility Principle

The principles states- "Every software module should have only one
reason to change " which means that every class, method, etc. can
do many things, but they should serve a single purpose.
All the methods and variables in the class should support this
purpose. Everything else should be removed. It shouldn't be like a
swiss knife providing 20 different utility actions since if one of them
is changed, all others need to be updated too.
But if we can have each of those items separated it would be simple,
easy to maintain, and one change will not affect the others. The
same principle also applies to classes and objects in the software
architecture- you can have them as separate simpler classes.
Let me give you an example .
public class CustomerOrder
{
public void Create()
{
try
{
// Database code goes here
}
catch (Exception ex)
{
File.WriteAllText(@"C:\\exception.txt", ex.ToString());
}
}
}

The CustomerOrder class is doing something which it is not supposed to


do. It should create purchases and save them in the database, but if
you look at the catch block closely, you will see that it also does log
activity. It has too many responsibilities.
If we want to follow the Single Responsibility principle SRP , we
should divide the class into two separate simple classes. We can
move the logging to a separate class.
public class FileLogger
{
public void CreateLogEntry(string error)
{
File.WriteAllText(@"C:\\error.txt", error);
}
}
After that the CustomerOrder class can delegate the logging to the
FileLogger class and be more focused on the creating purchases.
public class CustomerOrder
{
private FileLogger _fileLogger = new FileLogger();

public void Create()


{
try
{
// Database code goes here
}
catch (Exception ex)
{
_fileLogger.CreateLogEntry(ex.Message);
}
}
}

OCP – Open/Closed Principle


The principle says that "software entities (classes, modules,
functions, etc.) should be open for extension but closed for
modification ". It means that, if new requirements are written for the
already implemented functionality, these can be added in a way so
that we don't need to change the whole structure of the existing
code that has been already unit tested. Since if we change it and
add it directly, we can trigger new regression problems.
How about looking at an example?
public enum OrderType
{
Normal,
Silver,
}

public class DiscountCalculator


{
public double CalculateDiscount(OrderType orderType, double totalPrice)
{
if (orderType == OrderType.Silver)
{
return totalPrice - 20;
}
else
{
return totalPrice;
}
}
}

Look at the IF condition in the CalculateDiscount method. The problem is


that, if we add new order types, we will have to add one more IF
condition, meaning that we will have to change the implementation
inside the DiscountCalculator class, because of a change that has
happened outside of it. If we need to change the class every second
week, we need to ensure that the previous requirements are still
satisfied, and the new ones integrate well with the old ones. Instead
of modifying the existing code for every new condition, we strive to
develop a solution that can be extensible.
We can easily refactor this code to follow the Open/Closed
principle OCP , so every time a new order type is added, we create
a new class, as shown in the example. The existing code stays
untouched, and we need to test and check only the new cases.
public class DiscountCalculator
{
public virtual double CalculateDiscount(double totalPrice)
{
return totalPrice;
}
}

public class SilverDiscountCalculator : DiscountCalculator


{
public override double CalculateDiscount(double totalPrice)
{
return base.CalculateDiscount(totalPrice) - 20;
}
}

public class GoldDiscountCalculator : DiscountCalculator


{
public override double CalculateDiscount(double totalPrice)
{
return base.CalculateDiscount(totalPrice) - 50;
}
}
LSP – Liskov Substitution Principle
The principle says that "you should be able to use any derived class
instead of a parent class and have it behaved in the same manner
without modification ". It means that the child class shouldn't modify
or change how the base class behaves. The main point is that the
child classes can be used as if they are their parent itself, but if you
change behavior in a child class, unexpected issues will occur.
Shall we continue with the same DiscountCalculator example? We want
to introduce a new type of discount for bonus points. To calculate it
we will add a new method to our base class.
public abstract class DiscountCalculator
{
public virtual double CalculateRegularDiscount(double totalPrice)
{
return totalPrice;
}

public virtual double CalculateBonusPointsDiscount(double totalPrice, int points)


{
return totalPrice - points * 0.1;
}
}

As you can see, we have renamed the CalculateDiscount method to


CalculateRegularDiscount and we added a new method for calculating the
discounted total price based on the bonus points.
public class SilverDiscountCalculator : DiscountCalculator
{
public override double CalculateRegularDiscount(double totalPrice)
{
return base.CalculateRegularDiscount(totalPrice) - 20;
}

public override double CalculateBonusPointsDiscount(double totalPrice, int points)


{
return totalPrice - points * 0.5;
}
}
public class GoldDiscountCalculator : DiscountCalculator
{
public override double CalculateRegularDiscount(double totalPrice)
{
return base.CalculateRegularDiscount(totalPrice) - 50;
}

public override double CalculateBonusPointsDiscount(double totalPrice, int points)


{
return totalPrice - points * 1;
}
}
public class PlatinumDiscountCalculator : DiscountCalculator
{
public override double CalculateRegularDiscount(double totalPrice)
{
return base.CalculateRegularDiscount(totalPrice) - 100;
}

public override double CalculateBonusPointsDiscount(double totalPrice, int points)


{
throw new InvalidOperationException("Not applicable for Platinum orders.");
}
}

What we did here is to add a new type of order- Platinum. We have


already applied the maximum allowed discount, so the bonus points
discount is not applicable for Platinum orders. In this case, we
decide to throw InvalidOperationException to let the calling methods know
that this operation is not supported for the Platinum type. As the
Polymorphism from the OOP Principles states, we can use any of the
child classes calculators derived from the DiscountCalculator , as if they
are the actual DiscountCalculator class.
Thanks to this principle, as you can see the code below, I have
created a collection of DiscountCalculator objects where I can add Silver,
Gold, and Platinum Discount Calculators as if they are instances of
the same type - DiscountCalculator . After that, I can go through the list
using the parent customer object and invoke the calculation
methods.
var _discountCalculators = new List<DiscountCalculator>();
_discountCalculators.Add(new SilverDiscountCalculator());
_discountCalculators.Add(new GoldDiscountCalculator());
_discountCalculators.Add(new PlatinumDiscountCalculator());
foreach (DiscountCalculator discountCalculator in _discountCalculators)
{
double bonusPointsDiscount = discountCalculator.CalculateBonusPointsDiscount(1250);
}
So far so good, but when the CalculateBonusPointsDiscount of the
PlatinumDiscountCalculator is invoked, it leads to InvalidOperationException . The
problem is that the PlatinumDiscountCalculator object looks like a
DiscountCalculator , but the implementation of the child object has
changed the expected behavior of the parent method. So, to follow
the Liskov principle , we need to create two interfaces, one for the
regular and other for the bonus points discount.
public interface IRegularDiscountCalculator
{
double CalculateRegularDiscount(double totalPrice);
}

public interface IBonusPointsDiscountCalculator


{
double CalculateBonusPointsDiscount(double totalPrice, int points);
}

I will spare you all the refactoring that we need to do. Let's see how
we will use the code to fix the problem we faced.
var _discountCalculators = new List<IBonusPointsDiscountCalculator>();
_discountCalculators.Add(new SilverDiscountCalculator());
_discountCalculators.Add(new GoldDiscountCalculator());
// _discountCalculators.Add(new PlatinumDiscountCalculator()); // we cannot add it

foreach (IRegularDiscountCalculator discountCalculator in _discountCalculators)


{
double bonusPointsDiscount = discountCalculator.CalculateBonusPointsDiscount(1250);
}

Instead of using the base class, we use the IBonusPointsDiscountCalculator


interface. This means that all added objects to the collection will
have this method implemented otherwise we won't be able to add it
to the list.

ISP – Interface Segregation Principle


The principle states that "clients should not be forced to implement
interfaces they don't use. Instead of one fat interface, many small
interfaces are preferred based on groups of methods, each one
serving one sub-module" . The Single Responsibility principle
can be applied not only for classes but for the interfaces as well.
Each interface should provide methods that serve a single purpose.
In this case the interface gives us functions for more than one goal,
this could lead our implementation code to include methods that are
not needed in the class.
Why not see an example of why we shouldn't use one fat interface?
We can use our previous example for discount calculators. Instead of
creating two separate interfaces we could easily create a single one.
To simplify the code, let’s remove the inheritance.
public interface IDiscountCalculator
{
double CalculateRegularDiscount(double totalPrice);
double CalculateBonusPointsDiscount(double totalPrice, int points);
}

public class GoldDiscountCalculator : IDiscountCalculator


{
public double CalculateRegularDiscount(double totalPrice)
{
return totalPrice - 50;
}

public double CalculateBonusPointsDiscount(double totalPrice, int points)


{
return totalPrice - points * 1;
}
}

public class PlatinumDiscountCalculator : IDiscountCalculator


{
public double CalculateRegularDiscount(double totalPrice)
{
return totalPrice - 100;
}

public double CalculateBonusPointsDiscount(double totalPrice, int points)


{
throw new NotImplementedException("Not applicable for Platinum orders.");
}
}

As you can see, we have a similar problem. Since we have one fat
interface, we are forced to implement all methods- even when we
may not need them. In the future this could become even worse,
imagine that we need to add another type of discount, but this time
it is not applicable to the Silver orders. This would mean that in the
we will have a new method that throws
SilverDiscountCalculator
NotImplementedException . Better solution would be creating a new
interface rather than updating the current interface.

DIP – Dependency Inversion Principle


The principle states that “high-level classes should not depend on
low-level classes. Both types should be created based on
abstractions. These abstractions shouldn't depend on any low-level
details. All details should use the abstractions instead” . The high-
level classes are usually the ones that contain the business logic,
and the low-level ones consist of actions such as CRUD DB
operations, reading files or calling web APIs. When we say that a
class is tightly coupled it means that it has access to low-level details
(that it shouldn't have) instead of using the abstractions.
How about extending our CustomerOrder example a bit? The
requirements have already changed, and we have different types of
orders to process- silver, gold and platinum. Also, there are new
requirements which state that we need to add two more ways of
notifications in case something happens. For example, sending an
email and SMS with updates. To do so:
We will create a common interface for all loggers called ILogger .
public interface ILogger
{
void CreateLogEntry(string errorMessage);
}

After that we will create three separate loggers- one for file logging,
one for email and one more for SMS.
public class FileLogger : ILogger
{
public void CreateLogEntry(string errorMessage)
{
File.WriteAllText(@"C:\\exceptions.txt", errorMessage);
}
}

public class EmailLogger : ILogger


{
public void CreateLogEntry(string errorMessage)
{
EmailFactory.SendEmail(errorMessage);
}
}

public class SmsLogger : ILogger


{
public void CreateLogEntry(string errorMessage)
{
SmsFactory.SendSms(errorMessage);
}
}

The business logic behind the new requirements is that we need to


send an email for gold orders and an SMS for platinum orders since
they bring our company more money.
public class CustomerOrder
{
public void Create(OrderType orderType)
{
try
{
// Database code goes here
}
catch (Exception ex)
{
switch (orderType)
{
case OrderType.Platinum:
new SmsLogger().CreateLogEntry(ex.Message);
break;
case OrderType.Gold: new EmailLogger().CreateLogEntry(ex.Message);
break;
default:
new FileLogger().CreateLogEntry(ex.Message);
break;
}
}
}
}

The code violates the Single Responsibility principle again. It


should be focused on creating purchases, but also decides which
object to be created, while it is not the work of the CustomerOrder class
to determine which instances of the ILogger should be used. The
biggest problem here is related to the new keyword. This is an extra
responsibility of making the decision which objects to be created, so
if we delegate this responsibility to someone other than the
CustomerOrder class, that will solve the problem.

We can have different child classes of CustomerOrder for the different


types of orders. Also, the logger can be passed as dependency
rather than creating it in the method itself.
public class CustomerOrder
{
private ILogger _logger;

public CustomerOrder(ILogger logger)


{
_logger = logger;
}

public void Create()


{
try
{
// Database code goes here
}
catch (Exception ex)
{
_logger.CreateLogEntry(ex.Message);
}
}
}

public class GoldCustomerOrder : CustomerOrder


{
public GoldCustomerOrder( )
: base(new EmailLogger())
{
}
}

public class PlatinumCustomerOrder : CustomerOrder


{
public PlatinumCustomerOrder()
: base(new SmsLogger())
{
}
}
High-Quality Test Attributes
The time has come to define the automated test high-quality
attributes. You will find that some of these attributes are connected
to the SOLID principles and throughout the book, we will continue to
talk about these connections. Since the book is also about design
patterns, and we haven't discussed them yet, we will first go
through these definitions, and then we will go briefly though each of
the high-quality attributes. We will not go into many details now
since there will be a dedicated chapter for each of the attributes.

What Is a Design Pattern?


We can define the design patterns as prescribed solutions to
everyday software challenges. They don't consist of code or any
specified algorithm, but instead, they describe how to group your
logic smartly, reuse it, or to make it easier to maintain. It is a
template for solving design problems, which we can use while we
create our software solutions.

Test Maintainability and Reusability

Imagine there is a problem in your tests. How much time do you


need to figure out where the problem is? Is it an automation bug or
an issue in the system under test? In the next chapters, we will talk
in detail about how to create maintainable tests using various
practices and design patterns. But if at the beginning, you haven't
designed your code in such a way, the changes may need to be
applied to multiple places which can lead to missing some of them
and thus resulting in more bugs. The better the maintainability is,
the easier it is for us to support our existing code, accommodate
new requirements, or just to fix some bugs.
A closely related principle to this definition is the so-called DRY
principle- Don't Repeat Yourself . The most basic idea behind
the DRY principle is to reduce long-term maintenance costs by
removing all unnecessary duplication.

Test Readability
By reading the code, you should be able to find out what the code
does easily. A code that is not readable usually requires more time to
read, maintain, understand and can increase the chance to introduce
bugs. Some programmers use huge comments instead of writing
more simple readable code. It is much easier to name your
variables, methods, classes correctly, instead of relying on these
comments. Also, as the time goes by, the comments are rarely
updated, and they can mislead the readers.

API Usability
As we mentioned above, the API is the specification of what you can
do with a software library. When we use the term usability together
with the term API, it means "How easy it is for you as a user to find
Discovering Diverse Content Through
Random Scribd Documents
remain freely available for 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
small donations ($1 to $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.
Project Gutenberg™ eBooks are 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.
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade

Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.

Let us accompany you on the journey of exploring knowledge and


personal growth!

ebooknice.com

You might also like