Creating a simulated constant class in Angular 2+ for handling environment variables

Can anyone assist me with writing unit tests for functionalities that depend on the current environment? I am struggling to force a constant environment to return specific values in my source code. Here is the component code I need to test:

import { Component, OnInit } from '@angular/core';

// DEV environment
import { environment } from '@env/environment';
// PROD environment
// import { environment } from '@env/environment.prod';
import { Logger } from '@app/core/logger/logger.service';
import { I18nService } from '@app/core/language/i18n.service';

let log: Logger;

@Component({
  selector: 'app-root',
   templateUrl: './app.component.html',
   styleUrls: ['./app.component.scss'],
   providers: [I18nService]
 })

export class AppComponent implements OnInit {

  constructor(private i18nService: I18nService) {
     log  = new Logger('X');
  }

  ngOnInit() {
    // Setup logger
    if (environment.isProduction) {
        Logger.enableProductionMode();
    }

    log.debug('init');

    // Setup translations
    this.i18nService.init(environment.defaultLanguage, environment.supportedLanguages);
  }

Here is the unit testing code:

 import * as environmentDEV from '@env/environment';
 import * as environmentPRO from '@env/environment.prod';
 ...

 let component: AppComponent;
 let fixture: ComponentFixture<AppComponent>;

 // Spies declarations
 let spy_initI8nServiceMethod: jasmine.Spy;
 let spy_debugLoggerAttr: jasmine.Spy;
 let spy_enableProductionModeLoggerMethod: jasmine.Spy;

