Mocking Components in Angular
Tags: angular, mocking, testing, jasmine
Published: 2020-10-28 13:37:33

Are you a front-end developer using Angular? If yes, you _need_ to be writing unit tests to give you the confidence you need to know that your code is working as intended.

This series of articles will cover concepts of unit testing, and show you how to test common operations in an Angular project.

In this first article, I hope to accomplish three things:

1. Understand why testing in isolation is extremely important
2. Understand how Angular resolves dependencies 
3. Understand how to mock components

### Testing In Isolation

One of the things I've noticed throughout the years is that many developers fail to understand a key concept of unit testing: testing in isolation. 

Testing in isolation sounds complex, but it's actually a simple concept.

Testing in isolation means that the unit that is being tested should be separate from other parts of the application.

What does this mean when we talk about unit testing in Angular?

Whatever you're testing (whether that be a component, service, pipe, etc) should have all other dependencies (units) separated/mocked. 

If you don't test in isolation, you'll end up with hours of headache as you sift through ambiguous console errors trying to figure out why your tests are failing.

Still a bit confusing? Keep reading; I'll clear up a few things in a minute.


### So, how does Angular handle dependencies?

Before we dive into mocking components, we need to understand how Angular resolves dependencies. Angular resolves dependencies through modules. 

 [This is one of the best descriptive definitions I've found. ](https://angular-2-training-book.rangle.io/modules/introduction#:~:text=In%20Angular%2C%20a%20module%20is,to%20see%20the%20full%20picture.) 


> In Angular, a module is a mechanism to group components, directives, pipes and services that are related, in such a way that can be combined with other modules to create an application. An Angular application can be thought of as a puzzle where each piece (or each module) is needed to be able to see the full picture.


Open the `app.module.ts` file. 

```
@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
```
There are several properties in the NgModule, but let's cover some of the more important properties:

1. The `declarations` array is used to list out components, directives and pipes in your application. Whenever you generate a new component, directive or pipe using the Angular CLI, it will automatically be placed here. 
2. The `imports` array is used to list out other modules in your application.
3. The `providers` array is used to list out services, but generally you won't edit the providers array in the `app.module.ts` file.


Try to remember what each array is responsible for - we'll come back to this shortly.



### The Problem

When we generate a new project with the Angular CLI, the `AppComponent` is generated for us by default. 

A new project also has some default tests as well. Running the tests produces the following results:

> NOTE: I have 'ChromeHeadless' enabled in my `karma.config.js` file.


![Screen Shot 2020-10-27 at 2.26.07 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603826778810/Ymq_7Qtho.png)



It's nice to have a starting point, but you'll quickly find yourself generating new components and services. 

Let's generate a new component with the Angular CLI called `HeaderComponent` which displays a navbar for our web application. Generating the component isn't enough for it to show up in your web application; we need to consume it so that it is rendered on the screen. In order to do that, let's say that we consume the `HeaderComponent` in the `AppComponent`.

```
// app.component.html

<div>
   <app-header></app-header>
</div>
...

```

Now, in order for the `AppComponent` to work correctly, it needs to render the `HeaderComponent`. Thus, we can say that the `AppComponent` is _dependent_ upon the `HeaderComponent`.
 
From a testing-perspective, we now have a problem. 

If we run the tests in our project using `npm test`, we'll see that we have some failing tests.

![Screen Shot 2020-10-27 at 2.27.35 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603827021827/Rg5Zr-QsH.png)



Why? 

Looking at the output from the terminal gives us a clue. 

The test file associated with the `AppComponent` assumes that we are testing in isolation; it only contains what it needs to run the tests for the component. Because we've introduced a new dependency (the `HeaderComponent`) in the template file of the component under test, the test environment is now complaining because it doesn't know anything about the `HeaderComponent`. Open the `app.component.spec.ts` file. Notice the following code and the lack of a definition for the `HeaderComponent`:

```
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

....
{
```

When we generated the `HeaderComponent` with the Angular CLI, it automatically imported the component in the `app.module.ts` file under the 'declarations' array, but does _not_ include the component in test files, as shown above. 

Because the `app.component.spec.ts` file doesn't have the `HeaderComponent` listed in the `declarations` array, it doesn't know how to satisfy that dependency. 

### The Wrong 'Solution'

Now that you understand why the test is failing, your first instinct may be to import the `HeaderComponent` and include it in the `declarations` array like this:

```
beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent,
        HeaderComponent
      ],
    }).compileComponents();
  }));
```

Doing this and running the test makes all the tests pass.


![Screen Shot 2020-10-27 at 4.11.32 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603833100600/gSFqpymgL.png)



All good, right? 

Well, not really.

By bringing in the `HeaderComponent`, the test environment is now using the _real_ `HeaderComponent`. This breaks the law of testing in isolation. If the `HeaderComponent` had other components inside of it, or had a service injected in it, all of those dependencies are now showing up and being used in our test file for the `AppComponent`. No bueno.

How do we solve this issue?

Let's take a look.

### The Real Solution - Mocking

Instead of using the real `HeaderComponent`, we can create a fake class, also called a mock, that _looks_ like the `HeaderComponent` and then provide it to our testing environment. This makes the test environment happy, and allows us to define the shape of the component without all of the other dependencies and/or encapsulated logic. This makes testing super simple.

So, how do we mock out a component?

It's very simple.

At the top of our test file, use a `@Component` decorator and define a new mock component class.

```
@Component({
  selector: 'app-header',
  template: ''
})
class MockHeaderComponent {}
```

Notice a few things:
1. The `selector` property has a value that matches the selector of the real `HeaderComponent`. This _must_ match the real `HeaderComponent` selector and is how the test environment satisfies the dependency.
2. The template property is required, but can be left as an empty string. 

Now that we've defined the mock component, head back to the `TestBed.configureTestingModule` and include the `MockHeaderComponent` class in the `declarations` array. 

```
TestBed.configureTestingModule({
      declarations: [
        AppComponent,
        MockHeaderComponent
      ],
    }).compileComponents();
```

Now if we run our tests, everything still passes. The difference is that the `AppComponent` is now using the mock class instead of the real `HeaderComponent` in the tests. 

Great job!

### Final Thoughts

Now that you know how to mock components in Angular, you've tackled one of the most common headaches that developers face when unit testing. 

In a real enterprise application, I'd move all of my component mocks into their own directory in the project so that each test file can import the mock component it needs. 

I hope this article has been helpful and has helped demystify what it means to test in isolation, and how to actually mock out components in Angular.

In the next part of this series, I'll show you how to mock out services - make sure to subscribe to my newsletter so you don't miss it!