Best practices for unit testing NGXS async HTTP requests

I'm currently utilizing NGXS in my Angular application for state management. As part of my development process, I am now focusing on implementing Unit Tests for NGXS async actions. Here is a snippet of the code that I have been working on:

schedule.service.ts

public getSchedulesByOrgIdAndJobRefId(orgId: string, jobRefId: string): Observable<Schedule[]> {
    const url = 'http://localhost:5000/api/v1/schedules';
    return this.httpClient.get<Schedule[]>(url);
  }

and within the action section of schedule.state.ts

@Action(LoadSchedulesAction)
  loadSchedules({patchState, getState}: StateContext<ScheduleStateModel>,
                         {orgId, jobRefId}: LoadSchedulesAction) {
    return this.scheduleService.getSchedulesByOrgIdAndJobRefId(orgId, jobRefId)
      .pipe(
        map((schedules: Schedule[]) => { // convert to a map
          return schedules.reduce((prev: EntityMap<string, Schedule>, curr: Schedule) => {
            prev[curr.id] = curr;
            return prev;
          }, {});
        }),
        tap((schedulesMap: EntityMap<string, Schedule>) => {
          const existingSchedules = getState().schedules;
          patchState({schedules: {...existingSchedules, ...schedulesMap}});
        })
      );
  }

While the action functions as expected, I have encountered challenges when it comes to Unit Testing such HTTP async actions. There seems to be a lack of documentation or resources addressing this specific scenario. Below is an excerpt from one of my test cases that aims to test the aforementioned action:

schedule.state.spec.ts
testSchedules = [...Array(5).keys()].map((cd, i: number) => {
      return {
        orgId,
        containerId: `CCD-${i}`,
        jobRefId,
        jobTitle: `Test Job ${i}`,
      };
    });

describe('Actions', () => {
    it('should load and schedules for specific job File', async () => {
      spyOn(scheduleService, 'getSchedulesByOrgIdAndJobRefId')
        .and.returnValue(of(testSchedules));
      await store.dispatch(new LoadSchedulesAction(orgId, jobRefId)).toPromise();
      expect(scheduleService.getSchedulesByOrgIdAndJobRefId).toHaveBeenCalled();
      const scheduleState = store.selectSnapshot((state) => state.schedulesState);
      // using a util method testSchedules converted to a Map type schedulesMap
      expect(scheduleState.schedules).toEqual(schedulesMap);
    });
  });

The issue at hand is that scheduleState.schedules consistently returns an empty object during testing. Interestingly enough, if we were to subscribe directly to the service call within the loadSchedules() function, the expected value would be returned. However, it is generally discouraged to subscribe to service calls within an Action implementation.

If anyone has insights on how to effectively Unit Test this particular type of action in NGXS, or if there are any details I might have overlooked, I welcome your expertise and advice.

Answer №1

 describe('Test case: loading and scheduling specific job file', () => {
  beforeEach(() => {
    spyOn(scheduleService, 'getSchedulesByOrgIdAndJobRefId')
      .and.returnValue(of(testSchedules));
  });

  it('should load schedules for a specific job file', () => {
    store.dispatch(new LoadSchedulesAction(orgId, jobRefId)).toPromise();
    expect(scheduleService.getSchedulesByOrgIdAndJobRefId).toHaveBeenCalled();

    const scheduleState = store.selectSnapshot((state) => state.schedulesState);
    expect(scheduleState.schedules).toEqual(schedulesMap);
  });
});

To make changes to the test function as shown above, inject your `scheduleService` within the `beforeEach` section. If you need to verify the HTTP service URL or request method (POST, PUT, or GET), utilize `HttpTestingController`, specifically designed for Angular HTTP services.

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

Discovering the power of angular async binding in your projects can greatly

Is there a way to achieve something similar to <span *ngIf="admins.includes(name) | async"> where the admins is declared as Observable<string[]> in the component class? I understand that this code may not work, but is there a workaround to make ...

Tips for deleting markers from a TomTom map on a web application using Angular 5

I have integrated the TomTom map into my web application to display vehicles on the map. While I have successfully displayed the vehicles, I am now facing an issue with removing markers that have been added to the map. Please refer to the attached screens ...

One way to run a single test suite using Angular2 and Karma is by using the describe.only

I'm having trouble getting this to function properly with Karma and Angular2. describe.only("MyComp"), () => { ... Does anyone have any suggestions on how to make this work? ...

What are the methods for showcasing data within a string?

