Testing Selector Use page
Learn how to write unit tests for a Component making use of NgRx Selectors.
Quick Start: You can checkout this branch to get your codebase ready to work on this section.
Overview
Verify
DashboardComponent
’susername$
member extractsusername
from Login State.Verify
AuthenticationGuard
’suserId$
member extractsuserId
from Login State.
Running Tests
To run unit tests in your project, you can either use the test
npm script, or the ng test
command:
npm run test
# or
ng test --watch
The --watch
switch will rerun your tests whenever a code file changes. You can skip it to just run all tests once.
Description
In this section, we will write unit tests involving Selectors. Whenever we use Selectors in our Components, Services, etc. we can use provideMockStore()
to mock the behavior of our Selectors.
Update dashboard.component.spec.ts
We will walk through updating src/app/dashboard/dashboard.component.spec.ts
to run tests for your Sectors.
Updating our TestBed
When testing our use of Selectors, we can specify a configuration to our MockStore
. It is possible to pass an initialState
option which will influence our Selectors:
src/app/dashboard/dashboard.component.spec.ts
// src/app/dashboard/dashboard.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import * as LoginActions from '../store/login/login.actions';
import * as fromLogin from '../store/login/login.reducer';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
let store: MockStore<fromLogin.LoginPartialState>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [DashboardComponent],
providers: [
MockStore,
provideMockStore({
initialState: {
[fromLogin.loginFeatureKey]: {
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
},
}),
],
}).compileComponents();
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
store = TestBed.inject(MockStore);
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('logout()', () => {
it('should dispatch logout action', () => {
const spy = spyOn(store, 'dispatch');
component.logout();
expect(spy).toHaveBeenCalledOnceWith(LoginActions.logout());
});
});
describe('username$', () => {
it('should get username from login state', () => {
// TODO: Verify username comes from login state
});
});
describe('userId$', () => {
it('should get userId from login state', () => {
// TODO: Verify userId comes from login state
});
});
});
When our updated configuration, we can now update the remaining TODO
s to verify the behavior of DashboardComponent.username$
and DashboardComponent.userId$
:
src/app/dashboard/dashboard.component.spec.ts
// src/app/dashboard/dashboard.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import * as LoginActions from '../store/login/login.actions';
import * as fromLogin from '../store/login/login.reducer';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
let store: MockStore<fromLogin.LoginPartialState>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [DashboardComponent],
providers: [
MockStore,
provideMockStore({
initialState: {
[fromLogin.loginFeatureKey]: {
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
},
}),
],
}).compileComponents();
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
store = TestBed.inject(MockStore);
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('logout()', () => {
it('should dispatch logout action', () => {
const spy = spyOn(store, 'dispatch');
component.logout();
expect(spy).toHaveBeenCalledOnceWith(LoginActions.logout());
});
});
describe('username$', () => {
it('should get username from login state', done => {
component.username$.subscribe(username => {
expect(username).toBe('some-username');
done();
});
});
});
describe('userId$', () => {
it('should get userId from login state', done => {
component.userId$.subscribe(userId => {
expect(userId).toBe('some-user-id');
done();
});
});
});
});
Final Result
At the end of this section, the following spec file(s) should be updated. After each spec file has been updated and all the tests have passed, this means that all the previous sections have been completed successfully:
src/app/dashboard/dashboard.component.spec.ts
// src/app/dashboard/dashboard.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import * as LoginActions from '../store/login/login.actions';
import * as fromLogin from '../store/login/login.reducer';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
let store: MockStore<fromLogin.LoginPartialState>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [DashboardComponent],
providers: [
MockStore,
provideMockStore({
initialState: {
[fromLogin.loginFeatureKey]: {
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
},
}),
],
}).compileComponents();
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
store = TestBed.inject(MockStore);
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('logout()', () => {
it('should dispatch logout action', () => {
const spy = spyOn(store, 'dispatch');
component.logout();
expect(spy).toHaveBeenCalledOnceWith(LoginActions.logout());
});
});
describe('username$', () => {
it('should get username from login state', done => {
component.username$.subscribe(username => {
expect(username).toBe('some-username');
done();
});
});
});
describe('userId$', () => {
it('should get userId from login state', done => {
component.userId$.subscribe(userId => {
expect(userId).toBe('some-user-id');
done();
});
});
});
});
Wrap-up: By the end of this section, your code should match this branch. You can also compare the code changes for our solution to this section on GitHub or you can use the following command in your terminal:
git diff origin/test-used-selectors