Transcript from the "Component Testing" Lesson
>> I do not believe that this would be a complete workshop or a course, if we did not stop and talk about testing. And so we're going to take a moment, and we're going to talk about testing within an Angular application and lay that foundation so that not only can you create Angular applications, but you can test them.
[00:00:27] So just to start it, the very, very, very basics when you have an Angular application and it's created by the Angular CLI, or if you use NX dev tools. The easiest way to run your test is just go NPM test and it is going to kick off the Karma test runner.
[00:00:54] And so this is going to pop up a browser and it's going to essentially build the app is gonna run through a bunch of tests. And so one of the things that they have done and I believe every major framework is they've gotten really good it kind of caching responses, and the stuff that hasn't changed then they don't recompile it.
[00:01:20] So, you saw that that was very, very, very quick, but what we have here is 24 specs with zero failures. And what Karma does, and by extension, I believe this might be a jasmine thing is that they randomize the test. So that if you have, for some reason, a test that's dependent on the output of another test that by randomizing it that's going to get caught.
[00:01:49] And so what you definitely don't want is to have unintentional state being carried over from one test to another. And so, I'm going to just exit out of this for now, and we will close this down, and we're gonna go back into our code. And we're going to be working out of the app component.
[00:02:18] And so you'll saw that I just kinda ripped everything out real quick. And the reason being is I want to put this back together. And I want to talk about why we're doing the things we're doing in each part that every kind of element that we add into this, or component or mechanism, what it does and why it is there.
[00:02:41] So, if we're going to talk about Angular testing at the very, very, very core of this is the testing module or the testbed. And so I'm going to create a before each block and this is going to be a synchronous. And the reason why this is a synchronous is because when you have your templates and your component classes separated, is that those need to be loaded all at once and then compiled.
[00:03:19] And so, typically what you'll have is a number of before each blocks, but the initial one is going to be async. And what that does, is that accommodates the fact that you have external templates. So from here testbed, and we're going to go configure testing module. And what this is going to do, is it's going to create an Angular module that's very similar to a real module.
[00:03:53] But it allows us to create an environment for us to load a specific component, or service or whatever unit we're testing into a real Angular environment with its dependencies etc and configure it into a running runtime, that felt weird to say. All right, so within our testbed configure testing module, what we're going to do is we're going to define something that looks very, very, very similar to an object that we pass into a regular angie module.
[00:04:35] And so the first thing I wanna do, declarations and because we're testing the app component, we are going to add that in. And from here, we can add in our dependency. So this is typically the next thing that I do. And this is one of these things that sometimes it's kinda a little bit of trial and error that you will spin up a test, you're like ooh that didn't work and the reason being is because you forgot to include a dependency.
[00:05:12] And so, in this case, I know that I'm going to need the router testing module. And so let's leave it at that for now. So you can get this working and then we'll build on that. And then once I've completed that I'm going to call compile components. And so what this does, is it assembles the testing module, it goes and grabs the components that we need and it calls compile components.
[00:05:44] All right, and then from here, I am going to, Add in one test here. And I'm kind of doing this off the top of my head, but let's see how this works. It should create the app. All right, and so what we're gonna do here is, I am going to Let's do const and I'm gonna go fixture equals, and I'll explain what this is in just a moment.
[00:06:36] Createcomponent, I'm going to pass in the AppComponent and then I'm going to actually get the component. And so the component is going to be fixture.componentInstance. And from here, I'm just going to expect that component is going to be truthy. So not true, but truthy, all right? I'm going to save this, and we'll go back into our command line and MPM test for a very, very basic test.
[00:07:28] I am creating the fixture and then I am grabbing the component off of that. So let me look here. AppComponent should create the app. Very good. So if we go back here, let me explain what the fixture is. So, you have your component, but along with your component you have a number of elements that come along with the component itself.
[00:07:59] So for instance, you have your component class, but then you also have the DOM or the template that exists with that component that you can get access to, if you want. As well as you have the ability to grab the component, but then to also grab the DOM element or the debug element that is that comes with that, and then do query selectors on it and check the the template.
[00:08:36] And let me see here real quick. If we step in here, and so this is, I've mentioned this before, but it's very helpful to be able to, if you're like, what does this thing actually do? To just start clicking through, this is one of I think one of the best things that exist with TypeScript, is the ability to kind of click through type definitions and learn as you're going along.
[00:09:03] So I mentioned the debug element. But you can get a component instance, the native element, the element ref, and the change detector ref. So there's a lot of things that you can do here. For instance, detect changes. This is important, we'll talk about that in a moment. As well as you can check, is it stable, when is it stable etc.
[00:09:31] So the fixture is incredibly helpful. Now what I'm going to do is I'm going to extract these elements right here out of this particular test, and I'm going to break this into a new before each with a slight variation. So, what I wanna do is because I want these to be available before each test, but I don't want to have to keep instantiating those.
[00:10:12] So from here I'm going to define my component, lead component, and this is going to be an AppComponent and let fixture is going to be a component fixture that is going to be associated with this AppComponent. And so now, what I've done is I've just added in an additional before each, and I'm able to grab this.
[00:10:48] All right, now, I'm going to make one more addition here and I'll explain what this is once I'm done typing. When you're writing an Angular test, change detection is not automatic. The reason being, is there are circumstances where you possibly want to validate a pre change detection state against a post change detection state.
[00:11:21] And so Angular gives you the ability to essentially manually detect change or kick off that change detection cycle, and you do that with fixture.detect changes. Now, let's extend this a bit more, and let's write another test and this is going to be around, it should have the correct title.
[00:11:53] And when you have a component instance, what you're referencing is in fact the component class. And so if you have a property on your class, then you can test against that. And so, what we're gonna do here is we're going to create a property called title and I'm just going to go 'Angular 13 fundamentals'.
[00:12:24] And from here, what I'm going to do is go expect(component). Now watch, you see the Auto hint here is showing me the properties that exist on that component, the A TypeScript, and we expect the component title to equal title. All right, and let's go back in and see if these are being picked up, and let me just actually step into this So I'm able to click on this and so you can see now we have two test.
[00:13:11] Should create the app and should have the correct title. All right, now what we're gonna do is I'm going to introduce the debug element. And so I'm going to go up here and we're going to go let D for debug element, and this is going to be a debug element.
[00:13:37] And inside of our second before each we're going to go D equals fixture debug, oops debug element. There we go. And now let's write a test against this. So it should render An updated title. Feel like I picked up an extra parentheses there, or quote all right, so within this, what we're going to do is I'm going to define a new title.
[00:14:26] And we're gonna call this angular 20 fundamentals for our spring workshop. And now let's grab our title element. And so we have our debug element. But what we really need to get is within our template itself We need to pull in this particular element right here. So within the spec, let's go ahead and go debug element query and Angular introduced this pretty handy thing called by.
[00:15:13] And what this allows us to do is essentially select the DOM or the debug element by various means. In this case, we're gonna go with CSS, but I think you can do by directive, by name., there's a bunch of different ones you can use, I typically use CSS.
[00:15:39] From here, we are going to basically grab the title, so we're going to go through and we're looking for anything with a class of title which in this case happens to only be one. All right, and so we're going to go component title equals new title, and then let's write our assertion.
[00:16:03] So our titleElement, nativeElement, InnerText. We expect this to be new title. All right, so within A test a very common approach and I don't think about it consciously that much anymore, but you have a range, Act and assert. And so, just for a bit of insight, if you're not aware of this, I think this is a really good way to organize your test.
[00:16:48] A lot of people do this you'll hear it quite frequently, but what I'm doing here is I'm basically arranging the pieces for the test, then I am acting upon us on doing something and then I am asserting the result, is this indeed what I expected to happen. So I'm gonna save this, go into our browser and let's see if this updated.
[00:17:19] So you notice here, expected Angular 13 Fundamentals to be Angular 20 Fundamentals, and that was not the case. Why is that? Well the reason being is that if you remember change detection is manual that you have to trigger it out. And so if you are making a change to the class, and you need to kick off or that you need the template for the component or testing to update and capture that.
[00:17:56] You need to essentially tell it to detect changes. So fixture, detect changes, and now let's save this again. And let's go back and ta-da, and that is what I would consider to be the basic the most basic fundamental knowledge set that you need to write test in Angular.
[00:18:30] So you have a component that you wanna test. You have a fixture that contains all of the information in the contextual information about that component. Then you have your debug element, which then gives you the ability to one query the template that is associated with that component. And when you make changes to a template and you needed to render or to the component needed to render on the template, this is where you kick off fixture.detect changes.