Testing Selectors page
Learn how to write unit tests for NgRx Selectors.
Quick Start: You can checkout this branch to get your codebase ready to work on this section.
Overview
Verify
selectUserId()
Selector returnsuserId
.Verify
selectUsername()
Selector returnsusername
.Verify
selectToken()
Selector returnstoken
.
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
When testing Selectors, we will mock the Login State and verify that each Selector returns the expected result. Like testing the Reducer, we don’t need a TestBed
since there shouldn’t be any dependencies when dealing with Selectors since they are pure functions.
Update login.selectors.spec.ts
We will walk through updating src/app/store/login/login.selectors.spec.ts
to run tests for your Selectors.
Verify Feature State Selector
This test has already been written in a previous section where we were testing the Reducer (Testing Reducers):
src/app/store/login/login.selectors.spec.ts
// src/app/store/login/login.selectors.spec.ts
import * as fromLogin from './login.reducer';
import { selectLoginState } from './login.selectors';
describe('Login Selectors', () => {
it('should select the feature state', () => {
const result = selectLoginState({
[fromLogin.loginFeatureKey]: {
...fromLogin.initialState,
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
});
expect(result).toEqual({
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
});
});
});
The Feature State Selector is unique to all other Selectors for a Feature Store because this Selector is the source for all other Selectors for a Feature Store and is the only Selector generated using the createFeatureSelector()
helper. Testing the Feature State Selector should be straight-forward since it should return all of the Login State.
Verify selectUserId
Selector Returns userId
To properly test Selectors in a way that ensures that each Selector does not rely on mutations on the Login State, we will use beforeEach()
to create a fresh Login State per test:
src/app/store/login/login.selectors.spec.ts
// src/app/store/login/login.selectors.spec.ts
import * as fromLogin from './login.reducer';
import { selectLoginState } from './login.selectors';
describe('Login Selectors', () => {
let state: fromLogin.LoginPartialState;
beforeEach(() => {
state = {
[fromLogin.loginFeatureKey]: {
...fromLogin.initialState,
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
};
});
it('should select the feature state', () => {
const result = selectLoginState(state);
expect(result).toEqual({
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
});
});
});
Now we can write our tests for our Selectors. The first Selector we will test is the selectUserId()
Selector:
src/app/store/login/login.selectors.spec.ts
// src/app/store/login/login.selectors.spec.ts
import * as fromLogin from './login.reducer';
import {
selectLoginState,
selectUserId,
} from './login.selectors';
describe('Login Selectors', () => {
let state: fromLogin.LoginPartialState;
beforeEach(() => {
state = {
[fromLogin.loginFeatureKey]: {
...fromLogin.initialState,
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
};
});
it('should select the feature state', () => {
const result = selectLoginState(state);
expect(result).toEqual({
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
});
});
describe('selectUserId', () => {
it('should return userId from login state', () => {
const result = selectUserId(state);
expect(result).toBe('some-user-id');
});
});
});
Verify selectUsername()
Selector Returns username
src/app/store/login/login.selectors.spec.ts
// src/app/store/login/login.selectors.spec.ts
import * as fromLogin from './login.reducer';
import {
selectLoginState,
selectUserId,
selectUsername,
} from './login.selectors';
describe('Login Selectors', () => {
let state: fromLogin.LoginPartialState;
beforeEach(() => {
state = {
[fromLogin.loginFeatureKey]: {
...fromLogin.initialState,
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
};
});
it('should select the feature state', () => {
const result = selectLoginState(state);
expect(result).toEqual({
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
});
});
describe('selectUserId', () => {
it('should return userId from login state', () => {
const result = selectUserId(state);
expect(result).toBe('some-user-id');
});
});
describe('selectUsername', () => {
it('should return username from login state', () => {
const result = selectUsername(state);
expect(result).toBe('some-username');
});
});
});
Verify selectToken()
Selector Returns token
src/app/store/login/login.selectors.spec.ts
// src/app/store/login/login.selectors.spec.ts
import * as fromLogin from './login.reducer';
import {
selectLoginState,
selectToken,
selectUserId,
selectUsername,
} from './login.selectors';
describe('Login Selectors', () => {
let state: fromLogin.LoginPartialState;
beforeEach(() => {
state = {
[fromLogin.loginFeatureKey]: {
...fromLogin.initialState,
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
};
});
it('should select the feature state', () => {
const result = selectLoginState(state);
expect(result).toEqual({
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
});
});
describe('selectUserId', () => {
it('should return userId from login state', () => {
const result = selectUserId(state);
expect(result).toBe('some-user-id');
});
});
describe('selectUsername', () => {
it('should return username from login state', () => {
const result = selectUsername(state);
expect(result).toBe('some-username');
});
});
describe('selectToken', () => {
it('should return token from login state', () => {
const result = selectToken(state);
expect(result).toBe('some-token');
});
});
});
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/store/login/login.selectors.spec.ts
// src/app/store/login/login.selectors.spec.ts
import * as fromLogin from './login.reducer';
import {
selectLoginState,
selectToken,
selectUserId,
selectUsername,
} from './login.selectors';
describe('Login Selectors', () => {
let state: fromLogin.LoginPartialState;
beforeEach(() => {
state = {
[fromLogin.loginFeatureKey]: {
...fromLogin.initialState,
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
},
};
});
it('should select the feature state', () => {
const result = selectLoginState(state);
expect(result).toEqual({
userId: 'some-user-id',
username: 'some-username',
token: 'some-token',
});
});
describe('selectUserId', () => {
it('should return userId from login state', () => {
const result = selectUserId(state);
expect(result).toBe('some-user-id');
});
});
describe('selectUsername', () => {
it('should return username from login state', () => {
const result = selectUsername(state);
expect(result).toBe('some-username');
});
});
describe('selectToken', () => {
it('should return token from login state', () => {
const result = selectToken(state);
expect(result).toBe('some-token');
});
});
});
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-selectors