TDD in Angular - Dependency Injection and Mocking
Tags: angular, javascript, tdd
Published: 2020-05-28 01:36:22

In our last article [here](https://dev.to/qarunqb/tdd-in-angular-understanding-an-angular-unit-test-jja), we went through the basic structure of an Angular Unit Test and went on to test services. In this article, I want to show how to connect your service to a component and how to properly test this from a Test-Driven Development perspective.

_Code for this article can be found [here](https://github.com/Qarun-Qadir-Bissoondial/ng-jasmine-testing-demo/tree/dependency-injection-and-mocking)_

Let's get started!

## Creation of an inventory component

Let's say we want to create a component that takes our `inventoryCount` from the Inventory Service and displays it, as well as increase and decrease the count. This means that `InventoryService` is a dependency of  `InventoryComponent`. In Angular, we inject dependencies through the constructor.

Therefore, we'll need to inject our `InventoryService` through the constructor of our `InventoryComponent` to have access to the methods.

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/85208cf868f89ee4daa551e3ff121cf4 %}

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/d82eef0362c3cb1fb0362ea6537e6da5 %}

_I know there are better ways to update a count in a service and bind it to a component (such as using an Observable). This is just to illustrate a concept._

Whenever we bring in dependencies into components, we should always make sure that those services are tested first so that they behave as expected. Our `InventoryService` was tested in the previous article so it's safe for us to use it now.

The logic for this component is beyond simple but there's still a key concept of testing that it covers. We don't need to re-test the service code in this component, but _we do need to make sure that it is called when needed_. 

Let's focus on the component test and run through what the auto-generate code means. Remember we can focus on a test suite by using `fdescribe` (focused describe) and focus on a single test using `fit` (focused it).

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/b1bb504a8f7cf79d16ec5d7efe655a00 %}

We see that an instance of the component is created and a `fixture` is set up to house the component instance. This also gives us access to component life cycle methods and a DOM that we can use during our unit tests. You can read more about fixtures [here](https://angular.io/api/core/testing/ComponentFixture).

`TestBed.createComponent(InventoryComponent)` instantiates the component, which means that the constructor code is immediately executed along with all the component life cycle hooks implemented by that component. `fixture.detectChanges()` is responsible for any updates made to the component. It syncs any component variables bound to the DOM. On the first time that it is is run, it runs `ngOnChanges()` and `ngOnInit()` (Thanks [@LayZeeDK](https://dev.to/layzee) for the correction! :heart:). You can read more about [ngOnChanges](https://angular.io/api/core/OnChanges) and [ngOnInit](https://angular.io/api/core/OnInit) on the docs.

If the component has any dependencies, those are instantiated as well, meaning that their constructor functions are immediately executed. This breaks our concept of unit testing since multiple pieces of code are being brought into this one unit test suite. These dependencies need to be **mocked**.

## Mocking Dependencies

Typically when mocking a dependency, a dummy class is provided with many of the same methods as the original. These methods do not provide functionality, but they may just return predictable values that we can use for testing purposes. 

For example, you may want to mock network calls, return a known value and see if your components and services behave as they should. You may want to willingly return errors from mock services to see if your application handles errors gracefully. You can even mock Angular features such as the Router.

All this is necessary to isolate the piece of code to be tested. Otherwise, when a test fails we won't know if a dependency or the code in question caused it, which leads to many wasted hours and a poorly designed codebase.

Let's create a `MockInventoryService` and supply that in place of our  `InventoryService` in the component unit test. We know that the service is already tested, so if any tests fail, the bad code *has* to be in our component.

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/02e52a5c880567cd03d5bdd9b1583346 %}

Notice how our `incrementCount` and `decrementCount` are basically No-ops. Because the logic of this service is so simple, we just want to test if these functions are going to be called in our component. If the methods of the mock service are called in the unit test then it is safe to assume that the actual methods of the real service are called in the component during normal execution. 

We need to tell our component unit test to replace the injected `InventoryService` with the `MockInventoryService`. This is done in the `providers` array in the module setup of the component test as follows: 

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/a4cf290a31724adee7d985bbca6d9b7c %}

Now, whenever `incrementCount` is called in the component during the unit test, the method from the mock service will be invoked instead.

## Writing our tests

In order for us to tell when a method has been called on a service or not, we need to spy on that method. Jasmine can tell us when a function has been invoked, what the parameters were and what the return value was. 
This is useful for us to test our component. 

When `increment()` is called in the component, we expect that `incrementCount()` is called in the service. Similarly, when `decrement()` is called in the component, we expect that `decrementCount()` is called in the service. Let's set up our Jasmine spies and write our tests.

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/f5b73e757aad5ae1d298fc5bcef8d4cd %}

We set up our spies at the very beginning of our test suite and instantiated them after we got a hold of the service from `TestBed.inject`. 

`expect(incrementSpy).toHaveBeenCalled()` tests whether or not the function being spied upon was called during the test.

 ![Jasmine Spies Tests Passing](https://dev-to-uploads.s3.amazonaws.com/i/hvxog3zdtzqh91c1erog.png)

## Conclusion

In this article, we covered the following:
 - How to inject dependencies into components
 - The auto-generated unit test of a component
 - Producing a mock service
 - Providing the mock service to the component
 - Spying on functions inside that service.

Hopefully this article was useful to you. There are lots more to learn about mocking and test strategies in Angular and I aim to cover them all.
Thanks a lot for reading!