Mocking a service dependency in Angular using Jest and Spectator during testing of a different

I am currently using:

Angular CLI: 10.2.3

Node: 12.22.1

Everything is working fine with the project build and execution. I am now focusing on adding tests using Jest and Spectator. Specifically, I'm attempting to test a basic service where I can mock most of the values.

@Injectable({
    providedIn: 'root'
})
export class BasicAuthService {
  environmentName = '';
  environmentUrl = '';

  constructor(
    private http: HttpClient,
    private config: ConfigService, //custom service 1
    private runtimeConfig: RuntimeConfigService, // custom service 2
  ) { 
      this.environmentName = runtimeConfig.config.environmentName;
      this.environmentUrl = this.environmentName == "localhost" 
          ? "http://" +  runtimeConfig.config.serviceUrl 
          : runtimeConfig.config.serviceUrl;    
  }
  
  getAuthentication(credentials) {
    let basicAuthHeaderString = 'Basic ' 
      + window.btoa(credentials.username + ':' + credentials.password);
    let headers = new HttpHeaders({'Content-Type': 'application/json'});
    let options = {
      headers: headers
    }
    let envUrl = `${this.environmentUrl}/api/login`
    return this.http.post<any>(envUrl, JSON.stringify(credentials), options)
      .pipe(
        map(
          data => {
          sessionStorage.setItem('authenticatedUser', credentials.username);
          sessionStorage.setItem('token', data.token);
          
          this.config.userGroupData = data.entitlements[0];

          }
        )
      );
  }


}

Within the constructor, it attempts to set two variables (this.environmentName and this.environmentUrl) based on another custom service (runtimeConfig).

My testing setup looks like this:

describe('BasicAuthService', () => {
  let spectator: SpectatorService<BasicAuthService>;
  const createService = createServiceFactory({
    service: BasicAuthService,
    providers: [],
    imports: [
        HttpClientTestingModule],
    entryComponents: [],
    mocks: [ConfigService, RuntimeConfigService]
  });


  beforeEach(() => spectator = createService());

  it('should be logged in', () => {
    
    const runtimeConfigService = spectator.inject<RuntimeConfigService>(RuntimeConfigService);
    const configService = spectator.inject<ConfigService>(ConfigService);
    runtimeConfigService.config = { 
      environmentName: "localhost", 
      serviceUrl : "localhost:8071"
    };     // This also does not work, same error.   
    expect(spectator.service.getAuthentication(createService)).toBeTruthy();
  });

});

