The Angular Tutorial Preview
The Angular Tutorial Preview
Tutorial
Learn Front-End Development and
Automated Testing with Angular
by Adam Morgan
2
The Angular Tutorial
Learn Front-End Development and Automated Testing with
Angular
Adam Morgan
ii
Contents
Preface v
1 Book Methodology 1
1.1 The approach . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Who is this for? . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.1 The wannabe front-end developer with a grasp on the
basics . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.2 The Angular developer who wants to learn automated
testing . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.3 The front-end developer who wants to learn Angular . 3
1.3 What this book is vs. what this book isn’t . . . . . . . . . . . 3
1.4 Why am I writing this book? . . . . . . . . . . . . . . . . . . 4
1.5 What are we building? . . . . . . . . . . . . . . . . . . . . . 4
1.6 Conventions used in this book . . . . . . . . . . . . . . . . . 9
iii
iv CONTENTS
2.3 Node.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4 Angular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.5 The Client-Server model . . . . . . . . . . . . . . . . . . . . 14
5 Introduction to Angular 49
5.1 The approach . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.2 Install the CLI . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.3 Exploring the code . . . . . . . . . . . . . . . . . . . . . . . 54
5.4 Root folder . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.5 Src folder . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.6 AppComponent . . . . . . . . . . . . . . . . . . . . . . . . . 56
5.7 AppModule . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.8 NgModule . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
5.9 Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
5.10 Directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
5.11 Pipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
5.12 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
8 Introduction to Testing 83
8.1 Karma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
8.2 Jasmine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8.3 Unit testing . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
8.4 Integration testing . . . . . . . . . . . . . . . . . . . . . . . . 85
8.5 End-to-end (E2E) testing . . . . . . . . . . . . . . . . . . . . 86
8.6 The testing pyramid . . . . . . . . . . . . . . . . . . . . . . . 87
8.7 Why do we test? . . . . . . . . . . . . . . . . . . . . . . . . . 88
8.8 How to approach testing . . . . . . . . . . . . . . . . . . . . 89
8.9 Testing Adder . . . . . . . . . . . . . . . . . . . . . . . . . . 89
8.10 Test-driven development . . . . . . . . . . . . . . . . . . . . 92
9 User Signup 95
9.1 Auth service . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
CONTENTS vii
12 Navbar 183
12.1 Component setup . . . . . . . . . . . . . . . . . . . . . . . . 183
12.2 Add isLoggedIn to navbar . . . . . . . . . . . . . . . . . . . 187
12.3 Event Emitter . . . . . . . . . . . . . . . . . . . . . . . . . . 188
12.3.1 Add Event Emitter to login . . . . . . . . . . . . . . . 189
12.3.2 Add Event Emitter to logout . . . . . . . . . . . . . . 191
12.4 Subscribe to event in NavbarComponent . . . . . . . . . . . . 192
12.5 Add logout to NavbarComponent . . . . . . . . . . . . . . . . 192
12.6 Navbar test . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
12.6.1 Logged in user . . . . . . . . . . . . . . . . . . . . . 197
12.6.2 Logged out user . . . . . . . . . . . . . . . . . . . . . 200
12.7 Navbar E2E test . . . . . . . . . . . . . . . . . . . . . . . . . 201
12.7.1 Add first test suite . . . . . . . . . . . . . . . . . . . 201
12.7.2 Cypress command . . . . . . . . . . . . . . . . . . . 203
12.7.3 Add second test suite . . . . . . . . . . . . . . . . . . 204
12.8 Update signup E2E test . . . . . . . . . . . . . . . . . . . . . 205
12.9 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
xvii
xviii PREFACE
2
https://mattlewis92.github.io/angular-calendar/#/kitchen-sink
3
https://www.youtube.com/watch?v=aVrerOv73o8
xix
clarify things for you. Take a walk to clear your mind and things may click
when you’re away from your computer when you least expect it to. I hit brick
walls myself while writing this book. How in the hell do I explain this? Just
like coding, I’d have breakthroughs when I wasn’t at my computer writing the
book. I’d be taking a shower and a new approach to explaining a concept would
just come to me. Be patient, but persistent.
Our education system is outdated and alternatives are desparately needed. If
you know how to code, employers don’t care if you don’t have a degree. This
book is my contribution to give people an alternative to the current system.
Writing this book is one of the most difficult things I’ve ever done. I’ve done
my best to provide you the material you need to learn Angular and hopefully
get you started on a path to a career as a software developer. I hope you find it
valuable. Good luck.
If you run into trouble at any point in this book, I’d suggest comparing your
code to the reference code4 for this book that contains the finished code for
each chapter in this book. You can also post questions on Stack Overflow5
using the tag “theangulartutorial”6 .
You’re also welcome to join a Discord community I’ve created just for this
book. Click here7 to join this group.
(If you’re reading a print copy of this book and you’d like a digital copy in the
form of a PDF, email me a copy of your receipt to atom.morgan@gmail.com)
4
https://github.com/theangulartutorial/lets-get-lunch-code-ng8
5
https://stackoverflow.com/
6
https://stackoverflow.com/questions/tagged/theangulartutorial
7
https://discord.gg/xMc7n3Z
xx PREFACE
Chapter 1
Book Methodology
1
2 CHAPTER 1. BOOK METHODOLOGY
This process isn’t at all unlike one you may expect to see at your first job as a
front-end developer where a separate team would be responsible for developing
the API. You’re merely responsible for consuming it.
What are the basics? HTML, CSS, and the fundamentals of JavaScript and/or
TypeScript.
As far as HTML and CSS go, I won’t be taking the time to explain the differ-
ences between a <div> and a <span>, the box model in CSS, or the various
selectors at our disposal. The templates and styling for code will be provided
(using Bootstrap1 which is a popular front-end framework for designing web-
sites) but a detailed explanation of the two will not be provided. However,
any HTML or CSS that’s closely tied to Angular (we’ll see this later when we
tackle forms) will be explained in greater detail.
As for JavaScript and TypeScript, I’m assuming a basic understanding of fun-
damentals such as syntax, variables, data structures (boolean, undefined, Num-
ber, String, and Object), if-else statements, loops, functions, and operators such
as ==, ===, &&, ||, >, <, >=, and so on. If you know JavaScript but have never
used TypeScript, don’t worry. You’ll find the majority of it will be easy to pick
up as we go.
The code provided later will be explained but the basic workings of TypeScript
will not.
1
https://getbootstrap.com/docs/3.3/
1.3. WHAT THIS BOOK IS VS. WHAT THIS BOOK ISN’T 3
for source code management. To host our code that we manage with Git, we’ll
be using GitHub4 . Once again, these are tools I’ve used on most teams as a
developer and ones you can expect to use at some point as well. Our use of
these two within this book will be rather basic and we’ll get into more details
on this later.
Like most applications, we’ll provide users the ability to create accounts where
they can choose a username and password and select a set of preferred “dietary
preferences”.
6 CHAPTER 1. BOOK METHODOLOGY
After creating an account, users can then create “events” specifying a start date,
end date, and location. They’ll be able to view these “events” on a dashboard
which contains a user-friendly calendar to display their events.
1.5. WHAT ARE WE BUILDING? 7
Within these events, users can see a list of people who have “subscribed” to
the event (those who are opting-in to attend the event). We will also provide
a comment section within the event so users can further discuss details of the
event.
1.6. CONVENTIONS USED IN THIS BOOK 9
In addition to comments, we’ll also provide the option to suggest locations for
an event. If selected, a request will be made for a list of nearby restaurants that
match the dietary preferences of the users who have subscribed to the event.
echo hello
In addition to blocks are inline code, such as const. Inline, formatted text such
as const always relates to something within our code (including some error
messages provided to us by Angular). The inline code is a visual aid to dis-
tinguish between plain English and technically related things such as variable
names, file names, error messages, and occasionally a command-line interface
command.
So we may be talking about providing a user the ability to signup for an ac-
count, which is different from a signup function we may be writing. The
differences between the two within the book should be fairly straightforward.
Occassionally, there are times where a long piece of inline, formatted text
breaks from one line to the next. As expected, this division at the end of a
line requires hyphenation. This can get a bit weird with long function names
or file names such as event-view.module.ts that when hyphenated, ends
up with event-view- being one one line and .module.ts being on the next
line. When this occurs in the book just keep in mind that the hyphenation is
there as a part of the formatting process for this book. Thankfully, this isn’t
that common and even when it is there’s typically additional references to the
file or function names around the hyphenated version that should let you know
it’s just a formatting issue.
An example of an additional reference would be the file names found above
code blocks whenever we’re adding code to a file.
Listing 1.1
src/app/some-file.js
function myNewFunction() {
return true;
}
So if a hyphenated file name does throw you off, you can always refer to the
filepath found at the top of the code blocks as shown above.
Chapter 2
2.1 Overview
I want to begin by explaining how web applications work within the context
of the technologies we’ll be using within this book: MongoDB, Node.js, and
Angular.
2.2 MongoDB
MongoDB is an open-source NoSQL (also referred to as non-relational or
document-oriented) database that represents and stores data in JSON-like doc-
uments1 . Relational databases such as Microsoft SQL Server, MySQL and
PostgreSQL represent and store data in tables using rows.
In relational databases, every table has a schema that defines the columns and
data types for every row in the table. In non-relational or document-oriented
databases, there’s no defined schema and every document can be structured
differently. This gives non-relational databases more flexibility with documents
1
https://www.mongodb.com/scale/relational-vs-non-relational-database
11
12 CHAPTER 2. HOW WEB APPLICATIONS WORK
that may be updated to our needs without the need to modify database schemas
to include any new columns and their data types.
This doesn’t mean that non-relational databases are better than relational databases.
Like most engineering decisions, the right tool depends on the job2 . Since
this book focuses on front-end development, we won’t be doing much with
our database other than writing and reading data through the API. But at the
very least, it’s worth knowing the two types of databases (relational and non-
relational) and some of the most common database names you may see when
developers are talking about databases.
2.3 Node.js
Node.js is an open-source, cross-platform run-time environment for executing
JavaScript code server-side. Historically, JavaScript was used for client-side
scripting with scripts embedded in a webpage’s HTML.
Node.js enables JavaScript to be used for server-side scripting with the possi-
bility to produce dynamic web page content before a page is sent to a user’s
browser.
Box 2.1.
In our case, Node.js won’t be producing our web pages. Instead, it will be receiv-
ing and sending data to our Angular application which will generate the web pages
our user sees. We’ll go into more detail on this shortly.
If you’ve heard of languages like Go, Ruby, Python, PHP, Java, C#, and oth-
ers (especially within the context of APIs) Node.js is yet another server-side
scripting language like these.
Along with Node.js is its package manager, npm. npm is bundled with Node.js
and allows developers to use a variety of packages to extend the functionality
of Node.js.
When we setup our API we’ll be using npm to install some of these packages
that were used to build the API your Angular application will be interacting
with.
2.4 Angular
Angular (also referred to as Angular 2, Angular 2+, Angular 5) is a TypeScript-
based open-source front-end framework built by Google. If you’re familiar
with AngularJS, an earlier framework also written by Google, it is a complete
rewrite so the two are in no way related.
Like many other front-end frameworks, Angular provides us with tools to write
modern web applications. This includes a file structure to organize our applica-
tion, a command-line interface to create files for us, modules and components
to group our HTML, CSS, and client-side scripting (TypeScript/JavaScript),
forms for gathering user input, services to interact with 3rd party APIs, routing
for the various pages in our application, and so on.
Some of these features will become more clear later on as we begin to use
them but the primary takeaway is that a framework gives us an easy way to use
our HTML, CSS, and TypeScript knowledge to build web applications without
having to build every feature ourselves.
If you’ve ever played a video game with a built-in level creator, you can think
of frameworks as a level creator for the web. In a video game’s level creator,
there are certain assets available for you to use such as structures, textures,
enemies, items, and so on. You may even be given the option to build a structure
14 CHAPTER 2. HOW WEB APPLICATIONS WORK
from base components to create a new structure that you can re-use and even
share with others. Web frameworks essentially do the same thing. But rather
than providing us textures and structures that exist within the game’s universe,
we’re given the various tools we need to create modern web applications such
as routing, form builders, form validation, and services to abstract many of the
details of making HTTP requests from us. It provides us the basic building
blocks for creating a web application.
Server
• Database (MongoDB)
• Server/API (Node.js)
Client
With that client-server split in mind, let’s visualize how these parts communi-
cate with each other.
2.5. THE CLIENT-SERVER MODEL 15
Let’s begin with a real world example. When I go to my Twitter page, https://-
twitter.com/atommorgan, a request is made to Twitter’s API to find the user
“atommorgan”. Twitter’s API then queries their database to see if a user with
the username “atommorgan” exists. If it does, it sends data (containing infor-
mation such as tweets) back to the API which then returns that data in the form
of JSON to the front-end client. From here, the front-end displays the user’s
profile page and all of their related tweets in the browser. An entire client-server
request-response cycle has been completed.
Because of this client-server split the server and the web in general has much
more flexibility. Rather than providing actual web pages, it can merely provide
data that’s used to construct web pages. With this approach, one server can
power a web-based Twitter as well as iOS and Android versions of Twitter
which both use their own frameworks and languages to create apps.
You may even own a console or smart TV with their own apps with entirely
different UIs from the ones you see on your phone. Once again, a single server
16 CHAPTER 2. HOW WEB APPLICATIONS WORK
can send the exact same data to these various clients where the client can decide
how to visualize that data.
As I mentioned earlier, the server which we’ll refer to as the API going forward,
in the client-server model will be provided for you in this book. Our goal is to
consume that API with an Angular application.
Chapter 3
• Postman
17
18 CHAPTER 3. GETTING STARTED AND INSTALLATION
• Node.js
• nvm
3.4 Terminal
Next we’ll need to get a terminal emulator for all of the command-line work
we’ll be doing.
If you’re on Mac, you have the option of either iTerm23 or Hyper4 . Our needs
throughout this book are rather simplistic so again this is largely a matter of
personal preference. I use iTerm2 myself.
If you’re on Windows, the popular option for you would be Hyper5 .
1
https://code.visualstudio.com/
2
https://www.sublimetext.com/3
3
https://www.iterm2.com/
4
https://hyper.is/
5
https://hyper.is/
3.5. GIT 19
3.5 Git
Next you’ll need to install Git6 . Git is an open source version control system
which allows you to track changes in source code and coordinate working on
these files among a team. Git is what you’ll be using to push your code to
Github7 and clone (or download) existing code repostories that other developers
have created which is the focus of Chapter 4.
Once you’ve installed Git you can verify it was installed by running the follow-
ing command:
git --version
When running through the installation process, ensure “Use Git from the Win-
dows Command Prompt” is selected. All of the other options in the installation
prompt can be left to their default settings.
6
https://git-scm.com/
7
https://github.com/
20 CHAPTER 3. GETTING STARTED AND INSTALLATION
Once Git is installed, you’ll need to edit some preferences within Hyper. First,
open Hyper. Then go to Edit > Preferences. Within the preferences file that
opens you should see a shell, shellArgs, and env property. Update those
values to what’s shown below:
Listing 3.1
.hyper
shell: 'C:\\Program Files\\Git\\git-cmd.exe',
shellArgs: ['--command=usr/bin/bash.exe', '-l', '-i'],
env: {
TERM: 'cygwin'
}
3.6. POSTMAN 21
Restart Hyper and you should now be able to verify Git is working correctly by
running:
git --version
3.6 Postman
The next tool you’ll need is Postman8 . This is a GUI tool we’ll be using to
make requests to our API without the need to write actual code. We’ll get into
the finer details of Postman later.
8
https://www.getpostman.com/
22 CHAPTER 3. GETTING STARTED AND INSTALLATION
3.7 Robo 3T
The last tool you’ll need to download is Robo 3T9 (formerly Robomongo).
This provides us with a GUI for viewing the data we’ll eventually create in our
database.
3.8 Node.js
With all of those tools installed, it’s now time to download and install Node.js.
You can do this by visiting the Node.js website10 and downloading the Node.js
installer which will walk you through the process.
Once you’ve finished the installation process, you can double check that every-
thing has been installed correctly by opening a terminal window and running
the following command:
node -v
npm -v
3.9 nvm
Finally, we’re going to install nvm which is a handy tool to allow you to manage
and use multiple Node.js versions on one computer. If you happen to be work-
ing on multiple projects, each using a different version of Node, nvm will allow
you to easily switch between them reducing any friction for your development
workflow.
3.9.1 Mac
If you’re on a Mac you can install nvm by running the following command.
You can verify nvm was installed by running the following command.
nvm --version
3.9.2 Windows
If you’re on Windows, you can get the latest version of nvm for windows on the
releases page11 for nvm-windows. The file name should be nvm-setup.zip.
Once you’ve finished going through the installer you can verify nvm was in-
stalled by running the following command.
nvm --version
11
https://github.com/coreybutler/nvm-windows/releases
24 CHAPTER 3. GETTING STARTED AND INSTALLATION
Chapter 4
The first step to getting our API setup is to install MongoDB, the database the
API uses. The easiest way to install MongoDB is through Homebrew. You
can visit the Homebrew website1 for more details but the install process is
straightforward. You’ll just need to run this in a terminal window.
/usr/bin/ruby -e \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Once that has completed you can now install MongoDB with Homebrew.
1
https://brew.sh/
25
26 CHAPTER 4. API SETUP AND INSTALLATION
Once you’ve installed MongoDB, you’ll need to create the data directory that
MongoDB will use to write data to.
mkdir -p /data/db
mkdir \data\db
With the data directory created, verify that MongoDB has been installed and
everything is working correctly.
mongod
"C:\Program Files\MongoDB\Server\4.0\bin\mongo.exe"
2
https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/#get-mongodb-community-edition
4.1. INSTALLING MONGODB 27
When you run that you should see a bunch of text logged to your terminal
window.
Near the bottom you should see something like: NETWORK [initandlisten]
waiting for connections on port 27017.
With that, everything is working correctly so you can close your MongoDB
connection by typing Ctrl + C.
28 CHAPTER 4. API SETUP AND INSTALLATION
3
https://github.com
4.3. CREATING A ZOMATO ACCOUNT 29
Your profile URL will be needed in our next step as we setup your Zomato
account to get an API key.
Feel free to leave your GitHub window open since we’ll need it at the end of
this chapter when we add this API to your newly created account.
4
https://github.com/theangulartutorial/lets-get-lunch-code-ng8/tree/master/updated-account-registration-
flows
5
https://www.zomato.com
6
https://developers.zomato.com/api
4.3. CREATING A ZOMATO ACCOUNT 31
From there, run the following command to “clone” the API code to your direc-
tory.
4.5. CREATING CONFIG FILES 33
If you’re following along with the examples, you’re directory structure should
now look like this:
| book-code
| lets-get-lunch-api
Now that we’ve cloned the repository, go ahead and cd into your new API
directory.
cd lets-get-lunch-api
7
https://github.com/theangulartutorial/lets-get-lunch-api
34 CHAPTER 4. API SETUP AND INSTALLATION
Listing 4.1
src/test-config.json
{
"port": 8080,
"bodyLimit": "100kb",
"db": "mongodb://localhost:27017/name-of-db",
"secret": "supersecretkey",
"zomato": "zomatokey"
}
You can leave the port and bodyLimit property as is. Port specifies the port
our API should use when it’s run and bodyLimit specifies the maximum body
size of incoming requests.
The db property will need to be modified slightly. You’ll need to remove
name-of-db and update it to the name you want for your database. Let’s
use lgl-api-test.
The secret property is the secret key that the API will use when generating
JSON Web Tokens (JWT) which our application will be using for authenticat-
ing users, signing new tokens and verifying existing ones. Feel free to add
whatever string here you’d like or to leave it as is.
Box 4.1.
Since we’re just learning, that secret isn’t a huge deal for us right now. But if
we were releasing a product to the world with real users we’d want to keep that
secret, well, a secret. If a malicious third party gained access to that secret key,
they would be able to make requests as if they were someone else. This is why the
secret and the configuration files aren’t a part of the public code repository itself
and must be manually generated.
4.6. TEST CONFIG VS. DEV CONFIG 35
Finally, you’ll want to update zomato to the API key Zomato provided to you
earlier when you created an account. With all of that, your config file should
now look similar to this.
Listing 4.2
src/test-config.json
{
"port": 8080,
"bodyLimit": "100kb",
"db": "mongodb://localhost:27017/lgl-api-test",
"secret": "sup3rs3c123tk3y",
"zomato": "zomato12345"
}
Save that file and open up your other config file, src/dev-config.json.
The values will be almost exactly the same as your src/test-config.json
with one update to the database name.
Listing 4.3
src/dev-config.json
{
"port": 8080,
"bodyLimit": "100kb",
"db": "mongodb://localhost:27017/lgl-api",
"secret": "sup3rs3c123tk3y",
"zomato": "zomato12345"
}
Here we’ve updated the db property be removing the -test that was in our
test-config.json file. Once again, set your API key as the value for the
zomato property.
Since testing is such a heavy emphasis within this book, we’ll be using an
entirely separate database (lgl-api-test) when we run our tests that’s wiped
clean between each test. This will be known as our testing environment. This
will help us verify that various features of our application work at the most
basic level.
Our other database (lgl-api) is the database we’ll be using as a developer
while we’re developing our application. This will be known as our development
environment. Both of these environments are local to our own computer.
The final database which we’ll be setting up much later is what’s known as a
production database. This is what real users who are accessing our application
through the Internet would be using - whether they realize it or not.
This split is one you could expect to see working at any tech company. Some
may have more environments while some may have less. The key takeaway
is that our test and development environments are local to our own computer.
They allow us to use our application without polluting the database that real
users are interacting with.
Then set your current version of Node.js to this newly installed version.
nvm use
npm install
Once that has finished, we’ll need to build the project files compiling all of the
TypeScript files into JavaScript files that Node.js can run. First, you’ll need to
install gulp8 .
Then you can run the gulp command to build the project’s JavaScript files.
gulp
8
https://gulpjs.com/
38 CHAPTER 4. API SETUP AND INSTALLATION
ls -l
dir
A new /built directory should be visible which contains the JavaScript files
necessary for Node.js to run the project.
4.7. RUNNING THE API 39
After a few seconds you should see a ready message similar to this:
Congratulations! You’ve successfully set up the API and have verified that it’s
running.
To give you some perspective, this is a process that has sometimes taken me
days at real companies so don’t sell yourself short. It’s the closest thing to a
real-world, paid version of syllabus week9 .
9
https://www.urbandictionary.com/define.php?term=syllabus%20week
4.8. VERIFYING THE DATABASE 41
Box 4.2.
It’s worth noting that you’ll need to run nvm use every time you’re in the API
directory before running any commands like gulp or npm run api-test unless
you haven’t closed your terminal window since the last time you ran it. Without
setting your Node version using nvm use you may run into some issues since
later versions of Node may have introduced some breaking changes.
If you want to avoid running nvm use every time, you can set 7.6 as your default
Node.js version using nvm by running nvm alias default 7.6.
And if you’re ever curious about what version of Node you’re currently using just
run nvm current.
Now that our API is running, let’s verify that our database has been created.
Open Robo 3T and a window should open with an empty list of connections.
At the top, click “Create”.
Enter a name for your connection, something like angular-book, and verify
the address is set to localhost and the port number next to it is 27017.
42 CHAPTER 4. API SETUP AND INSTALLATION
In the list of connections on the left-hand side you should see a database with
the name you specified earlier in src/test-config.json. If you used the
one in the example you should see lgl-api-test.
Note: Due to recent changes in Robo3T the database name may not appear
yet. That’s okay. In Section 9.2 you’ll learn how to read API documentation
and create a user using Postman. Once that user is in the database you’ll then
be able to verify the database exists.
44 CHAPTER 4. API SETUP AND INSTALLATION
If you’re using Windows and you aren’t seeing the database, that’s expected. For
whatever reason, Robo3T doesn’t show the database in this list until a record has
actually been created in the database which we’ll do in Section 9.2. As long as
MongoDB ran successfully in Section 4.1.3 everything should be fine. We’ll get
back to verifying this later.
Go back to your terminal window that’s running the API and enter Ctrl-C to
stop the API. Now run npm run api-dev to run the API using our develop-
ment environment.
4.8. VERIFYING THE DATABASE 45
Go back to Robo 3T, right click angular-book in the left-hand sidebar, click
“refresh”, and you should see your second development database lgl-api
displayed in the list.
Note: Once again, due to recent changes in Robo3T the database name may
not appear yet. That’s okay. In Section 9.2 you’ll learn how to read API docu-
mentation and create a user using Postman. Once that user is in the database
you’ll then be able to verify the database exists.
46 CHAPTER 4. API SETUP AND INSTALLATION
Now we’re ready to add our API to GitHub. First, go back to the home page of
GitHub and click “Start a project”.
Then you’ll be asked to provide a name for your repository (project). Your
directory name (lets-get-lunch-api) and repository name don’t have to
be the same but it may help so that you can keep the two aligned. Then, click
“Create repository”.
4.9. ADD TO GITHUB 47
Since the API was cloned from an existing repository, you’ll need to update its
remote repository URL. Run the following command:
git remote -v
From there, you can push your API up to your GitHub repository by running:
48 CHAPTER 4. API SETUP AND INSTALLATION
4.10 Conclusion
The API and our two databases have now been setup and we’ve pushed them
to a GitHub repository. We’ve now finished the grunt work of setting up our
backend so it’s time to move on to Angular!
Chapter 5
Introduction to Angular
49
50 CHAPTER 5. INTRODUCTION TO ANGULAR
basic, working application for us right out of the box following Angular’s own
best practices. As a developer, this saves us a lot of setup time.
Install the CLI by running:
cd ..
That should get you back into the book-code directory. From there, generate
a new Angular app by running:
ng new exploring-angular
5.2. INSTALL THE CLI 51
When you run the ng new command you should see some command prompts
asking you “Would you like to add Angular routing?” and “Which stylesheet
format would you like to use?”. Just hit the “Enter” key twice to select the
default values for both prompts (No and CSS).
52 CHAPTER 5. INTRODUCTION TO ANGULAR
Once that has finished, move into your new directory and run the application.
cd exploring-angular
ng serve
Once you see webpack: Compiled successfully you can open a browser
window and direct your browser to http://localhost:4200. You should see an
Angular logo with a few links listed below it.
5.2. INSTALL THE CLI 53
Listing 5.1
| exploring-angular
| e2e
| src
- .editorconfig
- .gitignore
- angular.json
- package-lock.json
5.4. ROOT FOLDER 55
- package.json
- README.md
- tsconfig.json
- tslint.json
While we’re on the subject of styles, styles.css is our first CSS stylesheet.
Open that file and you’ll see a comment at the top mentioning it’s the location
for global styles. If you want a style to apply across your entire application,
this is where it goes. We’ll see more stylesheets shortly.
5.6 AppComponent
Within the src folder you’ll see another folder named app. This is where the
real code for our application lives. The first files we want to look at are all of
the app.component files followed with .css, .html, .spec.ts, and .ts.
The first file, app.component.css, is the stylesheet that’s specific to our
component. At the moment it’s empty. Unlike the styles.css stylesheet we
saw earlier, the styles within app.component.css will only affect this single
component. We’ll see how this relation is made shortly.
Next we have app.component.html. This is the HTML template for our
component. Since this is the only component in our application at the moment,
this template contains all of the HTML we saw when we opened our app in our
browser earlier.
Next is app.component.spec.ts. This is the test file containing the unit
tests for this component. This file may look overwhelming at the moment but
it’s one we’ll become much more familiar with throughout this book.
Finally, we have app.component.ts. Within this file you’ll see an import of
the Component symbol from Angular’s core library at the very top. This is a
part of every Angular component and much of what we see in this file will be
automatically generated for us when we use the CLI to create new components
for us.
Below that you’ll see a @Component decorator function that specifies the meta-
data for this component. The selector property defines the name of the
custom HTML element we’re creating that we use in our HTML to use this
component. Take a glance at index.html again and you’ll see <app-root>-
5.7. APPMODULE 57
</app-root> within the <body> tag. Next we have our templateUrl and
styleUrls. These two correspond to app.component.html and
app.component.css which we just covered. This decorator function is what
glues all of these files together.
Finally, there’s the name of our exported class at the bottom: AppComponent.
In this class is a property title with a value exploring-angular. If you go
back to
app.component.html you’ll notice {{ title }} on line 4. The double
curly braces is what’s known as “interpolation”. Here we’re using it to display
the component property title. Update the value of title in app.component-
.ts to My Angular App, save, and go back to your browser to see your up-
dated value.
These three files (excluding app.component.spec.ts) come together to
create our AppComponent.
5.7 AppModule
So now the question is, how does our application know to use AppComponent?
The mere existence of the files in our project isn’t enough. This is where
AppModule comes in.
Open up app.module.ts and inside you’ll see an import statement for our
AppComponent. If AppComponent was the glue for our html, css, and com-
ponent code, AppModule is what glues our entire application together.
Along with the import for AppComponent you’ll see import statements for
BrowserModule and NgModule. BrowserModule is a module provided to
us by Angular for running our app in a browser.
Box 5.1.
58 CHAPTER 5. INTRODUCTION TO ANGULAR
Modules like BrowserModule are one of the many modules provided to us that
are truly the core of Angular - the “level creator building blocks” for the web. Later
on we’ll be importing modules for forms, routing, and http requests that save us
the time from having to write these features ourselves. These, and their associated
counterparts found in other frameworks, are the individual tools provided to us in
the technical toolbox known as a front-end framework.
The import for NgModule and its @NgModule decorator is similar to @Component
which we saw earlier in app.component.ts.
5.8 NgModule
NgModules, or modules, are a way for our application to organize itself. They
also provide us a way to extend the capabilities of our application to include
external libraries that aren’t provided to us by Angular itself.
NgModules combine the various parts of Angular such as components, direc-
tives, pipes, and services into blocks of functionality. (We’ve already seen
our first component. We’ll get to directives, pipes, and services shortly. The
takeaway here is that NgModules aggregate these various parts into one larger,
related unit known as a module.)
Every Angular app must have at least one module known as the root module
which we see in app.module.ts. This module is “bootstrapped” to launch
our application. Similar to @Component we see metadata for our @NgModule
as well.
The first is declarations. This declares the components, directives, and
pipes that belong to the module. In our case, we’re declaring AppComponent
as our first and only declaration. This is what allows us to view AppComponent
in the browser. Remove this line and our page would turn into a blank screen
along with some helpful errors printed to the console by Angular telling us
5.8. NGMODULE 59
Box 5.2.
If you’d like to see where our exported class AppModule is used, open src/-
main.ts and you’ll see our application’s bootstrapping in action. Near the
end of the file there’s a .bootstrapModule() call and our AppModule is
provided. Don’t worry too much about this file though since we won’t be going
60 CHAPTER 5. INTRODUCTION TO ANGULAR
back to it often. It’s just a small detail to show how AppModule is being used
in one of the files generated for us by the CLI.
5.9 Services
Now that we’ve seen how our AppComponent fits into AppModule it’s time to
go back to the other features of Angular (services, directives, and pipes) which
can, along with components, be aggregated into modules.
In Angular, we use services to manage the data that’s used within an applica-
tion. Examples would include a service to manage users. This service could
provide us a way to create new users, retrieve additional details about a user, the
ability to update details about the user, or even delete the user entirely. These
services can then be used throughout our application within any component or
module that needs to leverage its functionality. Within this book, we’ll be using
services primarily to make HTTP requests to an API to either save, update, or
retrieve data from a database. For now, we’ll start with a simple service that
returns some hard-coded data.
First we’ll use the CLI to generate a service for us.
ng g service ng-features
This creates two new files for us: our service (ng-features.service.ts)
and its associated test file (ng-features.service.spec.ts). Take a look
inside ng-features.service.ts and you’ll see a providedIn property
set to root inside the service’s @Injectable decorator.
Listing 5.2
src/app/ng-features.service.ts
@Injectable({
providedIn: 'root'
})
5.9. SERVICES 61
constructor() { }
}
Listing 5.3
src/app/ng-features.service.ts
constructor() { }
getFeatures() {
return [
{ 'name': 'Components' },
{ 'name': 'Services' },
{ 'name': 'Directives' },
{ 'name': 'Pipes' }
];
}
We now have our first basic service set up. Since its providedIn property is
set to root other application components like AppComponent can use it.
Open up app.component.ts and add an import statement for our service.
Listing 5.4
src/app/app.component.ts
import { NgFeaturesService } from './ng-features.service';
Listing 5.5
src/app/app.component.ts
export class AppComponent {
title = 'My Angular App';
features: Array<object>;
The only thing left is to call our service and set the return value to our local
features property. We could technically make this call within our construc-
tor but it’s not a best practice. Our constructor shouldn’t do anything except
dependency injection.
Instead we’re going to make this call in one of Angular’s lifecycle hooks ngOn-
Init. This is a hook that Angular itself will call, a single time, after it has
constructed our component.
5.10. DIRECTIVES 63
To use this lifecycle hook, we’ll first need to import it. Add OnInit after the
Component import.
Listing 5.6
src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
Listing 5.7
src/app/app.component.ts
export class AppComponent implements OnInit {
...
}
Now we can add the ngOnInit method, call our service’s getFeatures
method, and set its return value to our local features property.
Listing 5.8
src/app/app.component.ts
export class AppComponent implements OnInit {
title = 'My Angular App';
features: Array<object>;
ngOnInit() {
this.features = this.ngFeatures.getFeatures();
}
}
5.10 Directives
Similar to title our features property is ready to be interpolated to our
view. But unlike title which was a simple string features is an array of
64 CHAPTER 5. INTRODUCTION TO ANGULAR
objects. This is where we use our first built-in directive: ngFor. You can see a
complete list of Angular’s built-in directives here3 .
ngFor is what’s known as a structural directive. Structural directives reshape
the DOM’s structure usually adding, removing, or modifying elements. They
can be identified within HTML with a preceded asterisk (*) to the directive’s
name.
At the bottom of app.component.html, we can use this structural directive
for our array of features interpolating the feature name to the view.
Listing 5.9
src/app/app.component.html
<h2>Angular features:</h2>
<ul>
<li *ngFor="let feature of features">{{feature.name}}</li>
</ul>
We’ve created an unordered list and applied the *ngFor directive to an individ-
ual list element. We apply the directive to the element we want to be duplicated.
The let keyword declares a template variable that we reference within this
template. Here we’re looping through the features (declared in our compo-
nent) and setting each element within the array to the feature variable in our
template. We then use our template variable feature to interpolate the name
of our feature using double curly braces.
Check your browser again and you should see a new list at the bottom of the
page containing the features provided to us in our service.
5.11 Pipes
Our last remaining feature is what’s known as a pipe. Pipes are a way to for-
mat strings, currencies, dates, and other display data. Angular comes with a
3
https://angular.io/guide/template-syntax#built-in-directives
5.12. CONCLUSION 65
few built-in pipes and we’ll use our first one here to illustrate the concept. A
complete list of Angular’s built-in pipes can be found here4 .
Within our template where we interpolated the feature name, add a pipe opera-
tor, |, followed by the name of the built-in pipe we want to use, uppercase.
Listing 5.10
src/app/app.component.html
<li *ngFor="let feature of features">{{feature.name | uppercase}}
Go back to your browser window and you should see the feature names listed in
capital letters. The word uppercase to the right of the pipe operator activated
Angular’s UppercasePipe5 .
5.12 Conclusion
In this chapter we saw the structure of an Angular application and the building
blocks of Angular that we use to create components and modules. This is all
we’ll be doing within the exploring-angular application. You can either
leave the project as is if you want it for reference or delete it. If you’d like to
delete the application, first move up a directory.
cd ..
rm -rf exploring-angular
4
https://angular.io/guide/pipes#built-in-pipes
5
https://angular.io/api/common/UpperCasePipe
66 CHAPTER 5. INTRODUCTION TO ANGULAR
Chapter 6
6.1 Refresher
67
68 CHAPTER 6. HOW ANGULAR WORKS
As we start working on the various features throughout this book we’re go-
ing to be creating additional modules referred to as “feature modules”. These
feature modules will contain their own *.module.ts file along with all the
*.html, *.css, and *.component.ts files that come together to create a
component. If the component requires services, directives, or pipes, they will
will be contained in the feature module as well.
These various feature modules come together to create our application. So our
application which is boostrapped by the root module (AppModule), is com-
posed of even more modules (feature modules) that provide the user the various
functionality of our application in a browser.
6.4 Conclusion
We’ve now seen at a high level how our application will be leveraging feature
modules and how modules can also be composed of other modules. If this
seems like inception at the moment, that’s perfectly normal. These concepts
will hopefully start to make more sense once we begin creating some of our
features.
We also briefly introduced the concept of routing and the ability for users to
view multiple pages within our application. Since our application will be lever-
aging many routes, let’s move on to the first part of our real application starting
with implementing the router.
Chapter 7
71
72 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING
Listing 7.1
| book-code
| exploring-angular (unless deleted)
| lets-get-lunch
| lets-get-lunch-api
We added the --routing flag to here which tells Angular to set up a routing
module for us. The scaffolding for our app’s routing has now been created in
src/app/app-routing.module.ts. We’ve also added the --style flag
setting its value to css to specify our stylesheet format. This is simply a way
to set default values for the CLI prompts we saw earlier in Section 5.2 of Chap-
ter 5.
In the file we see a variable routes set to an empty array. This is where we’ll
configure our routes. Below that is an @NgModule decorator which imports the
RouterModule. Here it has a call to .forRoot() with our routes variable
being passed in as an argument. forRoot is what configures our application’s
router using our application’s routes in routes that we define.
Below that is the exports metadata with RouterModule set as its export.
This exposes our routing module so that it can be imported in AppModule.
As a result of generating our app with the --routing flag you can see that
our AppRoutingModule has already been imported and added to imports in
AppModule.
7.2 HomeComponent
With our routing module set up it’s time to set up our first route and direct it to
our home component. First, change directories into the new lets-get-lunch
directory.
7.2. HOMECOMPONENT 73
cd lets-get-lunch
ng g component home
Listing 7.2
src/app/home/home.component.html
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="jumbotron">
<h1>Let's Get Lunch</h1>
<p>Coordinate and find local lunch spots with your coworkers.</p>
</div>
</div>
</div>
</div>
With our HomeComponent created and our template file updated, we can now
configure this component within our router.
In app-routing.module.ts first import our component and then add our
new route object to the routes array.
Listing 7.3
src/app/app-routing.module.ts
import { HomeComponent } from './home/home.component';
Here we’ve specified an empty path since we want our home page to display
at the root path of our application. We then set its component property to our
74 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING
imported HomeComponent. This tells the router, “When a user is viewing the
root path of our application, use this component.” Run ng serve and you
should see our markup at the bottom of the page.
Box 7.1.
If you want the CLI to automatically open a browser window for you rather than
navigating to http://localhost:4200 manually you can run ng serve -o.
But how did our application know where to inject our component’s template?
Open app.component.html and at the bottom you’ll see a new directive
7.2. HOMECOMPONENT 75
Listing 7.4
src/app/app.component.html
<router-outlet></router-outlet>
Take another look at your browser and you should see the home page containing
only the markup that’s contained in home/home.component.html.
76 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING
There’s an issue here because none of the Bootstrap styles we’re using in the
template are working. Let’s install and configure Bootstrap to fix this.
Now we need to configure the Angular CLI so that it can use Bootstrap when
it’s serving our application. In angular.json you should see a "styles"
property set to an array containing "styles.css". Add a path to Bootstrap’s
CSS just above it.
Listing 7.5
angular.json
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"styles.css"
]
Below that should be a "scripts" property set to an empty array. Add a path
to jQuery and Bootstrap’s JS.
Listing 7.6
angular.json
"scripts": [
"node_modules/jquery/dist/jquery.min.js",
"node_modules/bootstrap/dist/js/bootstrap.min.js"
]
To get these changes to take effect, we’ll need to restart the app so Angular’s
CLI can use our latest updates to its configuration. If your application is already
running, type Ctrl + C to stop the application. Then run it again.
ng serve -o
Now you should see our styled home page using our very first component and
our very first route.
78 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING
Box 7.2.
It’s fine to leave your existing terminal window with ng serve running. As you
make changes to your app ng serve and ng test will both automatically update
to reflect your latest changes and the two of these running at the same time don’t
conflict with one another.
7.4. UPDATING THE APPCOMPONENT TEST 79
The one scenario where you may run into issues is when you create new files (such
as components, services, or modules) using the CLI. After running commands like
these it’s worth restarting ng serve and ng test to ensure they’re picking up
the latest files that have been added.
The result of ng test should log errors stating a broken test with a message
like, "AppComponent should render title in a h1 tag FAILED".
This test is failing because we removed the HTML code that was previously
80 CHAPTER 7. HOME PAGE AND AN INTRODUCTION TO ROUTING
Listing 7.7
src/app/app.component.spec.ts
// Delete these
it(`should have as title 'lets-get-lunch'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('lets-get-lunch');
}));
Listing 7.8
src/app/app.component.ts
export class AppComponent { }
Now all of your tests should be passing with a message like, "Executed 2
of 2 SUCCESS".
7.5. ADD TO GITHUB 81
git add .
git commit -m "Add home component and routing"
7.6 Conclusion
In this chapter we set up the Angular project that we’ll be building on for the
remainder of this book. We created our first component, HomeComponent, and
configured its routing which displayed its view using the <router-outlet>
directive in AppComponent, our application’s shell. We then updated config-
uration files to include Bootstrap and jQuery so that our template in Home-
Component was styled correctly.
In the next chapter, we’ll introduce some concepts and the tools we’ll be using
for automated testing.
Chapter 8
Introduction to Testing
Before we get into creating and testing our features it’s worth taking some time
to discuss the tools and languages we’ll be using to test our code along with
the different types of tests. We’ll wrap up with basic examples that show us the
thought process to keep in mind while coding which is one of the most difficult
aspects for people to pick up when they first begin testing.
8.1 Karma
Karma is a tool that’s a direct product of the Angular team. It’s a tool that’s
used entirely for running our application’s test code. When we ran the ng
test command earlier, the Angular CLI used Karma to run our test in what’s
known as a “headless browser” which is a browser without a graphical user
interface (GUI).
The primary advantage of a headless browser when testing code is performance
since headless browsers avoid draw operations which handle rendering of the
UI on the screen. Just like a regular browser, a headless browser still under-
stands HTML, CSS, and JavaScript. As a result, a headless browser allows
our tests to run in a browser environment faster than it would in a traditional
browser while still providing us the ability to verify our application is behaving
83
84 CHAPTER 8. INTRODUCTION TO TESTING
as we expect it to.
Aside from the occasional configuration you won’t be interacting directly with
Karma other than through the ng test command.
8.2 Jasmine
Jasmine is a behavior-driven development framework created for testing JavaScript.
Jasmine is included within Angular app’s so it’s the testing framework we’ll be
using within this book.
A few other frameworks you may hear thrown around that are near equivalents
to Jasmine would be Mocha, AVA, and Jest.
function capitalize(word) {
return word.charAt(0).toUpperCase() + word.slice(1);
}
Here we would use a “unit test” to verify that when our capitalize function is
called with a string that the return value is indeed the exact same string with the
first letter capitalized. We don’t check to see if the word is actually capitalized
by opening a browser. We’re testing the code, in isolation, as its own unit.
8.4. INTEGRATION TESTING 85
Integration tests are a bit more difficult to explain and the term “integration test-
ing” is often used interchangeably with end-to-end testing which we’ll cover
next. Even within the testing section1 of the Angular documentation, the term
“integration testing” is briefly mentioned but rarely addressed afterwards.
While unit tests are for individual units of code at the smallest level, integration
tests are tests that integrate with some other parts of our application. In Angular
applications, these tests are also written using the Jasmine framework which are
run by Karma when we use the ng test command provided by the Angular
CLI.
Using our previous example with a function that capitalized a word, in a unit
test we would test this function in isolation passing it a string to verify its
expected behavior. In an integration test, we would test the integration of this
function and the other parts of our application. We may have a function that first
makes a request to an API and once it receives a response from the API, then
calls the capitalize function to capitalize certain words which are displayed
to the user. An integration test would test all of these various parts together.
Within Angular applications, the distinction between unit tests and integration
tests becomes even blurrier considering the two types of tests are typically writ-
ten within the exact same file. The key distinction to remember here is a unit
test is isolated from other parts of our application while integration tests inte-
grate with other parts of our application. If this seems a bit confusing at the
moment, don’t worry. We’ll get into more of the details later once we begin
creating features and writing tests.
1
https://angular.io/guide/testing#testing
86 CHAPTER 8. INTRODUCTION TO TESTING
Box 8.1.
If you’re familiar at all with the Angular world (AngularJS or Angular) you may
have heard of another tool Protractor which is also maintained by the Angular
team. We touched on this earlier in Section 5.4 when we were looking at the
directories created for us by the Angular CLI (the e2e directory that Protractor
uses).
2
https://www.cypress.io/
8.6. THE TESTING PYRAMID 87
Protractor is a tool similar to Cypress. They’re both used for end-to-end tests.
However, after much use I’ve found Cypress to be far easier to use and much more
reliable for end-to-end tests. End-to-end testing in general can be very finnicky
and in my experience Cypress reduces a lot of that.
As shown in the figure above, as you get higher in the pyramid the fewer tests
you have for the respective level. This is due to two factors: speed and relia-
bility. Unit tests provide us our fastest and most reliable tests while end-to-end
88 CHAPTER 8. INTRODUCTION TO TESTING
are the slowest and most prone to failure. While end-to-end tests are the most
brittle of the three, it isn’t entirely due to the testing framework that’s being
used for end-to-end testing. There are various quirks across browsers, anima-
tions, timing issues, and many others that can lead to end-to-end tests failing
even if the feature may be functional.
As a result of this trade-off between speed and reliability we have metaphors
like the “Testing Pyramid” to give us an idea of how our tests should be dis-
tributed between the three types of tests. Some developers attempt to quantify
the distribution of these tests as percentages but I think this is a bit much. I find
it best to keep the testing pyramid in mind and use what’s best suited for your
application and your needs.
Within the application we’ll be building in this book the large majority of our
tests will be unit and integration tests with a smaller subset of end-to-end tests.
element out of the process including all of its flaws but also have a source to
refer to that states how various parts of our application should behave.
be running these tests, we just want to see what tests look like at a basic level.
We’ll get to running tests in the next chapter once we start testing within An-
gular.
When we use Jasmine to test our code we group tests together with what Jas-
mine refers to as a “test suite”3 .
describe('Adder', () => {
// Tests go here
});
We begin our test suite by calling Jasmine’s describe function. This function
takes two parameters: a string and a function. The string serves as a title and
the function contains the code that implements our tests.
Within this describe (or test suite) is where we add our “specs”. Specs look
similar to test suites since they also take a string and a function as arguments
but rather than call describe we call it instead.
describe('Adder', () => {
it('should be able to add two whole numbers', () => {
// Test expectations go here
});
});
Just like our describe we’ve provide it with a brief summary of our test.
It helps to be descriptive here because the titles in our describe and it
combined read like a sentence telling us how our code should behave: “Adder
should be able to add two whole numbers”.
Within this it block is where we state our test’s expectations.
describe('Adder', () => {
it('should be able to add two whole numbers', () => {
expect(Adder.add(2, 2)).toEqual(4);
});
});
3
https://jasmine.github.io/2.4/introduction.html#section-Standalone_Distribution
8.9. TESTING ADDER 91
Our first test uses Jasmine’s expect method which we’ve provided with what
is referred to as an “actual”4 . This “actual” is the value, as in the actual value,
that we’re testing. In this case we’ve assumed Adder is an object with an add
method passing it two numbers.
Following our expect is what is known as a “matcher”5 function. This is
one of many matchers included with Jasmine but the one we’ve used here is
toEqual. We pass our matcher function, toEqual, the expected value we
expect to receive from Adder.add(2, 2). Let’s add one more test.
describe('Adder', () => {
it('should be able to add two whole numbers', () => {
expect(Adder.add(2, 2)).toEqual(4);
});
4
https://jasmine.github.io/2.4/introduction.html#section-Expectations
5
https://jasmine.github.io/2.4/introduction.html#section-Matchers
92 CHAPTER 8. INTRODUCTION TO TESTING
The implementation of add here may be bit crazy (so much for Adder being a
super-intelligent alien) but it would still return the sum of two numbers. When
we test code, the expected value is our only concern. A common mistake is to
test the implementation of code.
Within this book we’re going to take a reasonable approach with TDD. We’ll
be using TDD when it’s convenient and makes sense but we’re not going to be
religious about it. At the very least I want you to at least be familiar with the
practice and what it is since it’s well known within software development and
likely to be an interview topic even if there isn’t a single developer within the
company who practices TDD themselves.