[MUSIC]
The fragrance of jasmine from all those flower garlands
on the temple street wafting through the air.
What a settling feeling.
Must be the good karma from my past life.
Sorry, where was I?
I was talking to you about angular testing, right?
And we're going to use karma adjustment to do our angular testing.
So let's get on with it.
One good thing about using the Angular CLI is that it sets up all the scaffolding for
you to automatically include the support for
testing your Angular application.
So you will notice that whenever you generate a component or
a service along with the component ts files,
you will also notice a file called component.spect.ts
already included into your project folders.
And also, if you go to the package.json file,
you will see that there is already support for the Angular testing included there.
So if you go into the dev dependencies you will see that the jasmine core,
the jasmine spec reporter, the karma and
karma related npm modules are already installed within your
angular application that is scaffold out by Angular CLI.
So, we have all the things and place for
us to just get on with doing our testing for our angular application.
So, if you look around you'll see that there is
a file called test.ts in your source folder,
so let's open the test.ts file.
You'll also see that there is another folder here called to e2e.
We will come back to that in the next exercise.
But in the test.ts folder, you will see that this is where
your karma support is being configured here.
So going into this test.ts file, by default,
when you use the angular CLS support for test,
it'll run the tests for all the components and
services that are included in your angular application.
Now, I am going to illustrate to you how to do testing for
one specific component, the menuComponent.
So I want to restrict myself to only use the menu component .spec file and
do the testing for just the menu component.
So, go into the test.ts file.
One way we can configure the Angular CLIs test to run the test only for
a specific component is to go to this line here,
which says const context and then change this to menu,
you can see /menu\.component and spec.ts file.
Earlier it was just /\.spec.ts file, which meant that you
will run all the spec files that are there in your project source folder.
So, right now, by specifying this here,
I am interested to run the test only for the menu component spec file here.
Now this I figure out by searching around on the stack
overflow where I found somebody giving me the answer to this,
pointing to some resource on Angular CLI, and so
that's how I figured out that this is the way to restrict
the test to a specific component's test file.
So after making a change to the test.ts file, let's save the change.
Now, we will try to run our test on the menu component.
So as you see, in the menu component, we already have the menu
component .spec.ts file, which contains some code already.
Let's try to run this test and see what happens.
Going to the terminal, add the prompt, type ng test, and this
will result in an Angular CLI starting the testing for the menu component.
So when you type this and hit the Return,
then Angular CLI will start compiling your angular application.
It will also open a browser window, in my case it will open a Chrome browser
window and then use that browser window to run the test.
And you see that in the back, you have the Chrome browser window running and
it is all full of red and a whole bunch of words there.
The same information will also be printed out in your console window,
and from this I can see that my test did not run correctly.
There is something not configured properly in my test file, so I'll need to go and
edit my test file to include appropriate support for running this test correctly.
So let's go to the menu component spec.ts file and
then prepare our test accordingly.
Now we can leave the ng test running right here.
Anytime you make changes to the mini component.ts file or
the mini component.spec.ts file and
save the changes, this test will automatically run at that point.
So, let's Let's go to the MenuComponent.spect.ts file.
Going to the MenuComponent.spect.ts file,
we understand that the spect file uses the Jasmine syntax for describing the test.
So you already noticed that there is something here
which starts out with describe and then there is a function defined in there.
So this is what I was referring to when I talked about the Jasmine syntax earlier.
And also if you scroll down you'll see an it or so in there.
And in addition, you'll see something called beforeEach, here.
Let's examine this file in a bit more detail to understand how this
file is configured, and how this test is set up in this particular file.
So within this test file, you will notice that
we are importing here a set of classes from Angular core testing.
So this module enables you to provide the testing support for
your angular application, and enables you to design the test for your component.
So you see that I am importing async and
ComponentFixture and TestBed from this testing library.
I'm going to call it testing library.
Also you see that the MenuComponent is also imported here.
Now, once we understand these two,
let's walk down the code to understand what we are trying to do here.
So, here, as you expect, this describe here is
specifying that this particular function is describing,
A Jasmine based test that we are specifying for our Angular application.
So when you look at the describe here,
you will see that this includes all this code in here.
So this is part of one single set of tests that we are running are our
menu component.
So inside the describe, you will see that there is a string here.
This specifies this particular set of tests that you're running here.
So, it says on the menu component.
So here you can specify whatever string you want.
You can use this as a description to
specify what this particular set of tests are doing.
So here, I'm going to stay with a before thing.
We are trying to test the menu component.
And after that, you see that we are specifying an addo function here.
Within this addo function, we are going to construct the test.
For our MenuComponent here.
So after this, you will see that we are declaring a variable called component,
which is of the type MenuComponent.
Now and then, we're also declaring a fixture variable,
which is of the type ComponentFixture.
Which is of the type MenuComponent.
Now, why is this interesting?
Now, within our Angular application,
we have the MenuComponent that we have already designed.
So So in this test file,
we're going to create that MenuComponent using the angular TestBed,
and then we're going to carry out the tests on the MenuComponent.
So we're going to isolate the MenuComponent, and
then carry out unit tests on that specific component.
Now, this is where the angular testing utilities
which come with support using TestBed.
The TestBed allows me to set up the environment within which
I can test my angular component.
So here, you see that we are saying TestBed and
then calling the configure testing module method of the TestBed class here.
And inside this configure testing module,
you can declare a bunch of things, and you will see me adding more here.
This acts exactly like the NG module configuration
that you do for our at module there.
So all the information that you need for
setting up the MenuComponent will be declared here.
So the NG model part that you saw in the app module,
similar kind of syntax can be used here to declare the support for
testing the MenuComponent here.
And also after this, you will see this function called to compileComponents.
So this method will compile the MenuComponent and
make it ready for doing the testing.
Note also, that this whole thing, this function here,
is enclosed inside something called an async here.
Now, why do we need this async?
Now, because the preparation of the components using this
compileComponents method takes some amount of time.
And until this is completed, we can't proceed forward with the tests.
So using the async module, we are essentially specifying that
I'm going to wait for this whole thing to complete before proceeding forward.
So the asynch module as you see,
wraps a test function inside an asynchronous test zone.
So, what this means is that this test will automatically complete
when all the asynchronous calls inside this zone are done.
So this is what the use of the async that we have here, does.
So what this means is that you can use this sync function,
either in the before each or in the it also, so within the it, our declaration.
So when you see within the it also, you will see that you can use the async.
So in case you have any operations here that done asynchronously.
Then you need to wait for those operations to complete for that test to complete.
So for example, if you are calling a service and wait for
the service to return the value then.
All that needs to complete before you can proceed forward.
So those are enabled by using this async, to surround this function here.
Now, in addition, the second beforeEach, now we have separated
this into two separate beforeEach here, because we want this to complete.
And this compileComponents is going to take a certain amount of time,
especially if your component is using an external template,
as we do in our MenuComponent.
So, it requires some time for the external template to get ready for
my testing to proceed forward.
So, we need to wait for this whole thing to complete.
So that is why after this is complete,
then we will get to the second part where we are going to
get some references to some values from the test bed.
Now, what is this beforeEach function that you are doing here?
What the beforeEach means is that whatever you declare inside here,
that function will be executed beforeEach test,
that you are going to specify later on using the it.
So using this beforeEach, you can set up the initial
configuration for your test to proceed forward.
So meaning that here, we are setting up our TestBed,
preparing our menu component and then setting up a few things for
our menu component, before we proceed on to carry out the test.
So for each of the test that you're going to specify using an it here,
these before each whatever you specify in this before each,
will be executed before the test is conducted.
So here as you can see I am preparing the TestBed.
Then after, I am getting access to the fixture.
So I am using the TestBed, I'm saying createComponent and MenuComponent.
So this will return me a reference to the MenuComponent that
I'm creating within my test script here and
then get a reference to that, because I will need a reference to
that in order to do some manipulations on that component there.
In addition, from the fixture, I am getting an access to the component
instance that is being created by my component fixture here.
So as you can see, the component fixture of the type MenuComponent and
then calling the componentInstance, gives me access to that specific menu
component that I have created inside the TestBed for carrying out the unit tests.
Now, after this we call this this detectChanges.
Now, what this means is that this will proceed forward only after all the changes
are completed, and then recognize that the changes have completed.
So this will trigger a change detection cycle for this component.
So what this means is that this will ensure that you have made any
changes earlier, all the changes are detected and
everything is stabilized before you can proceed forward with your test.
Now, after this, we're going to use our it to configure our first test here.
What is the first test that we are doing here?
The first test,
what we're going to test is to make sure that the component has been created.
So, within this here, I'm using the Jasmin syntax and I'm saying it.
And then here you can give a description in the form of a string
to specify what this test is actually testing about.
So, what we are saying is,
we are testing to see if the component has been created or not.
So to do that, I'm saying it should create meaning that,
this setup here should have created the component correctly.
So that's why inside here, I'm using this method called expect.
So the expect method inside here takes a value here, and
then you can test to see if this value satisfies something.
So here we are saying toBeTruthy, meaning that this particular value should be true.
That is what is specified by this method called here called toBetruthy.
You can also say toBefalsy, meaning that should evaluate to false.
But in this case,
we are testing to make sure that the component has been created correctly.
So that's why we're saying expect toBetruthy here.
So this is a simple test that we are doing to make sure that our
component is correctly getting created.
But as we realize when I run this test, it is not running correctly.
Let's quickly glance and see what the problem is within
our component creation in the console window there.
Going back to the console window, let me just browse back and
see where the problem is.
And I'm beginning to notice that this is specifying that it doesn't seem
to be recognizing some of the things that my component is using.
In particular, it is saying that the MdSpinach
is something that it doesn't recognize,
the MdGetGridList and a few other things.
That immediately suggests to me that I need to do a few more
configurations within my test file before my test can run correctly.
So, let's go back to our spec file and make some changes to it.
Going back to our spec file,
from our recollection of how we created the template.
We know that within the template we had a router link in the MenuComponent's
template.
So I need support for routing in order to recognize that router
link that we used within the MenuComponent.html file.
So this is where I'm going to import
another test support provided
called as the RouterTestingModule,
which is available from Angular/router/testing library here.
So this router testing module will enable me to
configure some aspects about my router.
We will see that the way we use this is a little different from the router module
that we have used in our application.
So we will import the routing testing module, also you note
that we are using some animations, and
also using the Angular material component within our template.
So I need to import
the BrowserAnimationsModule into my test file.
So this comes from the angular/platform/browser.
Now you'll notice that we need to do this explicitly here simply because,
in the actual Angular application all this was in the app module,
and so that was available for the components.
Right now we are testing this component, the MenuComponent in isolation, so
that's why it doesn't know about all these things so
we need to explicitly specify all these things here.
So I will import that from IM platform browser animation here.
So angular.platform.browser animations importing the browser animations
module here.
Let me include the flex layer module, so
I will include Angular FlexLayout.
So these are needed because I need them in the template,
I'm going to also import that dish from,
Shared/dish because as you will see later,
I need to configure, A,
DishService because my MenuComponent
depends upon the DishService so
I need that also in place.
Although you will see that I am going to stop that out.
I'll explain that in a short while, and also I need to import a few more things.
I will import DISHES from,
you recall that we use this constant called DISHES
that we exported from the shared/dishes.ts file.
I will need that in order to configure a few more things.
I'm also going to import the baseURL.
Which I declared in the, Shared/base
url file.
And also, I need to import
the Observable from,
rxjs, slash, Because I
will also be using Observable a little bit later, when I configure a few things.
So now, I have imported all these things.
Getting down into the TestBed configuration, you see that this
part is exactly like the engine module decorator that we have in the app module.
So this is where I can also use the imports just
like I did with the engine module.
And within the imports, I'm going to import the BrowserAnimation
module because that is something is that l require, and
then also import FlexLayout module.
And also the RouterTestingModule because
I would need the RouterTestingModule to configure a few things.
Now, when you use the RouterTestingModule, you need to use this method
calledwithRoutes instead of using forRoot as we used with the router module here.
So within this withRoutes you can specify some default
routes that you need for your specific application here.
I'm just going to configure only one single path here,
and that is good enough for me for testing this module here.
So I am just going to say path: 'menu',
and component: MenuComponent.
Since the MenuComponent templates is using certain material components,
let's go ahead and import them.
So we will import the MatGridListModule
from @angular/material/grid-list and
also the MatProgressSpinnerModule from
@angular/material/progress-spinner.
And once we have imported them, then let's go ahead and
include them into the imports down below there.
So let's add in the MatGridListModule and the MatProgressSpinnerModule.
And that is all that I need for
this particular imports to be configured here.
So along with the imports, we have the declarations where I have
the MenuComponent being used here, I can also configure the providers here.
So I need to configure the providers here.
Now this is where we are going
to create a few stubs here, so
I will say provide: DishService.
Now, the DishService that we're going to use here is not going to be the real
DishService.
When you're testing a component, you don't want to use a real service.
Instead, you risk stubbing out the service, and
then use a stub DishService, which I am going to call as dishServiceStub.
So I will say, useValue: dishServiceStub.
So this would be a stub function that I will use in order
to mark the DishService here.
So that is one that I am going to make use of.
And also the second one that I am
going to make use of is BaseURL,
which I am going to use the value as the baseURL
that we have already imported earlier.
So, now, with this, I have actually configured my testing module,
so notice that the way we specify this is exactly like the way
we specify the NG module decorator for our app module.
And here, we are only configuring those that are required by the MenuComponent, so
I don't have the other ones that we use in the app module itself.
So this is how we will configure the testing module.
Now the one thing missing is this dishServiceStub.
So what I'm going to do is to
implement a function here and
call that let dishServiceStub
be a simple JavaScript object here,
which I am going to let it act in
place of the real service.
We can stub out services like this as seen here.
So inside here, I'm just going to
supply a simple function,
the getDishes function here,
which will return Observable of Dish array type.
So that is exactly how the return value was configured there.
And I'm going to return
an observable of DISHES here.
So using this stub function here,
what I am doing is I am stubbing out the dishService, and
then configuring it with this one method that I need for
my cache, saying getDishes, and when this method is called,
I'm just going to return this value.
The DISHES constant, here.
So this way, now you can see that how I am controlling what is being
supplied to the component from a stub dishService that I have just created here.
So this is how you will create stubs for your dishService.
The other approach that we can use for
cases where we are using services is called as using the spy.
Jasmine supports something called Spy, which allows us to spy on function calls,
and then capture the function calls, and then respond to those function call.
So that is another way of using a fake service in place of the real service.
So now we are able to control what is being supplied to our menu component.
So after these modifications, then we're
going to go down into our before each method here.
After the async, what I am going to do
is I will get hold of the dishService.
So you see that I am using the test bed and then using the get
method of the test bed to get ahold of the DishService that has been created here,
from this particular stub that we have declared here in the providers.
So this gives me a reference to the DishService so
that I can call the methods of the DishService wherever I need here.
So after making these changes, let's save the changes and
then see how our Angular CLI will run this test after these changes,
and whether our test will be successful at this point or not.
So let's save the changes.
The moment you save the changes you will notice that your NG
test will start rerunning our test again.
So it will compile the application and then rerun the test.
And voila, our test successfully passed after
making the changes to our component.spec.ts file.
So our first test, meaning that we are now able to create a menu component
from our menu, component.spec.ts file there.
So our test setup is working right.
Now I can go in and do more intense tests within my test component.
Going back to that menu component.spec file, let me add in another test.
Now you see that because I now have an established service here
which is returning this DISHES value I can then now make use of my dishService,
to which I have got the reference in here.
Inside was before each, and then call the getDishes method and
then I'll be able to obtain the value.
And that'll be available within my menu component that l have just created.
So my MenuComponent should now get access to the dishes constant that
has been supplied back to it through this dishservice stop.
So I can now test to see if the dishes variable inside my
MenuComponent is actually receiving the values correctly or not.
So going in, let me create another test here.
So I will create a test here, With the it,
as you saw earlier, so just like the previous one.
This second test I'm going to call that as it and
dishes, Items should be 4,
because the dishes constant that we declared in
the shared.dishes file has four dishes in the constant there.
So when my dishes variable inside my MenuComponent or
ts file gets initialized by calling the dishservice.
It should have received those four dishes and
got initialized with those four dishes.
So I'm going to carry out a few tests on that dishes variable there.
So how do I get access to the dishes variable here?
So I would say it dishes items should be 4, and
then I will declare an arrow function here.
And inside this arrow function I'm going to specify
the various tests that I'm going to carry out.
So here I'm going to now say expect(component.,
and notice that it automatically gives me
access to this dishes property from the MenuComponent.
So I can say that component.dishes, and then I can test
to see that the length of that dishes JavaScript object array should be.
So this is where I use the Jasmine's toBe method here.
And then I can specify the value 4,
because I was supplying 4 dishes in my dishes constant
that I have declared in the shared.dishes file there.
So that is the first test that I'm going to carry out,
this should turn out to be true.
Otherwise the setup did not work correctly, so
I know that my code is not working correctly.
But I know for sure that this test should work out correctly.
If you want to be sure,
let's save the changes and then go and see our test running.
Going to that terminal,
you see that now my app is being recompiled, and the test runs.
And notice all the greens in the terminal window.
It says executed 1 of 2 success, executed 2 of 2 success.
Which means that both my tests ran successfully.
If I look at my browser, so you see that the MenuComponent,
both the tests are successful.
Let me create a few more and
add them to that set of second group of tests that I'm creating there.
Going to the menu_component_spec.ts file, I will add in one more.
I will say expect component.,
and I will say dishes[1].
Recall that the first dish's name was,
So that is what I'm going to test, the dishes[1].name.
So this is how I can test to see if it is receiving the values correctly.
What are all the ways that I can test?
If I put a dot there, it'll give me a suggestion of all the possibilities here.
So I will say, I will also again use the toBe method here.
And then the second dish name is,
Just that there, so that is the second caster I'm going to carry out.
So if my code ran correctly, then this should also pass.
Similarly, let me put in one more here.
So I'll say expect,
Component.dishes and I will say 3.
And in here, the featured
property should be Falsy.
That you should be false, it is not true in this case, so let me add in one more.
You can start adding more if you want,
play around with it until you're satisfied.
So do those additions. Let me save the changes that I've made
here, let's go and see if this test runs correctly.
And going back to my terminal, you see that my test is again running.
You see that both the tests were successful,
now I'm going to make one of those things fail.
So I'm going to go in and change this to truthy.
See, that is the fun of playing around with code.
I'm going to change this to truthy and I know that this test is going to fail.
So let's save the changes.
Taking a look at the terminal, there you see the test has failed.
You see all this red here, meaning that the second test did not pass correctly.
And if you scroll backwards, you would see this particular test failed,
because somewhere down there it'll say that it did not.
Okay, see this statement here it says expected false to be truthy,
so that's why it failed.
So it was not supposed to be true, but I said it should be true, and
obviously the test is wrong, so that's why the test failed.
So this is how you can even test to see if the test fails,
if you asked for the wrong things also.
But again, let me go back and correct that one more time.
So going back to my, File I'll change them
back to falsy, because I want this test to run correctly.
Now we're not satisfied yet.
Now in this particular test I am only testing the dishes
variable that is inside my MenuComponent.
How do I go about playing around with the template itself?
So this is where we need some more help from Angular.
To carry out some tests on the template, I'm going to go in and
pull in a couple more imports here.
So going to my top of this file, menu_component_spec.tsfile.
I'm going to import, One
more called By, which is a method.
You will see me using this in a short while.
This should be imported from, Angular,
/platform-browser.
And also, I'm going to import one more which is a debug element.
The debug element allows me to get access to the DOM so
that I can carry out tests on the DOM elements directly.
So that's where I'm going to import this from, the Angular code here, so these two.
So add in the By and the DebugElement to the imports, and
then once you do that, we will walk down into the code here.
And then we will add in one more test where we are going to go into the DOM and
fetch one of the items from the doc.
So I would say it, so one more test here,
what is this test going to do?
For this test, I will specify
the test as should use dishes in the template.
I'm just randomly saying something here,
because I just want to check to see that the dishes variable,
whatever it is making available, is being used in the template correctly.
So that's the part that I'm setting up, and
then the second part is the arrow function, and
within this arrow function I'm going to supply the code for the next test.
So inside this arrow function what I'm
going to do is I will first call fixture and
then detect changes.
And then after that I will say let de:,
Is a DebugElement.
Through this I'm going to get access to the DOM and
then the element would be HTMLElement.
So I want to get access to one of the HTMLElements from my DOM.
So which element do I want?
So to do that, I will get access to the,
DebugElement by saying fixture and debugElement.
So this allows me to get access to items from my DOM here so debugElement.
And then I can query and then pick only those that will be of interest to me.
So I'm going to query and then I will ask for by using the By.
So notice where I am using the By that I have included by importing it.
So By, the By supports a bunch of metrics by which I can
ask to access particular elements in the DOM.
So as you can see it'll tell me what I can import and
then the .css allows me to get access
to match elements by the given css selector.
So what is the css selector that I'm going to use?
I know that there is an h1 element, html element in the DOM and
that I'm going to get access to the first h1 element from the DOM.
And one of the h1 elements in there is the one that
is going to display the name of each dish there.
Look back and you see that inside the grid timeline using a h1 and
that h1 contains the name of the dish there.
So I'm going to get hold of that and
then I'm going to get an element and then I would say de,
from the debug element, and then I would say nativeElement.
And that gives me access to the element specifically in the DOM
that I am referring to and I have selected by using this statement earlier.
So when I query the DOM and then say, return me all
those which are using the tag h1 for the HTML element.
And I get access to that, and I can query and
say expect (el.textContent).
So I'm looking for the content inside this h1 in my
template fixed content and then I will say toContain.
So this is another method, toContain, this is a Jasmine method called toContain.
This checks to see if that contains a particular value,
and then I will check for DISHES(0).name.
So is the first dish been created in the DOM and added into the DOM?
If my template works correctly, then that must be present in the DOM.
So that is what I'm going to check to see if the grid tile corresponding to
the first DISH has been created and added into the DOM or not.
So I say DISHES(0).name, and say to uppercase.
Recall that I used the uppercase pipe to display the name
of the DISH by converting the name of the dish into
all uppercase in the grid tile in the menu component.
So that is what I'm going to check for, okay, so I'm just checking for
only one of them.
You can carry out many of those tests, you can check for
DISHES(1).name and then DISHES(2).name and more if you want.
I'm just illustrating one example of how you would access elements of the DOM and
then check what they contain inside there.
So this is one test that I'm going to conduct here, so let me save the changes,
and then see if the test runs correctly.
Go into that terminal and when the test runs,
you see that my first test was successful, the second test was successful,
and the third test was also successful, wonderful.
Now, let me make the third test fail.
Going to the menucomponentspec.ts file,
I'm going to remove this to uppercase, and then carry out the same test.
You will see that this test will fail, save the changes.
And when the test runs, you will see that the third test will fail.
Because I am causing it to fail by checking for the wrong thing there and
there you go, the third test failed here.
So this is how you can check to see whether certain things that
are supposed to be there are there correctly or not.
Why is this case, so you can see here, the reason why this test failed.
It is expecting this to be in the DOM,
but instead this is what is in the DOM in the h1 element in the DOM.
So that's the reason why the test failed here.
So again going back let me correct the mistake and
then when I rerun the test you should now see that the test passes successfully.
Going back to the terminal you will see that when this test runs now it'll
run correctly.
And there you go, all the three tests are passed by my application.
Also, looking at the browser, you can see that the three tests
successfully were executed on the menu components template file and
also the menu component itself.
Now obviously, this is not necessarily the most comprehensive test but
I just wanted to give you a flavor of how you would go
about constructing tests within your Angular application.
If you get carried away you can go into all the spec.ts files that you have and
then write your own test for each and
every one of those components using the knowledge that you have gained here.
Also, you can read more about testing in the Angular documentation and
then learn more ways of carrying out tests on your Angular application.
Testing is a field by itself so if I need to teach you everything about
testing that will be come an entire course in itself for Angular.
And in this course, I just wanted to give you a flavor of how
you will carry out testing in your Angular application.
And this is an example of how we can configure and
carry out a unit test on one of the components in our Angular application.
This is a good time for you to do a good comment with the message components test.
[MUSIC]