- In this section, we're going to start looking
at how to test Angular applications.
There's quite a wide range of different tests you can write
but the process is basically the same in each case.
So what we'll do in this example is we'll just see
a very simple class
and how to run it, using the Jasmine tool set.
So, as we've mentioned previously,
when you use Angular CLI
as well as creating your template application
it also sets up an environment for testing.
So we use Jasmine for testing in Angular.
Jasmine is very similar to Jest,
so we shouldn't see many surprises there.
And Angular uses a tool called Karma to run the tests.
Karma is a test runner.
Okay?
It executes the tests that we've implemented in Jasmine.
Jasmine is the syntax for writing tests.
Karma is the tool to run those tests.
So I've got an example application.
If you go into lesson 11
Example2/HelloWorld
that's the application that we're going to be dissecting
and add in a class and testing it.
So here it is.
I'm going to pick out the content of the package.
json file, first of all,
to show you what libraries are there for testing.
Oh, by the way, if you want to use this example yourself,
you'll need to install the libraries.
I deleted node modules in my zip file.
So, if you wanna follow along, you need to reinstall them.
So, go to a command window, lesson 11, example 2.
Go into the Hello World demo application
and do an npm install.
It'll take a few moments.
And once you've done that, you'll then have the libraries
in node modules and you are only ready to go.
Right.
So let's take a look at package json.
This was generated for me by Angular CLI.
It contains the libraries and the tools
that I need for testing purposes.
Okay?
So in the dev dependency section
these are the relevant parts.
I'll show you the actual code.
That would be quite useful.
So here's my package json.
You remember with node,
you have dependencies libraries
that you need when your application's running,
and that's in the dependency section.
Okay?
So these are the packages or the libraries
that my application needs at run time.
But as well as that
we have dependencies that need development time,
the dev dependencies.
So these are tools to help you build the application
and to test it.
So what have we got here?
We got the TypeScript compiler
because Angular is TypeScript.
We have, oh, the Angular CLI.
In terms of testing, we have these libraries here.
So where to start.
Jasmine, core that defines the API
for writing tests.
Karma is the test runner.
And Karma-Jasmine enables it to run Jasmine tests.
Karma enables you to do coverage testing
to make sure that every line of code is actually covered
by a test.
When you run Karma, we'll see this in a few minutes.
When you run Karma to execute your tests,
it can automatically open a Chrome browser
to display the results.
And it can generate an html report
to say which tests work and which tests fail.
The test results are displayed in a webpage.
So various different tests related dependencies.
All of those obviously get installed in node modules.
When you do an npm install.
Also, probably just reminding you
that because we are using TypeScript
there are also some TypeScript dependencies there.
They define interfaces and enums
and such like to keep the TypeScript compiler happy.
So we have various dev dependencies.
What we need to do now is to see
what does it look like to write the test?
Okay, there's a script in package json called test.
It's the equivalent to ng test.
So you can either say npm run test
or you can just say ng test.
When you say ng test, it'll run the tests.
And what it does,
there's a file that tells it what tests to generate
and what tests to run.
We'll have a look at that in a moment.
When I was putting together this series of examples,
obviously the purpose of the course really is to show
how to test real applications.
So in an Angular application
that would mean testing components,
but it turns out to be a little bit too tricky
to do in one step.
So I thought what would be a useful first step
would be to show how to test a simple class.
So I'm gonna use the CLI tool
to generate a class called bank account.
Ng is the Angular CLI,
the g means generate.
You can generate a whole load of things like a class,
give it a name, use lowercase, and hyphens.
It'll convert it to a capital letter
and every hyphen will be replaced with a capital.
So it'll be capital B, capital A.
It'll generate a class called Bank Account
with a capital B and a capital A.
And it puts it into the a source app folder.
So it'll generate two files,
a file that contains the class
and the file that contains the test
where you can test the class.
So, always follow these conventions,
lowercase with hyphens,
and follow these file extensions as well.
It's what everybody uses in the Angular community.
So those have already been generated.
I've already done this in the demo.
And let's have look in the source app folder.
We have a bank account ts.
What I've actually done,
is I've implemented a simple bank account class.
We'll go through that in a moment.
And also generated by the Angular CLI was a spec.
It calls them specs.
A spec is like a test.
A specification for the behavior you expect to have
from your component or class.
I'm actually gonna generate another class
just because I can.
I'm gonna go into my demo folder,
example2\HelloWorld.
And I'm going to use the Angular CLI
to generate a class called employee.
So it'll generate two files,
an employee dot ts
and employee dot spec dot ts.
And it'll put those into the source app folder.
So let's take a look.
In my source app folder, I now have an employee ts.
It's exported.
Export basically means public.
It means that someone else can import it.
If it didn't say export,
then it would be private to this file.
By default, in modern JavaScript,
classes and functions and whatever
are private to the current file, to the current module.
If you want to access them externally
then you have to export them, like that.
So that's my employee class exported,
and the spec or the test, that was automatically generated.
It's very reminiscent of Jest, isn't it?
It is Jasmine, technically speaking,
but it looks just like Jest.
Import the class from the relevant file.
You don't have to specify employee dot ts.
It kind of knows that it's the employee ts file.
It imports the employee class from there.
There's a test suite.
That test suite starts and ends there.
We've seen all of this many times already.
It has a single function.
We use it to represent the function.
So like a test, basically.
The test has a textural description
and then the body of the test.
It just tries to create a new instance of employee
and verify that that comes back not to be undefined,
basically.
Does that class exist?
That's the simplest possible test that you could write.
Well, in terms of my bank account class,
I decided to implement it like this.
Export a bank account class.
A constructor to take in a parameter for the person's name
with a default empty string.
The initial balance with a default value of zero.
When you have a constructor in TypeScript
and you declare the parameters as either public or private
it does two things.
It takes these in as parameters
and it all automatically declares fields with the same name.
So effectively, it's like saying, this bank account
has a data member,
a private data member called name,
which is initialized by that parameter.
It takes the parameter and it automatically adds it
as a property into the class.
So my bank account has a property called underscore name
and a property called underscore balance.
I've got a deposit and withdraw method.
When you implement methods in JavaScript,
when you wanna access this object's data
you have to say this.
In Java or C ++ or C #,
you don't have to say this to refer to your own state,
but you do have to in JavaScript and TypeScript.
So in this bank account,
increment the balance or decrement it.
Oh, and gimme the balance, please.
The balance was private.
That's good practice.
So if you want to get the balance externally
here's a gutter.
I can just, this looks like a function
but I can just treat it like a field.
I can say a bank account dot balance
and it'll call this gutter and it'll return the balance.
You can also have a setter, but I don't.
I've just got a gutter to get the balance.
If I want to change the balance
I've gotta call deposit or withdraw.
So that's simple enough, isn't it?
That's my bank account class.
To test it,
so here's my generated test code.
We've seen a similar thing for the employee class just now.
I haven't changed this.
So it puts it into the same folder,
source app, bank account spec ts.
It imports the bank account class.
Notice with capitalization from the bank account ts.
Here's my test suite for bank account class.
I've just got one test function should create an instance.
There we go.
So in my test, try to create an object that should succeed.
I should get back an object.
Basically it's not null and not undefined.
So this should work.
Okay?
So that's the test.
Next question.
How do you run the test?
Oh, actually we using expect script modules here.
So we import modules like this.
Import the bank account.
From the bank account module import the bank account class.
Fair enough?
Our tests here using Jasmine.
But to be honest, Jasmine and Jest are so similar,
there's no problem there.
This is how you can run the tests.
Open the command window in your demo folder
a hello world ng test.
And when you use the dash dash watch option
it observes your code and it runs the tests.
It automatically reruns the tests if your code changes.
Now, this takes a while to get going.
Everything takes seems to take a long time with Angular.
So I'm just gonna start that
while I'm talking for the next few minutes.
So I'm in the right folder.
Hello world ng test
dash dash watch.
So I'll leave that run for a moment or two
for it to get started.
And while it's starting, I'll just describe
what actually happens.
So when you run the command like that
it builds the application code
and because TypeScript has to be converted into JavaScript
and other things as well,
it starts Karma.
Karma is the test runner.
Okay?
So basically it fires up the Karma engine
and Karma basically runs all the tests
as specified by this file here.
We'll have a look at that in a moment.
In the source folder, there's a file test JS.
It tells Karma what file pattern to look for
when it's trying to figure out which files are tests.
Should we have a look?
So in the source folder, if I just collapse it down again,
in my source folder, we have a test dot CS.
So in here it's basically telling it
what constitutes a test file.
So it's a bit complicated, but it's this thing here.
That's the important thing,
is basically saying, run any files that have this pattern,
a file name called dot spec dot ts at the end of the file.
Okay?
So any file name that ends with that pattern.
The back slash here is an escape sequence
because dot has a special meaning in a regular expression.
The back slash dot means literally just a dot.
So any file that is dot spec dot ts
that ends with that pattern is considered to be a test.
And those are the files which have executed.
So that's good.
It'll find
employee dot spec dot ts,
and bank account dot spec dot ts.
It'll run those tests.
Oh, there's another one up here
for the application component.
So I'm gonna have basically three suites.
A suite for the component,
that's a little bit more complicated.
We'll have a look at that later.
A suite for my bank account
and a suite for my employee.
All of those tests have,
those files have the correct file name and convention.
So those tests will be executed by Karma.
It opens a browser window,
Karma, opens a browser window to show the results.
Did the tests work or did they fail?
I wonder how my ng test is getting on.
Oh, it seems to have worked okay.
It started the server, it launched the browser with Chrome
and it ran the tests.
There were five tests and they've all executed successfully.
You can see the result in the browser.
And it'll rerun the tests automatically if we edit any code.
So this is what the browser would've looked like
in my original example.
In my example now, I've also got tests
for the employee class.
So let's have a look what the browser looks like
in our example.
Here it is.
So there was a suite for my app component.
There was a suite for bank account
and there was a suite for employee.
Inside the bank account suite there was one test.
Inside the employee suite there was one test.
And they've all worked fine.
Just in case you're wondering,
what would it look like if it failed?
- Let's deliberately introduce an error.
Let's go into my employee spec.
And what I'll do here is I'll deliberately have
something that fails.
I'll say 'false'.
Expect false to be truthy.
Well clearly that's gonna fail.
Let's see what it looks like when a test fails.
Whenever you change any code or any tests,
the test runner is watching your source code and your tests.
So Karma will automatically rerun the tests
whenever anything changes.
And this is what it looks like.
I need to make it a little bit smaller.
Okay.
So Karma connected to the browser environment
but one of the tests failed, basically this test here,
the employee should create an instance that failed
and it tells you why it failed.
Basically when an expect function fails
it throws an exception or throws an error.
And here's the error, expected false to be truthy.
I expected this to be truthy on line six.
Okay?
So that's the error that occurred.
So that was really easy.
Basically just open up a command window,
ng test dash dash, watch,
it'll run all the tests and it'll keep on running them
as you are going through your coding process.