Testing Angular Components with Stub Services and Spies in Jasmine
In my previous article, I explained the fundamentals of Unit testing and how Karma-Jasmine works together to achieve this goal. I also explained some basic testing in another article of mine.
In this article we’ll focus on something more interesting. We’ll try to test a component which has a service injected into it. You can get the component specific source code from my GitHub repository. Take away points from this article would be:
- Testing component by isolating it from external dependencies such as services and using :
2. Getting to know spies and how it can prove to be a helpful tool for Unit Testing. We’ll be using below spies whose details can be found on Jasmine page :
- spyOn
- toHaveBeenCalled
- toHaveBeenCalledWith
- and.callThrough
- and.returnValue
Scenario: We’ll be creating a component named students.component.ts. This component will handle below activities:
- Get the list of students from a service.
- Show details of students.
- The Component shows details of the clicked user by making an API call.
To begin with, we create the component using below command which would also create a spec file associated with it.
ng g c students
I’ll not get into the code for developing the feature component and creating the service. It’s very straightforward and can be understood by looking at the source code.
We’ll be testing:
- user_list is populated with some data.
- Selected user detail is rendered on the HTML.
- Testing the proper sequence of function calls using a spy.
- Handling Error response from service.
As soon as you will run
ng t
you’ll be greeted with an error message of StaticInjectorError:
To fix this you need to import HttpClientModule in TestBed because we are using http calls from our services, which has been injected into the component.
“user_list” is populated with some data
Let’s go ahead and check user_list and use it to test further:
You’ll see that the test case is failing, BUT WHYYYYY?
It’s because we are using “getUserList()” to make Http call and populate the variable using our service file as below :
The core essence of Unit testing is to isolate component from other services/dependencies and create our own stubs to mock the service behavior.
Let’s create our own stub to replicate what students.service.ts used to do for students.component.ts using useClass.
In simple words, useClass replaces the actual class instance (StudentsService) , injected as object property “provide”, to the one which is being injected as object property “useClass” (StudentsServiceStub).
{ provide: StudentsService, useClass: StudentsServiceStub }
Bingo! It works.
Selected user detail is rendered on the HTML
We can access the HTML using :
fixture.debugElement.query(By.css('.users-div')).nativeElement
We can also replicate click event and test further HTML behavior:
Note that we had to call fixture.detectChanges() to manually trigger “Change Detection” and get results on the HTML page
Testing proper sequence of function calls using spy
We can test which functions were called and with what parameters were they called using:
- .and.callThrough() [it allows function calls to go through and spy on it]
- toHaveBeenCalled() [checks if the spied function was called]
- toHaveBeenCalledWith() [checks if the spied function was called with expected parameter]
Handling Error response from service
We can override a function behavior using “spyOn” and can return custom value using .and.returnValue().
throwError comes from RxJS and provide error response which can be imported as
import { throwError } from 'rxjs';
Here are the complete unit tests which we have written in my GitHub repository.
Cheers !!