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

Is there a way to retrieve all properties within a Typescript Class that includes optional properties?

If we have a scenario where: class ExampleType { item1?: string, item2?: string } and all the properties are OPTIONAL. Is there a way to generate an array of property names like: ["item1", "item2"] I attempted to use console.log( ...

Options for importing TypeScript in WebStorm

Using WebStorm for auto-importing TypeScript classes has been a great help to tidy up my code and make it more organized. However, I have noticed that the imports are always formatted in a single line like this: import { Component, EventEmitter, Input, O ...

Using the spread operator in the console.log function is successful, but encountering issues when attempting to assign or return it in a

Currently facing an issue with a spread operator that's really getting on my nerves. Despite searching extensively, I haven't found a solution yet. Whenever I utilize console.log(...val), it displays the data flawlessly without any errors. Howev ...

`Is There a Solution When Compilation Fails?`

I keep encountering an issue when I run the command npm start. The problem seems to be originating from PancakeSwap Frontend and after several attempts, I am still unable to resolve it. Your assistance is greatly appreciated :) Below is a snippet of my Ap ...

Creating a dynamic columns property for Mat-Grid-List

Is it possible to create a Mat-Grid-List where the number of columns can be dynamically changed based on the width of the container? Here is an example: <mat-grid-list [cols]="getAttachmentColumns()" rowHeight="100px" style="width: 100%;"> <mat ...

Using styled-components in React

I came across this interesting code snippet in the styled-components documentation. Here it is: const Button = styled.button<{ $primary?: boolean; }>` background: ${props => props.$primary ? "#BF4F74" : "white"}; color: ${p ...

Ways to resolve the error message "TypeError: 'setOption' is not a function on type 'MutableRefObject' in React"

CODE export default function EChart({ option, config, resize }) { let chart = useRef(null) let [chartEl, setChartEl] = useState(chart) useEffect(() => { if (resize) { chartEl.resize() } if (!chartEl.cu ...

Struggling to effectively organize data routing within Angular? Let's tackle the challenges of

As a newcomer to Angular, I initially had success with CRUD operations without using routing. However, after implementing routing, I encountered an issue where the added values were not displaying in the content table on another page. It seems like there ...

Allow only specified tags in the react-html-parser white list

Recently, I've been working on adding a comments feature to my projects and have come across an interesting challenge with mentioning users. When creating a link to the user's profile and parsing it using React HTML parser, I realized that there ...

transferring attributes from a higher component to a lower one (modal)

I am relatively new to React and I want to share a detailed problem description: I have a Todo project that consists of multiple interfaces. The main interface displays all the lists, each containing a title, a group of tasks, and a button to create a ta ...

Error: 'Target is not found' during React Joyride setup

I am attempting to utilize React Joyride on a webpage that includes a modal. The modal is supposed to appear during step 3, with step 4 displaying inside the modal. However, I am encountering an issue where I receive a warning message stating "Target not m ...

In TypeScript and React, what is the appropriate type to retrieve the ID of a div when clicked?

I am facing an issue in finding the appropriate type for the onClick event that will help me retrieve the id of the clicked div. const getColor = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => { const color = event.target.id; // ...

Creating a dynamic linear gradient background color using Angular 2 binding

Using static values works perfectly <div style="background: linear-gradient(to right, #0000FF 0%, #0000FF 50%, #FFA500 50%, #FFA500 100%);"></div> in my TypeScript file. this.blueColor = '#0000FF'; this.orangColor = '#FFA500&a ...

Tips for utilizing functions in an inline HTML translation pipe

My objective is to streamline the code by using the Angular translate pipe. Currently, I find myself using 8 curly brackets and repeating the word "translate" twice... there must be a more efficient approach. Here is my current code setup: <s ...

Ways to retrieve form information from a POST request

I received a POST request from my payment gateway with the following form data: Upon trying to fetch the data using the code snippet below, I encountered errors and gibberish content: this.http .post<any>('https://xyz.app/test', { ti ...

tips for resolving pm2 issue in cluster mode when using ts-node

I'm having an issue using pm2 with ts-node for deployment. Whenever I try to use cluster-mode, a pm2 instance error occurs, saying "Cannot find module..." Error: Cannot find module '{path}/start' at main ({path}/node_modules/ts-node/dist/b ...

Angular child component displaying of table data is not looping properly

Currently, I am using Angular 13 along with Bootstrap 3 to develop an application that consists of two main components: form-component (dedicated to inputting user details) and list-component (designed to showcase all data in a table format). Within the HT ...

How can we add a key:value pair at a specific position in an array in Angular 2 using Typescript?

Is there a way to insert a key value pair at a specific index in an array? I am currently struggling with this task. Here is the code I have been working on: this.quiz.push( { "question-no":this.no, "Ans":this.ans } I require this functionality to ...

Simulating service calls in Jest Tests for StencilJs

When testing my StencilJs application with Jest, I encountered an issue with mocking a service class method used in a component. The service class has only one function that prints text: The Component class: import {sayHello} from './helloworld-servi ...

Can we create a class to represent a JSON object?

Can a JSON be modeled with a class in TypeScript (or Angular)? For example, I am using Firebase and have a node called /books structured like this: books -- 157sq561sqs1 -- author: 'Foo' -- title: 'Hello world' (Where 1 ...