However, the test is failing with the following error:

  ? BasicAuthService > should be logged in

  TypeError: Cannot read property 'environmentName' of undefined

      22 |     private runtimeConfig: RuntimeConfigService,
      23 |   ) {
    > 24 |     this.environmentName = runtimeConfig.config.environmentName;
         |                                                 ^

The runtime configuration is as follows. Even after trying to initialize the values, the issue persists:

// RuntimeConfigService
@Injectable({
  providedIn: 'root'
})
export class RuntimeConfigService {

  config: Config;
  
  constructor(private http: HttpClient) {}

  loadConfig() {
  return this.http
    .get<Config>('./assets/runtime-config.json')
    .toPromise()
    .then(config => {
        this.config = config;        
    });
  }
}

export class Config {
  serviceUrl: string;
  environmentName: string;
}

How can I effectively mock these services and their values to enable successful testing for this scenario?

Answer №1

If you want to experiment, use the mockProvider function from spectator. It's a simple way to mock the service with default values. This is really helpful when the constructor code relies on a value from DI.

import { mockProvider, ... } from '@ngneat/spectator/jest'; 

describe('BasicAuthService', () => {
  let spectator: SpectatorService<BasicAuthService>;
  const createService = createServiceFactory({
    service: BasicAuthService,
    providers: [
      mockProvider(ConfigService, {
        someFunction: () => someReturnValue; //if needed
      }),
      mockProvider(RuntimeConfigService, {
        config: {
          environmentName: "localhost", 
          serviceUrl : "localhost:8071"
        }
      })
    ],
    imports: [HttpClientTestingModule],
    entryComponents: [],
  });

  beforeEach(() => spectator = createService());

  it('should be logged in', () => {
    expect(spectator.service.getAuthentication(createService)).toBeTruthy();
  });
});

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

Utilizing *ngIf for Showing Elements Once Data is Completely Loaded

While working on my Angular 2 app, I encountered an issue with the pagination UI loading before the data arrives. This causes a visual glitch where the pagination components initially appear at the top of the page and then shift to the bottom once the data ...

When I close all of my tabs or the browser, I aim to clear the local storage

Could you recommend any strategies for clearing local storage upon closing the last tab or browser? I have attempted to use local storage and session storage to keep track of open and closed sessions in an array stored in local storage. However, this meth ...

Ways to improve the feedback for Typescript when dealing with the potential existence of a nested method

Recently encountered a critical bug that I believe could have been identified with the right TypeScript setup. Struggling to come up with a suitable title, so please bear with me. While initializing a widget app, similar to a chat app loaded by a parent a ...

Migrating image information from Angular version 14 to Asp.Net Core 6.0 Rest Api

When transferring product model data from Angular to a REST API using FormData, there is an images array included in the product data. Upon receiving this product in the REST API, the images data is accessed using Request.Form.Files. The images are then se ...

Ways to remove newly added tasks using JavaScript function?

I have an existing list of li elements in my HTML that can be deleted using JavaScript. However, whenever I add a new li, the delete function no longer works on the newly added item. I suspect the issue lies within the current implementation of the for loo ...

Avoid inheriting Parent component styles in Child component

I am facing an issue with styling a child component independently from its parent. Specifically, I want to prevent the text alignment set in the parent component from affecting the child component. Despite trying different ViewEncapsulation types (Native, ...

Generics causing mismatch in data types

I decided to create a Discord bot using DiscordJS and TypeScript. To simplify the process of adding components to Discord messages, I developed an abstract class called componentprototype. Here is how it looks (Please note that Generators are subclasses li ...

Highcharts3d was already defined locally, preventing the import declaration from being recognized with error code TS2440

Struggling to get my application to run in VS code due to this persistent error: error TS2440: Import declaration conflicts with local declaration of 'Highcharts3d' Any assistance in resolving this issue would be greatly appreciated. import ...

Guide to implementing fullpagejs with Angular 7 selectors

I have been working on an Angular 7 project with fullpagejs by Alvarotrigo. Everything seems to be functioning properly, but I am facing an issue where the content of my website is not visible because the color of fullpagejs covers it all. When I use norma ...

Guide to troubleshooting React Tape unit tests within the VS Code environment

Currently, I am in the process of debugging a third-party library unit test. The test case is executed using tape and tape-run, and it utilizes the following command to run the tests. "test": "browserify -x react-native -x react/addons -x react/lib/ReactC ...

Angular 7 error: No provider found for PagerService causing NullInjectorError

I have been struggling to get pagination working properly in my project. Below is the code I have written: pager.service.ts: import * as _ from 'underscore'; @Injectable({ providedIn: 'root', }) export class PagerService { ...

Instructions on transferring information from the app.component to its child components

I am currently working with Angular 6 and I have a specific requirement. I need to send data retrieved from an external API in my app.component to other child components. Instead of repeatedly calling the common API/service in every component, I want to ma ...

Tips for embedding an Angular application within another Angular application

I am working on two Angular projects at the moment. The first one is named App1, while the second one is called Angular Form Editor. My goal is to integrate the Form Editor into the App1 project. What steps should I take in order to achieve this integrat ...

The Tanstack react-table feature is limited in its ability to output tsx from the cell

Currently conducting a test on Tanstack react-table library using React and TypeScript. It appears that I am encountering an issue with returning tsx/jsx from the cell function of ColumnDef: Is there something crucial that I seem to be overlooking in this ...

`Managing select tag data in Angular reactive forms`

Having an issue with selecting the gender option from JSON formatted data received from the backend. The gender is displayed as a select tag on the frontend, but it does not pre-select the option that corresponds to the gender value in the JSON data. The b ...

Guidelines for returning an object upon finishing the .map function within a promise-based function in Node.js

While working on server-side code using nodejs, I have encountered an issue with the .map() method inside a Promise. The problem is that the method returns a value before the .map() function completes its execution successfully. Here's the snippet of ...

Encountering a hiccup during the installation process of Angular CLI

I'm encountering an issue in the command line, seeking assistance C:\Users\admin>npm -v 6.9.0 C:\Users\admin>npm install -g @angular/cli npm ERR! Unexpected end of JSON input while parsing near '...vkit/core":"8.0.4", ...

What causes data to be emitted sporadically by a subject?

We are seeking your input. Within the constructor of the component, there is code that listens for the mode which enables the isReportMode$ report from the service. However, upon switching to another component and returning back, the isReportMode$ variab ...

Tips for restricting User access and displaying specific sections of the menu

I have a component that utilizes map to display all menu parts. Is there a way to make certain parts of the menu hidden if the user's access rights are equal to 0? const Aside: React.FunctionComponent = () => { const[hasRight, setHasRight] = us ...

Firebase login success callback not getting invoked

I have set up routes, the AuthGuard, and Firebase login in my project. I am using callbacks to handle the success and failure of the Firebase login process. successCallback(signInSuccessData) { console.log("Received with Success!"); } errorCallback(e ...