Unit tests in Jasmine disable dispatchers when NGXS store.reset is invoked

I am facing a challenge with an unusual behavior during the unit testing of my NGXS store using Jasmine.

Specifically, I am encountering issues when trying to test the DeleteAlerts action :

  @Action(DeleteAlerts)
  deleteAlerts(ctx: StateContext<AlertStateModel>, action: DeleteAlerts) {
    return this.alertService.deleteAlerts(action.alertIds).pipe(
      map(response => {
          if (response.ok) {
            action.alertIds.forEach(alertId => {
              ctx.setState(patch({
                alerts: removeItem<UniqueAlert>(alert => alert.alertIds.includes(alertId))
              }));
            });
          } else {
            throw new Error('Server failed to respond.');
          }
        }
      ));
  }

However, prior to testing the DeleteAlerts action, dummy data needs to be populated in the store.

To achieve this, I have created a mock :

  const alertsMock = new Alerts({
alerts: [new UniqueAlert({alertIds: ['test1']}),
  new UniqueAlert({alertIds: ['test2']}),
  new UniqueAlert({alertIds: ['test3']})]
  });

The structure of my store is as follows :

 export interface AlertStateModel {
  alerts: UniqueAlert[];
}

When attempting to populate the store with the mock data :

store.reset({alerts: alertsMock.alerts})

Unfortunately, in my testing scenario, the DeleteAlerts action does not get dispatched upon calling

store.dispatch(new DeleteAlerts(alertIds))

An intriguing observation is that the action gets dispatched successfully if I replace the store.reset method with a dispatch for GetAlerts, which fetches alerts from a mocked service :

The GetAlerts action :

  @Action(GetAlerts)
  public getAlerts(ctx: StateContext<AlertStateModel>) {
    return this.alertService.getAlerts().pipe(
      tap(fetchedAlerts => {
        ctx.setState({alerts: fetchedAlerts.alerts});
      })
    );
  }

The following test passes without any issues :

  it('should delete one alert from the store when DeleteAlerts is dispatched', () => {
    spyOn(alertService, 'getAlerts').and.returnValue(of(alertsMock));
    store.dispatch(new GetAlerts());
    spyOn(alertService, 'deleteAlerts').and.returnValue(of(new HttpResponse({status: 200})));
    store.dispatch(new DeleteAlerts(['test2']));
    store.selectOnce(AlertState).subscribe(data => {
      expect(data.alerts).toEqual(alertsMock.alerts.filter(alert => !alert.alertIds.includes('test2')));
    });
  });
});

Conversely, this test doesn't yield the expected results :

  it('should delete one alert from the store when DeleteAlerts is dispatched', () => {
    store.reset({alerts: alertsMock.alerts});
    spyOn(alertService, 'deleteAlerts').and.returnValue(of(new HttpResponse({status: 200})));
    store.dispatch(new DeleteAlerts(['test2']));
    store.selectOnce(AlertState).subscribe(data => {
      expect(data).toEqual(alertsMock.alerts.filter(alert => !alert.alertIds.includes('test2')));
    });
  });

In addition, it's worth noting that the expectation is set on data instead of data.alerts in the non-functional test. This aspect adds to my confusion as the selector should ideally return the state containing a nested alerts object.

Why are the two tests producing different outcomes, and why does the selector not return the anticipated object when utilizing store.reset for populating the store?

Regarding the inclusion of a nested alerts object within the alertsMock; this format mirrors the data structure returned by the alertService.

Answer №1

It appears that the store snapshot is not being expanded into the test reset. Although it's not explicitly mentioned in the documentation, I found that my tests are successful when I reset in this manner.

