Mocking multiple services and their constructors in an Angular 2 TypeScript Jasmine test component

I've got this login component code snippet that I need help testing in Angular.

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { BasicAuthService } from './../../services/auth/basic-auth.service';
import {ConfigService} from  '../../services/common/config.service';

import { RefdataService } from '../../services/common/refdata.service';
import { config } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { RouteGuardService } from '../../services/auth/route-guard.service';



@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
  form: FormGroup;
  private formSubmitAttempt: boolean;
  invalidLogin = false;
  
  credentials:any;
  userPermissions: any;
  errorMessage: any = null;
  isLoading: boolean = false;
  
  

  constructor(
    private router: Router,
    private fb: FormBuilder,

    private basicAuthService: BasicAuthService,
    public config: ConfigService,

    private refdataService: RefdataService,
    private authGuard: RouteGuardService,

  ) { }

  ngOnInit(): void {

    this.form = this.fb.group({
      userName: ['', Validators.required],
      password: ['', Validators.required]
    });
  }


  handleAuthentication() {
    if (this.form.valid) {
      this.isLoading = true;
      console.log("handleAuthentication");

      
      let iUserName = this.form.value.userName;   
      let iPassword = this.form.value.password;
      
      this.credentials = {
        username: iUserName,
        password: iPassword
      }
      
      this.basicAuthService.getAuthentication(this.credentials)
          .pipe(
            switchMap(
              data => {
                console.log(data);
                this.invalidLogin = false;
                this.formSubmitAttempt = true;
                return this.refdataService.loadRefDataConfig();
              })
          ).subscribe(
            res => {             
                            
              // some variable updates
              this.router.navigate(['main']);

            },
            error => {
              this.isLoading = false;
              console.log(error.status);
              let mess = '';
              if (error.status === 401) {
                mess = 'Invalid Login credentials supplied! Correct the errors and re-submit.'
              } else {
                mess = error.error
              }
              this.errorMessage = { _body: 'Error Code: ' + error.status + '\nMessage: ' + mess };
              this.errorService.openSnackBar(this.errorMessage, 'Close', 'red-snackbar', 10000);
              
              this.invalidLogin = true;
              console.log(this.invalidLogin);
            }
          ); 
    }
  }

}

I want to write a test for this component and mock all services. Can someone assist me with this as I am new to Angular?

Below is my current failing test:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms'
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { LoginComponent } from './login.component';

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;


  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        ReactiveFormsModule,
        FormsModule,
        HttpClientTestingModule
      ],
      declarations: [ LoginComponent ],

    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });


  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Error:

LoginComponent > should create
TypeError: Cannot read property 'environmentName' of undefined
    at <Jasmine>
    at new BasicAuthService (http://localhost:0000/_karma_webpack_/src/app/services/auth/basic-auth.service.ts:24:49)
    at Object.BasicAuthService_Factory [as factory] (ng:///BasicAuthService/ɵfac.js:5:10)
    at R3Injector.hydrate (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:11248:1)
    at R3Injector.get (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:11070:1)
    at NgModuleRef$1.get (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:24198:1)
    at Object.get (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:22101:1)
    at getOrCreateInjectable (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:3921:1)
    at ɵɵdirectiveInject (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:13752:1)
    at NodeInjectorFactory.LoginComponent_Factory [as factory] (ng:///LoginComponent/ɵfac.js:6:50)
    at getNodeInjectable (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:4029:1)
Expected undefined to be truthy.
Error: Expected undefined to be truthy.
    at <Jasmine>
    at UserContext.<anonymous> (http://localhost:0000/_karma_webpack_/src/app/components/login/login.component.spec.ts:35:23)
    at ZoneDelegate.invoke (http://localhost:0000/_karma_webpack_/node_modules/zone.js/dist/zone-evergreen.js:364:1)
    at ProxyZoneSpec.onInvoke (http://localhost:0000/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:292:1)

The failure points to an issue with the constructor for the basic-auth-service.

export class BasicAuthService {
  
  environmentName = '';
  environmentUrl = '';

  constructor(
    private http: HttpClient,
    private config: ConfigService,
    private runtimeConfig: RuntimeConfigService,
  ) { 
    this.environmentName = runtimeConfig.config.environmentName;
    this.environmentUrl = this.environmentName == "localhost" ? "http://" +  runtimeConfig.config.serviceUrl : runtimeConfig.config.serviceUrl;    
  }

I need to know how to mock this service (and any other called services) to prevent its constructor from running. How can I create a simple mock of BasicAuthService for my component test?

Answer №1

Here are some helpful tips:

  • If you want to ignore dependencies, consider using @Optional. If you don't need to test a service, make it optional instead of injecting it and leaving it unused.

  • If something can't be made optional, try mocking it instead:

    { provide: UserService, useClass: UserServiceMock }
    
    export class UserServiceMock {}
    
  • Another approach is to use jasmine spyOn in Angular to mock dependencies and directly instantiate new components for more focused unit testing.

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

Tips for displaying dynamic images using the combination of the base URL and file name in an *ngFor loop

I have a base URL which is http://www.example.com, and the file names are coming from an API stored in the dataSource array as shown below: [ { "bid": "2", "bnam": "ChickenChilli", "adds": "nsnnsnw, nnsnsnsn", "pdap": " ...

What is the best way to send information using an array of objects?

In my Angular 5 project, I am using the ngx select dropdown package (https://www.npmjs.com/package/ngx-select-dropdown) and I'm wondering how to pass an array of objects instead of an array of strings. Currently, when I do so, it displays [Object Obje ...

JavaScript: Manipulating Data with Dual Arrays of Objects

//Original Data export const data1 = [ { addKey: '11', address: '12', value: 0 }, { addKey: '11', address: '12', value: 0 }, { addKey: '12', address: '11', value: 0 }, { addKey: &a ...

Trigger Angular2 EventEmitter in child component to inform parent component

I am having trouble triggering an event from the child component to the parent component. @Component({ template:'<foo></foo>' }) export class ParentComponent{ onDoSomething($event){ //handling logic goes here } } @Compo ...

Encountering an issue with a MEAN application using Angular 2: The error message states "Cannot read property

As a first-time application developer, I am working on creating a system to manage Client profiles. Utilizing the Angular tour of heroes for the basic structure, I integrated mongodb and express components sourced from various online platforms. However, I ...

Maintain the chosen month in every dropdown toggle div in angular 4

While displaying data using toggle options, I am facing an issue where if I click on a different month, all other greyed out headers are displaying the previously selected values. I am trying to figure out a way to keep the value under Selected month as i ...

Leveraging Amazon IVS Player within Angular

Struggling to integrate the npm version of the amazon-ivs-player with Angular, as it seems optimized for webpack while I am using angular-cli. Following a guide at this link. The issue arises with importing the wasm files in my Angular application: ERROR ...

When using `onClick` in React, the old state value is not captured even though it may appear to be

In order to enhance the modularity and generality of my tabs and tab slots, I have developed a somewhat intricate setup. To achieve this, I opted to create a context and provider that expose methods for manipulating tabs and slots (where slots represent wh ...

What is the method for accessing the string value of a component's input attribute binding in Angular 2?

In my Angular2 application, I have a straightforward form input component with an @Input binding pointing to the attribute [dataProperty]. The [dataProperty] attribute holds a string value of this format: [dataProperty]="modelObject.childObj.prop". The mod ...

Tips for infuriating TSC with Lookup categories

I'm looking for the Typescript compiler (TSC) to throw errors when I make mistakes in signatures. export class EventEmitter<EventTypes extends { [key: string]: any }> { subscribe<Event extends keyof EventTypes>(type: keyof EventTypes, ...

What is the most efficient way to perform an inline property check and return a boolean value

Can someone help me with optimizing my TypeScript code for a function I have? function test(obj?: { someProperty: string}) { return obj && obj.someProperty; } Although WebStorm indicates that the return value should be a boolean, the TypeScript compil ...

Exploring the power of Vue.js reactivity using Object.defineProperty in a TypeScript environment

Currently, I am in the process of developing a TypeScript class to manage form submissions and handle server errors: export default class Form<Model> { private readonly original: Model private changes: Partial<Model> constructor(d ...

Can multi-page applications be developed using Angular?

As I contemplate developing a food ordering application similar to Zomato, I have observed that Angular-like routing is used on certain pages, specifically during the ordering process after selecting a restaurant. Unlike other sections where server routing ...

What is the process for exporting a plugin from dayjs() in JavaScript?

Currently, I have incorporated the plugin isToday() to enhance the capabilities of dayjs(). Nevertheless, I am uncertain about how to export isToday() in order to utilize it across other files. import isToday from "dayjs/plugin/isToday"; expor ...

Is there a way to prevent CSS Module imports in a Next.js component from causing parsing errors during unit testing?

As a beginner in Next.js, I am utilizing CSS Modules in my project and testing with Riteway. However, when I execute unit tests for React components, any component that imports a CSS Module encounters a SyntaxError as it fails to parse the CSS file being i ...

You cannot assign type void to type any

I'm currently working on a component that involves some code: export class AddNewCardComponent { public concept = []; constructor( private _router: Router, private _empDiscService: empDiscService) { } ngOnIni ...

When attempting to compile Typescript code, error TS1128 occurs indicating a missing declaration or statement. However, the code functions correctly when executed through a server

Currently, I'm in the process of developing a project using Angular2. As part of this project, I have created a primary Component class which serves as a central piece: import { Component, OnInit} from '@angular/core'; import { submitServi ...

I encountered an issue with Typescript Jest where it was unable to find the mock or mockReturnedValue functions on the types I

Let's test out this interesting class: //RequestHandler.js import axios, {AxiosInstance} from 'axios'; import settings from './settings'; const axiosHandler: AxiosInstance = axios.create({ baseURL: 'http://localhost:8081&a ...

Is it possible to obtain Literal types for object keys dynamically in typescript?

I am looking to extract the type of object keys. Below is a generic function for objects with keys as strings: type GenericInput = { [key:string]: {value:string,type:HTMLInputTypeAttribute,placeholder:string,min?:number,max?:number,required?:boolean, err ...

"Encountered a TypeError while attempting to send a server action to a custom

My custom form component, <ClientForm>, is built using Radix Primitives. "use client"; import { FC } from "react"; import * as Form from "@radix-ui/react-form"; const ClientForm: FC = (props) => ( <Form.Root {.. ...