 describe('AppComponent', () => {
       beforeEach(async(() => {
        TestBed.configureTestingModule({
          imports: [TranslateModule.forRoot()],
          declarations: [AppComponent],
          providers: [I18nService]
        });
        TestBed.compileComponents();
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.debugElement.componentInstance;
       }));

       beforeEach(inject([I18nService],
                        (_i18nService: I18nService) => {

        i18nService = _i18nService;

        // Create spies
        // Spies
        spy_initI8nServiceMethod = spyOn(I18nService.prototype, 'init');
        spy_debugLoggerAttr = spyOn(Logger.prototype, 'debug');
        spy_enableProductionModeLoggerMethod = spyOn(Logger, 'enableProductionMode');
       }));

       it('should component init on PRO environment',
           async(() => {

        spyOn(environment, 'isProduction').and.returnValue(environmentPRO.environment.isProduction);
        spyOn(environment, 'defaultLanguage').and.returnValue(environmentPRO.environment.defaultLanguage);
        spyOn(environment, 'supportedLanguages').and.returnValue(environmentPRO.environment.supp...
        
        component.ngOnInit();

        expect(spy_enableProductionModeLoggerMethod).toHaveBeenCalledBefore(spy_debugLoggerAttr);
        expect(spy_debugLoggerAttr).toHaveBeenCalledBefore(spy_initI8nServiceMethod);
        expect(spy_initI8nServiceMethod).toHaveBeenCalledWith(environmentPRO.environment.defaultLangua...
       }));
 });

I have tried using spyOn and spyOnAttribute to force the constant environment to return specific values but without success. Can anyone point out what I might be doing wrong here? Should I consider trying a different approach?

Answer №1

After careful consideration, I have come to the conclusion that using a configuration file in TypeScript may pose a security risk, especially since it will be converted into JavaScript during execution. Based on feedback from @mixth, it seems testing such a setup might not be feasible. As a result, I will heed your advice and opt for an injectable service instead, utilizing JSON files to store settings. Much appreciated for the valuable insight and the helpful thread reference from stackoverflow provided by @mixth.

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

How can I collaborate on a component in Angular?

I'm currently developing an Angular application that utilizes a map feature powered by the Google Maps API. What I aim to achieve is the ability to freely move around the map to locate specific places, add markers, as well as edit existing markers, am ...

Encounter an Unexpected Token Issue when using NextJS-auth0 in Jest

I am facing a problem with my Next.js app that is integrated with the nextjs-auth0 package. Whenever I attempt to test a particular file and include the following import: import { getSession } from '@auth0/nextjs-auth0'; An error occurs, stating ...

Setting up Angular 2 for ASP.NET MVC: A Step-by-Step Guide

angular.io provides a setup guide for asp.net core at: https://angular.io/docs/ts/latest/cookbook/visual-studio-2015.html. However, I am trying to configure it for an asp.net mvc application. The Quick Start files are already present in the asp.net mvc te ...

Ways to create a unified component from two similar but distinct components by assigning a value

Upon reviewing my menu setup, I realized that the submenus in my project are essentially the same component with only a single index differentiating them. In order to simplify the structure, I am looking to consolidate these components into one. HTML < ...

Include a new course based on a specific situation

Is it possible to conditionally add a specific class using vue js? In my DataStore, I have two values defined in TypeScript: value1: 0 as number, value2: 0 as number Based on the values of value1 and value2, I want to apply the following classes in my te ...

Angular 12: Ensure completion of all data fetching operations (using forkJoin) prior to proceeding

Within my ngOnInit function, I am looking for a way to ensure that all requests made by fetchLists are completed before moving forward: ngOnInit(): void { this.fetchLists(); this.route.params.subscribe(params => { this.doSomethingWit ...

Tips on avoiding the conversion of the ✳ symbol into an emoji

My issue lies in my ✳ (Eight-Spoked Asterisk) symbol being converted to an emoji on iOS/android devices. Find more about the Eight-Spoked Asterisk Emoji here. Could someone guide me on how to prevent the normal symbol ✳ from being transformed into an ...

Struggling to incorporate res.send(result) into my Node.js code to effectively pass data to my Angular application

I have created a Nodejs and angular2 application where I need to integrate them with a Neo4j application. Currently, I am able to access the database from my NodeJS code via Angular2, but I am facing issues in sending the data back to the Angular2 app. Whe ...

The TypeScript compiler does not recognize the property 'theme' in this context

I am currently working on a ReactJS starter project using typescript along with material-ui v1.x beta. I have encountered an issue with the themes, which are detailed at: . TypeScript is throwing errors stating that the 'theme' property does not ...

What is the best way to store a small number of files in the state

I have recently implemented Drag and Drop functionality, and now I am facing an issue where I need to save a few files in state. const [uploadedFiles, setUploadedFiles] = useState<any[]>([]); const onDropHandler = async (e: React.DragEvent<HTMLDi ...

Issue encountered with passport-local in TypeScript: Unable to utilize 'new' with an expression that does not have a call or construct signature

Currently, I am attempting to implement the passport-local package in TypeScript (version 2.0.0RC); however, a compiler error has arisen: Error TS2351: It is not possible to use 'new' with an expression lacking a call or construct signature. ...

An error occurred in Typescript's map() function: The element is implicitly of type 'any' because the expression 'string' cannot be used to index the type

I am currently working with an array (iotDatas) that consists of the following interface: interface iotData { id: string; device_id: string; color: number; ph: number created_at: string; } Within my state, I have a selector to choose between the &apo ...

Sequelize 5: Error encountered when using many-to-many functions

Currently, I am working with Sequelize 5.21.7 (in a next.js application) to establish a many-to-many relationship between the Image and Tag models. However, I encounter TypeErrors when attempting to utilize the imageInstance.addTag() and imageInstance.getT ...

The function called v1.isEqual is throwing a TypeError because it is not recognized as

When working on my NextJs/React project, I encountered an issue while using the useCollectionData hook. The problem arises when fetching posts from my firestore database, resulting in the error message TypeError: v1.isEqual is not a function Below is the ...

What could be causing the issue: Unable to locate or read the file: ./styles-variables?

I'm currently following a tutorial on how to create responsive layouts with Bootstrap 4 and Angular 6. You can find the tutorial here. I've reached a point where I need to import styles-variables.scss in my styles file, but I keep encountering t ...

When attempting to start npm, Angular2 fails to recognize a specific module

I recently installed a new module and added it to my system JS configuration as follows: // specifying where to look for things using the map object var map = { 'angular2-notifications': 'node_modules/angular2-notifications', } ...

Can the flat input structure be retained while using nestjs/graphql Args?

I was wondering about a simple query: @Query(() => ProfileOutput) async profile(@Args('id') id: string) { return await this.profileFacade.findProfileById(input.id); } My issue is that I'd like to use the @IsMongoId() validator from ...

Attempting to render the application results in an error message stating: "Actions must be plain objects. Custom middleware should be used for asynchronous actions."

I am experiencing an issue while testing my vite + typescript + redux application to render the App component using vitest for testing. I am utilizing redux@toolkit and encountering a problem when trying to implement async thunk in the app component: Error ...

Switching a class component to a functional component with react hooks (specifically useRef) - tips for preventing the dreaded "undefined" error

I have a code snippet that works as a class component and I'm trying to convert it into a functional component using the react-rewards library. Class component (working): import { Checkbox } from "@chakra-ui/react"; import React, { Compone ...

Designing personalized plugins with Typescript in Nuxt

In my Nuxt project, I have implemented a custom plugin file that contains an object with settings called /helpers/settings: export const settings = { baseURL: 'https://my-site.com', ... }; This file is then imported and registered in /plugi ...