In this article, I will provide a collection of some important statements used to unit test angular components. You can use any of the following examples directly in your project, or you may prefer to extract some of them into separate helper functions and reuse them all over your project. This article covers testing the following scenarios:
- Text interpolation
- User Input Value Change
- Clicking HTML Element
- Access Child (nested) Component
- Content projection
- Component inputs and outputs
- Component Dependencies
For this purpose lets assume we have the following simple example component generated using Angular CLI ng g c ExampleComponent:
A very basic component consists of one input header
and one property name
displayed in the template using a direct interpolation, a form with one input field and a submit button and one output nameChange
which will emit an event when the user submits the form.
When you create the above component using Angular CLI you will get automatically a unit test file in the same directory as your component. All the next sections in this article are based on this file, especially the fixture object let fixture: ComponentFixture;
. If you don't use Angular CLI to generate your component file, you may copy the above file in your project, and replace ExampleComponent
with your component class name.
Text interpolation:
Here we make sure that our component will bind the correct values in the template. Don't forget to call fixture.detectChanges()
which forces the TestBed to perform data binding and update the view.
User Input Value Change:
Here we test that the user interaction with the text input is reflected correctly into our component class. Notice here the use of fakeAsync and tick, because the forms binding involves some asynchronous execution.
Clicking HTML Element:
Access Child (nested) Component:
Lets assume that our component contains a nested child component:
<app-nested-component></app-nested-component>
You can access the child component and interact it as the following:
Content projection:
Testing content projection is not straightforward, to do so we need to add a wrapper component around the component being tested and use this wrapper component to pass content through projection. Let's add the following projected content to the view of our component
<div class="projected-content>
<ng-content select="[description]"></ng-content>
</div>
And we can test is by adding a wrapper ExampleWrapperComponent
as the following:
Component inputs and outputs:
You can test component input similar to any normal component property. on the other hand the outputs can be spied on and check if it emits the correct value.
Component Dependencies:
Components usually have dependencies (services) that help the component to function correctly, and the component needs to interact with these dependencies. When testing a component we need to provide our tests with those dependencies in order to run correctly. Here we need to distinguish between two way of providing a dependency:
Dependencies provided in the root injector:
When the component has a dependency on a service that is provided in the root injector, you need to provide this service to the TestBed configuration to be available to the component while running the tests:
Notice that we are using a mock service here since it is easier and safer to interact with. After that, you will be able to access that service in your tests by calling the inject
method of the TestBed
.
Dependencies provided in the component injector:
When you have a dependency provided in your component, you can not access it using the TestBed, since it will be available only on the component level of the injection tree. In this case, we need to override the component providers to provide this dependency, and then you can use the component injector to access it.
Do you have or need a specific testing scenario that is not covered by this article? Feel free add it in the comments sections and we will add a use case for you :)
Top comments (6)
I have a question about the purpose of tick(); ?
tick is just a simulation of passage of time, for example you can use tick(2000) to simulate a passing of 2 seconds you can check more about it here angular.io/api/core/testing/tick
Did you tried this package: npmjs.com/package/ng-mocks before?
Thank you
And another question about {provide: ExampleService, useClass: ExampleServiceMock} : ExampleServiceMock it's implemneted from scratch ? or we can use mock test library
It is up to you, you can create a new class that matches the real ExampleService and mock the members (properties and methods). you can use a mocking library if you want, but anyway it is up to you, depending on the logic of the service, to decide how the mock service should behave