describe('the testing suite', () => {
  let store: Store;
  beforeEach(() => {
    store = TestBed.inject(Store);
    store.reset({ ...store.snapshot(),
      DESIRED_STATE
    })
  });
  
  it('should execute the test', () => {
    store.reset({ ...store.snapshot(),
      NEW_DESIRED_STATE
    })
  });
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ionic 2 hides the form input area within its input component

I set up a login page, but the input fields are not showing up on the form. Here is my current code: <ion-list> <ion-item> <ion-label fixed>Username</ion-label> <ion-i ...

What is the best way to pass a string value instead of an event in Multiselect Material UI?

Greetings, currently utilizing Material UI multi select within a React TypeScript setup. In order to modify the multi select value in the child component, I am passing an event from the parent component. Here is the code for the parent component - import ...

Exploring the Magic of Class Variable Destructuring in React

Is there a simpler method to break down a prop object and assign them to variables of the same name in the class? I am familiar with ({ first: this.first, second: this.second, } = props) however, it can get complicated when dealing with numerous variable ...

I am currently encountering challenges while trying to operate my smartadmin theme on angular 2 platform

I recently acquired the SmartAdmin Theme for my project development needs. It worked seamlessly with AngularJS, but I encountered numerous errors when attempting to integrate it with Angular2. I have attached screenshots showing the issues for your referen ...

TSLint in TypeScript showing unexpected results

In the process of developing a project using Angular, I recently made the switch from VS Code to WebStorm. Unfortunately, I'm encountering some difficulties that I can't seem to make sense of. To ensure everything is functioning correctly, I perf ...

Have there been any instances of combining AngularJS, ASP.NET-WebApi, OData, Breeze.js, and Typescript?

I am attempting to combine different technologies, but I am facing challenges as the entity framework meta-datas are not being consumed properly by breeze.js. Despite setting up all configurations, it's proving to be a bit tricky since there are no ex ...

Protractor is having trouble locating an element even though the page has finished loading

It seems like a common issue, but I can't seem to find a solution that works for my specific problem. The problem occurs when my test runs fine until it reloads the page and needs to capture an element from a dropdown. I've tried using IsPresent ...

Regular Expressions: Strategies for ensuring a secure password that meets specific criteria

Struggling to craft a regex for Angular Validators pattern on a password field with specific criteria: Minimum of 2 uppercase letters Minimum of 2 digits At least 1 special character. Currently able to validate each requirement individually (1 uppercase ...

What causes the function endpoint to become unreachable when a throw is used?

One practical application of the never type in typescript occurs when a function has an endpoint that is never reached. However, I'm unsure why the throw statement specifically results in this unreachable endpoint. function error(message: string): ne ...

Exploring Custom Validator Comparisons in Angular

Having trouble comparing two input values in a custom validator. An error should occur if the minValue exceeds the maxValue. FormGroup: sumFormGroup = this.formBuilder.group({ from: ['', [Validators.min(0), sumValidator]], to: [&ap ...

Vue Error: The method "reduce" is not a function

Currently implementing Vue.js with Typescript and aiming to utilize reduce for summing up the values of desktopCnt and mobileCnt from the deviceCount array to display their total numbers. The deviceCount array structure is as follows: [ { " ...

What could be causing the module to break when my Angular service, which includes the httpClient, is added in the constructor?

After creating a backend RESTful API, I encountered difficulties while trying to access it. To address this issue, I developed a database-connection.service specifically for making POST requests. However, I am facing challenges in implementing this solut ...

Creating dynamic key objects in TypeScript with index signatures: A beginner's guide

How can the code be optimized to automatically initialize a new product type without adding extra lines of code? Failure to initialize the variable results in a syntax error. enum ProductType { PC = 'pc', LAPTOP = 'laptop', TV ...

Automate the process of opening an ngbpopover from an Angular 2 component using programming techniques

Currently, I am referring to this specific article in order to integrate Bootstrap with Angular 2. While the instructions in the article are helpful, there seems to be a lack of information on how to pass the popover reference to a component method. The on ...

Ways to stop a ngFor loop in TypeScript

I'm currently working on a Hybrid app using Ionic-3. I have a function in my Typescript file that is being called from the Html, but it seems like the function is being executed multiple times causing my browser and application to hang. Below are the ...

Is it possible to combine TypeScript modules into a single JavaScript file?

Hey there, I'm feeling completely lost with this. I've just started diving into Typescript with Grunt JS and I could really use some assistance. I already have a Grunt file set up that runs my TS files through an uglify process for preparing the ...

Tips for resolving the issue when Chrome is able to load the page but Postman cannot find it

I'm encountering a perplexing situation that is entirely new to me and difficult to comprehend. I find myself unable to decipher what exactly I am witnessing, leading to uncertainty about why it is occurring, not to mention the challenge of determinin ...

How to Establish an Angular Global Variable

What is the process for establishing a global variable in Angular? I have established a variable within a service, entered a value in the login component, and attempted to access this variable from another component. However, I noticed that the value res ...

Can a custom type guard be created to check if an array is empty?

There are various methods for creating a type guard to ensure that an array is not empty. An example of this can be found here, which works well when using noUncheckedIndexedAccess: type Indices<L extends number, T extends number[] = []> = T["le ...

Using jasmine spy object to replace injected Renderer in shallow tests for Angular 2 components

Angular Component File 'zippy.component.ts': import { Component } from '@angular/core'; import {ZippyService} from '../services/zippy.service' import {Renderer} from '@angular/core' @Component({ selector: ...