---
title: Bitovi Academy - Testing Reducers
description: Learn how to write unit tests for NgRx Reducers.
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting?hsLang=en-us)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting?hsLang=en-us)
- ![Project management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project management](https://www.bitovi.com/services/agile-project-management-consulting?hsLang=en-us)
- ![Product design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product design](https://www.bitovi.com/services/product-design-consulting?hsLang=en-us)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services?hsLang=en-us)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting?hsLang=en-us)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers?hsLang=en-us)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting?hsLang=en-us)
- [Design Systems](https://www.bitovi.com/services/axure-figma-migration?hsLang=en-us)
- [React](https://www.bitovi.com/services/frontend/react-consulting?hsLang=en-us)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting?hsLang=en-us)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native?hsLang=en-us)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting-1?hsLang=en-us)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study?hsLang=en-us)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign?hsLang=en-us)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work?hsLang=en-us)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source?hsLang=en-us)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about perfecting software delivery
  
  ](https://bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships?hsLang=en-us)
- [![Academy](https://www.bitovi.com/hubfs/limbo/icons/icon-academy.svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy/?hsLang=en-us)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse our tools or contribute to our open source mission.
  
  ](https://www.bitovi.com/open-source?hsLang=en-us)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

- [ Advanced JavaScript ](https://www.bitovi.com/academy/learn-advanced-javascript.html?hsLang=en-us)
- [ DOM and jQuery ](https://www.bitovi.com/academy/learn-dom-jquery.html?hsLang=en-us)
- [ Debug JavaScript ](https://www.bitovi.com/academy/learn-to-debug-javascript.html?hsLang=en-us)
- [ Learn TypeScript ](https://www.bitovi.com/academy/learn-typescript.html?hsLang=en-us)
- [ Learn React Native ](https://www.bitovi.com/academy/learn-react-native.html?hsLang=en-us)
- [ Learn Web Components ](https://www.bitovi.com/academy/learn-web-components.html?hsLang=en-us)
- [ Agile Program Management With Jira ](https://www.bitovi.com/academy/learn-agile-program-management-with-jira.html?hsLang=en-us)
- [ Learn RxJS ](https://www.bitovi.com/academy/learn-rxjs.html?hsLang=en-us)
- [ Giving a Training ](https://www.bitovi.com/academy/training.html?hsLang=en-us)
- [ Learn Technology Consulting ](https://www.bitovi.com/academy/learn-technology-consulting.html?hsLang=en-us)
- [ Learn React ](https://www.bitovi.com/academy/learn-react.html?hsLang=en-us)
- [ Learn NgRx ](https://www.bitovi.com/academy/learn-ngrx.html?hsLang=en-us) 
    - [ Why NgRx ](https://www.bitovi.com/academy/learn-ngrx/why-ngrx.html?hsLang=en-us)
    - [ Getting Started ](https://www.bitovi.com/academy/learn-ngrx/getting-started.html?hsLang=en-us)
    - [ Installing NgRx ](https://www.bitovi.com/academy/learn-ngrx/ngrx-init.html?hsLang=en-us)
    - [ Creating Actions ](https://www.bitovi.com/academy/learn-ngrx/create-actions.html?hsLang=en-us)
    - [ Dispatching Actions ](https://www.bitovi.com/academy/learn-ngrx/dispatch-actions.html?hsLang=en-us)
    - [ Testing Actions ](https://www.bitovi.com/academy/learn-ngrx/test-actions.html?hsLang=en-us)
    - [ Create an API Effect ](https://www.bitovi.com/academy/learn-ngrx/create-api-effects.html?hsLang=en-us)
    - [ Testing API Effects ](https://www.bitovi.com/academy/learn-ngrx/test-api-effects.html?hsLang=en-us)
    - [ Creating Redirect Effects ](https://www.bitovi.com/academy/learn-ngrx/create-redirect-effects.html?hsLang=en-us)
    - [ Testing Redirect Effects ](https://www.bitovi.com/academy/learn-ngrx/test-redirect-effects.html?hsLang=en-us)
    - [ Create a Reducer ](https://www.bitovi.com/academy/learn-ngrx/create-reducer.html?hsLang=en-us)
    - [ Testing Reducers ](https://www.bitovi.com/academy/learn-ngrx/test-reducer.html?hsLang=en-us)
    - [ Creating Selectors ](https://www.bitovi.com/academy/learn-ngrx/create-selectors.html?hsLang=en-us)
    - [ Testing Selectors ](https://www.bitovi.com/academy/learn-ngrx/test-selectors.html?hsLang=en-us)
    - [ Using Selectors ](https://www.bitovi.com/academy/learn-ngrx/use-selectors.html?hsLang=en-us)
    - [ Testing Selector Use ](https://www.bitovi.com/academy/learn-ngrx/test-used-selectors.html?hsLang=en-us)
- [ Learn Graphql ](https://www.bitovi.com/academy/learn-graphql.html?hsLang=en-us)
- [ Learn Docker ](https://www.bitovi.com/academy/learn-docker.html?hsLang=en-us)
- [ Learn CanJS ](https://www.bitovi.com/academy/learn-canjs.html?hsLang=en-us)
- [ Learn Angular ](https://www.bitovi.com/academy/learn-angular.html?hsLang=en-us)
- [ Advanced TypeScript ](https://www.bitovi.com/academy/advanced-typescript.html?hsLang=en-us)

[View More Courses](https://www.bitovi.com/academy/#courses) [ ]

# Testing Reducers `page`

Learn how to write unit tests for NgRx Reducers.

> **Quick Start**: You can checkout [this branch](https://github.com/bitovi/angular-ngrx-chat/tree/create-reducer) to get your codebase ready to work on this section.

## Overview

1. Verify Login State updates properly when the `LoginActions.loginSuccess` Action dispatches.
2. Verify Login State resets properly when the `LoginActions.logoutSuccess` Action dispatches.

## 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 a Reducer, we will test each of its `on()` handlers by verifying each pure function returns the expected value given an Action and a Login State (typically the `initialState`). Unlike the previous testing sections, when testing the Reducer, we don’t need a `TestBed` since there shouldn’t be any dependencies when dealing with the Reducer.

## Update `login.selectors.spec.ts`

Before we can test our Reducer, our tests involving our Selectors are failing. This is because we’ve changed the shape and initial value of our Login State. To continue, we need to update them. Copy the following code to replace the contents of `src/app/store/login/login.selectors.spec.ts`:

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',
    });
  });
});

```

```
// 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',
    });
  });
});

```

We will go over these changes in the upcoming section where we go over testing Selectors ([Testing Selectors](https://www.bitovi.com/academy/learn-ngrx/test-selectors.html?hsLang=en-us)). For now after you update `src/app/store/login/login.selectors.spec.ts`, all of your tests should pass.

## Update `login.reducer.spec.ts`

We will walk through updating `src/app/store/login/login.reducer.spec.ts` to run tests for your Reducer.

### Verify Login State Updates Properly When the `LoginActions.loginSuccess` Action Dispatches

To test the `on()` handler associated with `LoginActions.loginSuccess`, we will define 3 values:

1. Expected Login State after Reducer handles Action.
2. The Action we are testing.
3. The generated Login State after passing `initialState` and Action to `reducer()`.

src/app/store/login/login.reducer.spec.ts

```
// src/app/store/login/login.reducer.spec.ts

import { reducer, initialState, State } from './login.reducer';
import * as LoginActions from './login.actions';

describe('Login Reducer', () => {
  describe('loginSuccess action', () => {
    it('should update the state in an immutable way', () => {
      // Expectation of new state
      const expectedState: State = {
        ...initialState,
        userId: 'some-user-id',
        username: 'some-username',
        token: 'some-token',
      };

      const action = LoginActions.loginSuccess({
        userId: 'some-user-id',
        username: 'some-username',
        token: 'some-token',
      });

      const state = reducer({ ...initialState }, action);
    });
  });

  describe('an unknown action', () => {
    it('should return the previous state', () => {
      const action = {} as any;

      const result = reducer(initialState, action);

      expect(result).toBe(initialState);
    });
  });
});

```

Next we will add our expectations to verify that our expected Login State matches the generated Login State. When we do this, we will also verify that we do this in an immutable way to maintain an [immutable data structure](https://ngrx.io/guide/store/why#immutability-and-performance):

src/app/store/login/login.reducer.spec.ts

```
// src/app/store/login/login.reducer.spec.ts

import { reducer, initialState, State } from './login.reducer';
import * as LoginActions from './login.actions';

describe('Login Reducer', () => {
  describe('loginSuccess action', () => {
    it('should update the state in an immutable way', () => {
      // Expectation of new state
      const expectedState: State = {
        ...initialState,
        userId: 'some-user-id',
        username: 'some-username',
        token: 'some-token',
      };

      const action = LoginActions.loginSuccess({
        userId: 'some-user-id',
        username: 'some-username',
        token: 'some-token',
      });

      const state = reducer({ ...initialState }, action);

      // Compare new state
      expect(state).toEqual(expectedState);

      // Check for immutability
      expect(state).not.toBe(expectedState);
    });
  });

  describe('an unknown action', () => {
    it('should return the previous state', () => {
      const action = {} as any;

      const result = reducer(initialState, action);

      expect(result).toBe(initialState);
    });
  });
});

```

### Verify Login State Resets Properly When the `LoginActions.logoutSuccess` Action Dispatches

We will do the same when testing the `on()` handler associated with `LoginActions.logoutSuccess`, but instead of defining an expected Login State, we will just use the `initialState` since it already is our expected Login State. And instead of passing our `initialState` to the `reducer()`, we will pass some updated Login State instead to ensure that all values are reset properly:

src/app/store/login/login.reducer.spec.ts

```
// src/app/store/login/login.reducer.spec.ts

import { reducer, initialState, State } from './login.reducer';
import * as LoginActions from './login.actions';

describe('Login Reducer', () => {
  describe('loginSuccess action', () => {
    it('should update the state in an immutable way', () => {
      // Expectation of new state
      const expectedState: State = {
        ...initialState,
        userId: 'some-user-id',
        username: 'some-username',
        token: 'some-token',
      };

      const action = LoginActions.loginSuccess({
        userId: 'some-user-id',
        username: 'some-username',
        token: 'some-token',
      });

      const state = reducer({ ...initialState }, action);

      // Compare new state
      expect(state).toEqual(expectedState);

      // Check for immutability
      expect(state).not.toBe(expectedState);
    });
  });

  describe('logoutSuccess action', () => {
    it('should reset LoginState to initialState', () => {
      const action = LoginActions.logoutSuccess();

      const state = reducer(
        {
          ...initialState,
          userId: 'some-user-id',
          username: 'some-username',
          token: 'some-token',
        },
        action
      );

      // Compare new state
      expect(state).toEqual(initialState);

      // Check for immutability
      expect(state).not.toBe(initialState);
    });
  });

  describe('an unknown action', () => {
    it('should return the previous state', () => {
      const action = {} as any;

      const result = reducer(initialState, action);

      // Shouldn’t update state at all
      expect(result).toBe(initialState);
    });
  });
});

```

## 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.reducer.spec.ts

```
// src/app/store/login/login.reducer.spec.ts

import { reducer, initialState, State } from './login.reducer';
import * as LoginActions from './login.actions';

describe('Login Reducer', () => {
  describe('loginSuccess action', () => {
    it('should update the state in an immutable way', () => {
      // Expectation of new state
      const expectedState: State = {
        ...initialState,
        userId: 'some-user-id',
        username: 'some-username',
        token: 'some-token',
      };

      const action = LoginActions.loginSuccess({
        userId: 'some-user-id',
        username: 'some-username',
        token: 'some-token',
      });

      const state = reducer({ ...initialState }, action);

      // Compare new state
      expect(state).toEqual(expectedState);

      // Check for immutability
      expect(state).not.toBe(expectedState);
    });
  });

  describe('logoutSuccess action', () => {
    it('should reset LoginState to initialState', () => {
      const action = LoginActions.logoutSuccess();

      const state = reducer(
        {
          ...initialState,
          userId: 'some-user-id',
          username: 'some-username',
          token: 'some-token',
        },
        action
      );

      // Compare new state
      expect(state).toEqual(initialState);

      // Check for immutability
      expect(state).not.toBe(initialState);
    });
  });

  describe('an unknown action', () => {
    it('should return the previous state', () => {
      const action = {} as any;

      const result = reducer(initialState, action);

      // Shouldn’t update state at all
      expect(result).toBe(initialState);
    });
  });
});

```

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',
    });
  });
});

```

> **Wrap-up**: By the end of this section, your code should match [this branch](https://github.com/bitovi/angular-ngrx-chat/tree/test-reducer). You can also compare the [code changes for our solution to this section](https://github.com/bitovi/angular-ngrx-chat/compare/create-reducer...test-reducer) on GitHub or you can use the following command in your terminal:

```
git diff origin/test-reducer
```

###### On this page...

###### Get help

If you need [help](https://discord.gg/J7ejFsZnJ4) with an exercise, please reach out to us on [Discord](https://discord.gg/J7ejFsZnJ4)

If you find a bug, please [create an issue](https://github.com/bitovi/academy/issues/new) or email [contact@bitovi.com](mailto:contact@bitovi.com?Subject=Training%20Suggestion)

## Join the Bitovi Newsletter

 Do you want to receive updates about new Bitovi Academy courses, Bitovi open source projects, and dev community news? If so, please provide your email. We won’t spam you or give it away for any reason!  
*By providing your email you agree to receive communications from Bitovi*

![](https://bitovi.github.io/academy/static/img/close.svg)

```json
{
     "@context": "http://schema.org",
     "@type": "Organization",
     "mainEntityOfPage":{
          "@type":"WebPage",
          "@id":"https://www.bitovi.com/academy/learn-ngrx/test-reducer.html",
          "description": "Learn how to write unit tests for NgRx Reducers. 
"
     },
     "url": "http://bitovi.com",
     "logo": "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
     "image": "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
     "name": "Bitovi Web App Consulting",
     "alternateName": "Bitovi",
      "naics": "541511",
      "description": "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
     "address": {
          "@type": "PostalAddress",
          "streetAddress": "1134 Pine Tree Lane ",
          "addressLocality": "Libertyville",
          "addressRegion": "IL",
          "addressCountry": "United States",
          "postalCode": "60048"
     },
     "areaServed": {
          "@type": "GeoCircle",
          "geoMidpoint": {
               "@type": "GeoCoordinates",
               "latitude": "41.8781",
               "longitude": "87.6298"
          },
          "geoRadius": "5000 km"
     },
     "telephone": "312-620-0386",
     "email": "contact@bitovi.com",
     "sameAs": [
          "https://www.facebook.com/BitoviLLC/",
          "https://twitter.com/bitovi",
          "https://www.linkedin.com/company/bitovi"
     ]
}
```