I'm struggling to figure out how to display the column names from a JSON file. Currently, all the column names are being displayed in one column, and empty fields are showing up for some reason. How can I format it so that the column names are listed ...

Utilizing Angular's Mat-Table feature to dynamically generate columns and populate data in a horizontal manner

I am in need of a solution where I must populate the mat-table in a horizontal format with an array of JSON objects. The input array is as follows: [{ "SAMPLERULEID": 69, "SAMPLERULENAME": "Sample1", &q ...

Display issues with deeply nested components

I'm facing an issue with displaying the third nested component. Expected: Hello App Component Hello Nest-A Component Hello Nest-1 Component Hello Test-Z Component Actual: Hello App Component Hello Nest-A Component Hello Nest-1 Component Why ...

What is the method for transmitting a concealed attribute "dragable" to my component?

Currently, I have successfully integrated a here map into my project, but I am now tackling the challenge of adding draggable markers to this map. To achieve this, I am utilizing a custom package/module developed by my company. This package is designed to ...

How can we incorporate methods using TypeScript?

I'm currently diving into TypeScript and encountering some challenges when trying to incorporate new methods into the DOM or other pre-existing objects. For instance, I'm attempting to implement a method that can be utilized to display colored te ...

Encountering an issue while attempting to import the react-autosuggest module, receiving the following error

I recently encountered an issue while trying to import the react-autosuggestion module in my TypeScript file on Ubuntu v18 OS. Initially, I executed the following command: sudo npm install react-autosuggest --save import Autosuggest from 'react- ...

How come Typescript claims that X could potentially be undefined within useMemo, even though it has already been defined and cannot be undefined at this stage

I am facing an issue with the following code snippet: const productsWithAddonPrice = useMemo(() => { const addonsPrice = addonsSelected .map(id => { if (addons === undefined) { return 0} return addons.find(addon => addo ...

The Standalone Component does not appear for debugging in webpack:source when utilizing an incompatible version of Node

I have developed two components: https://i.sstatic.net/fSNqa.png However, after running ng serve, I am only able to see one component in the source of the Chrome browser: https://i.sstatic.net/VzdDS.png How can I troubleshoot this standalone component? ...

`What sets apart local and global dependencies within the Angular framework`

Can you explain the distinction between fetching local and global dependencies? Let's say I use "npm i bootstrap" in the project directory, will it be downloaded from that specific location? If I prefer to download it globally instead? Explore variou ...

Enhancing a Given Interface with TypeScript Generics

I am looking to implement generics in an Angular service so that users can input an array of any interface/class/type they desire, with the stipulation that the type must extend an interface provided by the service. It may sound complex, but here's a ...

Type errors in NextJS are not being displayed when running `npm run dev`

When encountering a typescript error, I notice that it is visible in my editor, but not in the browser or the terminal running npm run dev. However, the error does show up when I run npm run build. Is there a method to display type errors during npm run d ...

Unable to locate the name even though it is not referenced in the typescript code

I'm attempting to retrieve a value inside an if statement structured like this: const Navbar = () => { const token = getFromStorage("token"); if (token) { const { data, error } = useQuery( ["username", token], ...

Leverage TypeScript AngularJS directive's controller as well as other inherited controllers within the directive's link function

I am currently developing an AngularJS directive in TypeScript for form validation. I am trying to understand how to utilize the directive's controller and inherit the form controller within the directive's link function. Thank you in advance! ...

Unable to access the subscribe() value outside of the Angular 2 method

Having trouble accessing the subscribe value outside the subscribe() method. Can anyone assist with how to access myData outside the method? ngOnInit() { this.subscription = this.messageService.getMessage().subscribe(function(data){ console.log(dat ...

Dark Theme Issue with Angular Material's CheckBox in Mat-Menu

If you try to place a <mat-checkbox> inside a <mat-menu>, the dark themes won't apply to the text part of your <mat-checkbox>. Look at the image at the end for reference. A similar issue arises with <mat-label>s. However, the ...

iterating through the checked values in Angular 4

I need assistance in writing a loop using TypeScript to send each checked employee as a parameter to the postCouncilAbsence function. Can anyone help? component.ts: onattendanceSave(form:NgForm){ this.index = this.attendanceForm.value console.log ...

Tips for adjusting the language settings on a date picker

Is there a way to change the language from English to French when selecting a month? I believe I need to configure something in the core.module.ts. How can I achieve this? https://i.sstatic.net/Cpl08.png @NgModule({ declarations: [], imports: [ Co ...