How to Use Spectator to Test your Angular Components
How to Use Spectator to Test your Angular Components
A how-to guide using a sample app.
Source: Unsplash
I’ve been usingSpectatorto test my Angular apps. It’s my go-to test tool in my current company and personal projects. Its features are enough to explain why.
Spectator’s features from github.com/ngneat/spectator.
In this post, I will walk you through how to use Spectator to test an Angular app with a component using a service. There is good documentation around how to use Spectator. However, this post will focus on using it in a sample Angular app. Hopefully, this will help you to quickly start using Spectator tests in your existing app. My post assumes that you havesomeexperience building Angular apps with RxJS.
The sample app
We will add Spectator tests to a modified version of a sample app from one of my previous blog postsHow to Automatically Unsubscribe Multiple Observables in Angular.
To quickly recap.
How it looks like
We have a web app with two GitHub search boxes, one for searching repositories and another to search for users.
Screenshot of the sample web app by the author.
The search results are then displayed below the search boxes.
Screenshot of the sample web app by the author.
How it works under the hood
Let’s do a quick recap of how our sample app works under the hood.
Our web app will call the GitHub Search API after we enter a text in any of the search boxes. To achieve this, we need to subscribe to our formControls valueChanges observable.
AppComponent and GithubService.
Source code example on GitHub by the author.
The line
this.searchSubject$.next(searchString)
“emits” the signal to call the GitHub search API.
Source code example on GitHub by the author.
Our results will be rendered in our template.
<div *ngFor="let result of results$ | async">
<div>
<a [href]="result.html_url" target="_blank">{{ result.name || result.login }}</a>
</div>
</div>
Installing spectator
Installation is straightforward. From their docs:
NPM
npm install @ngneat/spectator --save-dev
Yarn
yarn add @ngneat/spectator --dev
Setting up spectator
In our unit test fileapp.component.spec.ts
, we will import our component and spectator dependencies.
Setup the component factory
Our tests will needSpectator
andcreateComponentFactory
from the Spectator package.
import { AppComponent } from './app.component';
import { Spectator, createComponentFactory } from '@ngneat/spectator';
Initialize our component factory usingcreateComponentFactory
.
let spectator: Spectator<AppComponent>;
const createComponent = createComponentFactory(AppComponent);
Create our component factory before each test.
beforeEach(() => spectator = createComponent());
Import dependencies
OurAppComponent
depends onGithubService
to call GitHub’s API.
import { GithbService } from './github.service';
DeclareGithubService
as a provider when we create our spectator component.
const createComponent = createComponentFactory({
component: AppComponent,
providers: [
{
provide: GithubService,
useValue: {}
}
],
});
We will get a similar error message in the screenshot below if we don’t import the component’s dependencies like ourGithubService
above.
Error message when there’s a missing dependency in the unit test file.
Verify that it works
We can verify that our basic setup is working by adding a test that will check if our component was created successfully.
it('should create', () => {
expect(spectator.component).toBeTruthy();
});
Our basic setup will look like the following. We can now run a test that checks if a spectator component is successfully created.
Source code example by the author.
Use init methods for unit testing
My sample app initializes all the observables inngOnInit
. Let’s rewrite this first to make our app easier to test.
Source code example on GitHub by the author.
We will move ourformControl
observable subscriptionssearchUsersFormControl
andsearchRepoesFormControl
into separate methodsinitSearchUsers()
andinitSearchRepos()
, respectively.
Below is for search users, it will be similar for search repositories.
this.initSearchUsers() will be called from ngOnInit().
Our methodsinitSearchUsers()
andinitSearchRepos()
will be called fromngOnInit()
.
ngOnInit(): void {
//...
this.initSearchUsers();
this.initSearchRepos();
}
Add tests to our component
Our component is easier to test after we have introducedinitSearchUsers()
andinitSearchRepos()
to break down ourngOnInit
declarations. The tests will be similar for both methods. I will add tests forinitSearchUsers()
only as an example.
Assert emitted values from the observables
When testing observables, sometimes we will need to declare the assertions first within thesubscribe
callback before we even call the method that we want to test.
To test thatinitSearchUsers()
is working properly, two assertions must be satisfied:
- The search value that’s being typed as an input to
searchUsersFormControl
should trigger thesearchSubject$
observable. If the search value is empty, then it shouldn’t trigger thesearchSubject$
observable. - Search results are emitted from the
results$
observable.
We will subscribe tosearchSubject$
to check if the emitted value is correct. Whatever the search value that’s being typed as an input tosearchUsersFormControl
should trigger thesearchSubject$
observable:
spectator.component.searchSubject$.subscribe(res => {
expect(res).toBe(searchTerm);
done();
});
We will do the same thing with theresults$
observable. Our component will return the search results based on the search type: repositories or users.
For example, if we have a mock that returns 100 results for user search.
spectator.component.results$.subscribe(res => {
expect(res.total_count).toBe(100);
done();
});
Then we should expect our user search’s total result to be 100.
Mock the service
After our assertions are set up in our observables, let’s mock our service’s search results. Mocking services using Spectator is straightforward.
Start by injecting the service.
const githubService = spectator.inject(GithubService);
Then, assign the mock return value.
githubService.searchUsers.andReturn(of({
total_count: 100,
}));
Call the methods
By having our assertions and mock in place, we are now ready to call the methods that we want to test. This is whereinitSearchUsers
andinitSearchRepos
come in handy.
Initialize oursearchUsersFormControl
observable.
spectator.component.initSearchUsers();
And finally set the value of oursearchUsersFormControl
. As if a user is typing a search text into our search input.
spectator.component.searchUsersFormControl.setValue(searchTerm);
Here is our complete test file.
Try it yourself
The code is available on GitHub.
After cloning the repo, install the packages.
npm install
And run the tests.
ng test
You should get a similar output below.
If you like this story, you might also enjoy my other stories about Angular:
More content atPlainEnglish.io. Sign up for ourfree weekly newsletter. Follow us onTwitterandLinkedIn. Join ourcommunity Discord.