Transcript from the "Basic Component Test" Lesson
>> Lukas Ruebbelke: And we're going to talk about how to test a component. So what we wanna do is I wanna focus on here my goal for this module is to explain the angular testing utilities and how it relates to a component test. Now compared to Angular 1, this is just dreamland.
[00:00:25] It's so incredible, the utilities that they've actually provided in terms of, in AngularJS, if you wanted to test a directive, you had to do some weird string manipulation and then compile it and do regex against it. It was just crazy. And so they've really done a very good job about making it easy to write component tests.
[00:00:45] And so here is a basic component.
>> Lukas Ruebbelke: This actually looks exactly like a component that I just generated. Except that I have a small property up here, just title. So I just put this up here so that we could have something to actually test. But [INAUDIBLE], I don't think it gets much more basic than this, short of being totally empty.
[00:01:09] And so then here is a basic test. And so this is what is generated with the angular CLI. And so now let's talk about what is happening here. And so I'm gonna walk through this, I'm going to explain the pieces and what they're doing, and how we can use them to write tests.
[00:01:32] So when you're writing a component test,
>> Lukas Ruebbelke: The first thing you need to do is configure the module. So anytime we create a component, what do we do? We go and we add it to, essentially, the app module. So when you are writing a test, essentially what you need to do is create a module for that component to run in so that it can be tested.
>> Lukas Ruebbelke: Well, this is where the TestBed comes in. So Angular provides us with this utility called the TestBed and what this allows us to do is configure a testing module. So it works very much like a regular module but it creates the context and the module for your component to run in.
[00:02:25] So in this case, we want to create a module that has the gadget component attached to it. Just like our app module, we just pass in an object. And we just say, here, declarations, we're declaring this, make this available to our testing module. Pretty straightforward. It's always identical to declaring an NGModule.
[00:02:49] So this is the most important thing. And we use TestBed.configureTestingModule to define the module with a container that our tests are going to run in. And from here, we need to configure the testing module in the BeforeEach block so that it gets reset before each spec. So this and everything that you'll do, you'll see that this happens in a BeforeEach block.
[00:03:15] So everytime you run new test, this gets thrown down and put back up. Secondly, we're going to create a fixture. And I'll explain what that is in just a moment. But if we look here, we can kind of come to the conclusion that a fixture is something that this component is going to live in.
[00:03:36] It's like we're providing it some kind of context. Where we going to put it into a context that's going to prove to be useful to us. What we're saying here is we're defining the fixture up here. And we're saying, this is a component fixture that has or is rapping the gadget component.
[00:03:57] And then down here, in this beforeEach block, we're saying, TestBed.createComponent and we're passing in the gadget component. And so this is returning our test fixture for us and so this essentially creates an instance of the component under test and returns it as a component test fixture. But it also gives us some additional utilities that we can use that come in quite handy.
[00:04:26] For instance, we can get the component instance. So using the component fixture, we can get the component itself using component instance but we can also get access to a debug element. So this is essentially the component's DOM element. So they're actually taking kind of, under the hood, is our By.css providing this representation of the template that you can query, for instance, with a CSS selector.
[00:05:05] So using debug element query and then the By utility, so using By.css, that allows us to construct its query as you reach in and get some information about our component template. It's super, super crazy that now, traditionally, this had to be done in an end-to-end context. But now we're able to actually represent or have some representation of our template and do stuff with it.
[00:05:35] So what we're seeing here is, one, I'm calling component.
>> Lukas Ruebbelke: = fixture.componentInstance. It's returning it. Yes?
>> Speaker 2: It is still in the context of the component, right? So it is still not rendering anywhere in all of that?
>> Lukas Ruebbelke: No.
>> Speaker 2: To get the DOM? It's just parsing the DOM of that component, that HTML file.
[00:05:55] It is not actually? When you're referencing a DOM in a test, you actually are looking at the browser and trying to interact with the DOM.
>> Lukas Ruebbelke: So it's not like in an end-to-end situation.
>> Speaker 2: Yeah.
>> Lukas Ruebbelke: But I think it's probably rendering it in memory somewhere.
>> Speaker 2: [INAUDIBLE] rendering it or is it just parsing a tree and getting a?
>> Lukas Ruebbelke: Yeah, maybe, so that's something I would have to look at but I don't know.
>> Speaker 2: Okay.
>> Lukas Ruebbelke: This is one of these things where like, look, I don't know how I metabolize delicious tacos when I eat them. I just know that they're delicious when I eat them, so.
>> Speaker 2: Yeah, because rendering would be difficult unless you get the CSS at the top level.
>> Lukas Ruebbelke: Right, so I think it's just creating some kind of representation, essentially like the tree, the structure that allows you to then go, it doesn't have to render, for instance, you can do a CSS query just on the thing.
>> Speaker 2: Right.
>> Lukas Ruebbelke: And do that. And so from here, we can now access properties and methods on the component. So component.title, we can write an assertion on that, so we expect this property to be this. Pretty simple, right? They're giving us the component. We don't have to set it up like it's there we just need to write some assertions on it, pretty easy, way easier than it used to be.
[00:07:08] Now, I mentioned debug element, so pretty easy, so I've added this piece right up here, debug element. So I've defined it and then down here, you just call fisher.debug element. And then you can do something like de.query(By.css('h1')). Now you've really got my brain spinning, because we're actually saying h1.nativeElement.innerText.
[00:07:42] So you're querying it as if it were actual DOM. It's probably not, there's probably some representation in memory. And so I don't think it's visually showing up anywhere but I think maybe they've found a way to represent it in memory. So expect(h1.nativeElement.innerText).toBe('Hello Gadget').
>> Speaker 2: How are we doing works for the case when you have a container component and then you have two presentation components inside of it?
[00:08:09] So when you create a container component, do you have to specify that there are these dependent component on it and you have to create those components for it to be tested, so for example our items component?
>> Lukas Ruebbelke: So we are kind of jumping, if we're over here in the spectrum of, this is the basic.
[00:08:28] So we can go really far over here or kinda that question is kinda somewhere here. But in one of the tests, you can actually override the template.
>> Lukas Ruebbelke: So for instance, I could say, I want to render the widget template. But because I'm not interested in testing the presentational components in it, I'm going to actually replace the real template with this modified version.
[00:08:58] And so you can actually override the template. Hit test time, it's pretty powerful what you can do. But you can also render it like a host component and test that your bindings work. There's a lot of things and we could probably spend an entire two days talking about how to do this.
[00:09:14] But you can test your child components and test the bindings, and that it actually renders properly and fires into the parent component. So,
>> Lukas Ruebbelke: Now Angular, this is an important distinction to make or to call out is that Angular is not manually handling change detection out of the box.
[00:09:42] Because what they want to do is allow you to make a change. And if you need to make an assertion, make that assertion, trigger change detection, and then test it. So they're giving you this opportunity to kind of insert yourself before change detection. You can turn it on by default.
[00:10:01] I recommend not. Cuz you can simply force change detections by calling detectChanges. And so this gives us, again, greater control of how we inspect our components pre and post binding. And so a lot of times what I'll do is write in the beforeEach. I'll just call it. So actually the generator, that's what they do.
[00:10:22] They just call the tech changes one time to trigger the bindings and make sure that everything renders. But what you'll need to do is, for instance, if I went component.title equals Hello developers. And I left this line out here. So I did not call this. Well, change detection has not run on my template.
[00:10:49] And so it's still going to be, hello widgets or gadgets or whatever. So in order for me to force the template to render I have to call detect changes, so that's why that is happening.