Tips for managing dependency injection in Angular unit tests with Jasmine

Embarking on my first experience with implementing unit tests in Angular, I have decided to use Jasmine. Coming from a background in Java\Spring where the Spring framework automatically injected all dependencies into my tests, transitioning to Angular Unit Test with Jasmine presents new challenges for me.

Allow me to elaborate further on the task at hand:

1) The component class PeopleListComponent is as follows:

import { Component, OnInit, ElementRef, ViewChild } from '@angular/core';
import { EventService } from '../event.service';
import interactionPlugin, { Draggable } from '@fullcalendar/interaction';

interface WorkShiftTypes {
  name: string;
  value: string;
}

@Component({
  selector: 'app-people-list',
  templateUrl: './people-list.component.html',
  styleUrls: ['./people-list.component.css']
})
export class PeopleListComponent implements OnInit {

  people: any[];

  workShiftTypes: WorkShiftTypes[];
  selectedShift: WorkShiftTypes;

  @ViewChild('draggable_people') draggablePeopleExternalElement: ElementRef;

  constructor(private eventService: EventService) { }

  ngOnInit(): void {
    this.eventService.getPeople().then(people => {this.people = people;});

    this.selectedShift = {name: 'Morning', value: 'Morning'};

    this.workShiftTypes = [
      {name: 'Morning', value: 'Morning'},
      {name: 'Afternoon', value: 'Afternoon'},
      {name: 'Night', value: 'Night'},
      {name: 'Custom', value: 'Custom'}
    ];
  }

  ngAfterViewInit() {
    console.log("PEOPLE LIST ngAfterViewInit() START !!!")
    var self = this

    new Draggable(this.draggablePeopleExternalElement.nativeElement, {
      itemSelector: '.fc-event',
      eventData: function(eventEl) {
        console.log("DRAG !!!");
        return {
          title: eventEl.innerText,
          startTime: "17:00",
          duration: { hours: 8 }
        };
      }
    });

  }

  createEventObject() {
    return 1;
  }

}

Within this component lies the straightforward method createEventObject():

createEventObject() { return 1; }

This method currently returns only the value 1. To keep things simple for testing purposes, I will initially work with this setup before delving into more complex scenarios.

Now, let's move on to the people-list-spec.ts file which will house the unit test for our previously mentioned method:

import { PeopleListComponent } from "./people-list.component"
import { EventService } from '../event.service';

describe('people-list', () => {

  let eventService = new EventService();
  let component = new PeopleListComponent(eventService);

  it('createEventObject() should return 1', () => {

  })
})

The objective here is simple - obtain an instance of PeopleListComponent, invoke the createEventObject() method, and verify that the returned value is indeed 1 to confirm the success of the unit test.

However, a complication arises when considering that the constructor of PeopleListComponent requires an EventService parameter. It turns out that the EventService constructor itself necessitates an HttpClient parameter.

In my Angular project, dependency injections are handled seamlessly by the framework. While I anticipate that the same logic applies to Angular\Jasmine, I find myself uncertain about the precise course of action.

Answer №1

When working with Angular, the spec file is automatically generated for you. If it doesn't exist, that's not a problem. Let's take a look at what it might look like:

import { PeopleListComponent } from "./people-list.component"
import { EventService } from '../event.service';

describe('people-list', () => {

let eventServiceSpy : jasmine.SpyObj<EventService>;
let component: PeopleListComponent;
let fixture: ComponentFixture<PeopleListComponent>;
 beforeEach(async(() => {
    const eventServiceSpyObj = jasmine.createSpyObj('EventService',['getPeople'])

    TestBed.configureTestingModule({
      declarations: [ PeopleListComponent ],
      providers : [{ provide : EventService, useValue : eventServiceSpyObj }]
    })
    .compileComponents();
    fixture = TestBed.createComponent(PeopleListComponent);
    component = fixture.componentInstance; 
   eventServiceSpy = TestBed.inject(EventService);
  }));


  it('createEventObject()  return 1', () => {
       eventServiceSpy.getPeople.and.returnValue(of(1));
       component.ngOnInit();
       console.log(component.people); // this value should be one as return in mock service.
  })
})

There may be errors related to imports and other aspects of the code. However, this structure provides a general idea of how the method will be set up. Potential errors could arise during the creation of components in the beforeEach function. Feel free to reach out if any errors occur.

Answer №2

Give this a try

import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import { PeopleListComponent } from "./people-list.component"
import { EventService } from '../event.service';
import {HttpClientTestingModule} from '@angular/common/http/testing';

describe('testing people list component', () => {
    let component: PeopleListComponent;
    let fixture: ComponentFixture<PeopleListComponent>;
    let eventServiceSpy : jasmine.SpyObj<EventService>;
        beforeEach(async(() => {
        const eventServiceSpyObj = jasmine.createSpyObj('EventService',['getPeople'])

        TestBed.configureTestingModule({
            declarations: [PeopleListComponent],
            imports: [HttpClientTestingModule],
            providers : [{ provide : EventService, useValue : eventServiceSpyObj }]

        });
        fixture = TestBed.createComponent(PeopleListComponent);
        eventServiceSpy = TestBed.inject(EventService);
        component = fixture.componentInstance;
        fixture.detectChanges();
    }));

    it('checking createEventObject() method returns 1', () => {
        expect(component.createEventObject()).toBe(1)
    })
})

To test the ngOnInit method, here is an example,

it('ngOnInit should initialize people variable correctly', () => {
     const mockPeopleList = [{age: 24, gender: "male"}];

     const fakePromise = new Promise(function(resolve, reject) {
       resolve(mockPeopleList)
     });

      eventServiceSpy.getPeople.and.returnValue(fakePromise);

     component.ngOnInit();

     expect(component.people).toEqual(mockPeopleList)
})

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

Issue with Angular 2 - Struggling with Routing

My objective is to create a menu with four links, each leading to a different HTML page: <p> <a routerLink="/function">Business Function</a> <a routerLink="/process">Business Process</a> <a routerLink="/appsystem"> ...

Should services be directly referenced in HTML templates when using Angular 2?

Is it advisable to reference the service directly in the template by using this code: [disabled]="stateService.selectedClient == null || stateService.currentStep == 1" In my view, this approach does not appear to be a good practice. I would prefer to ha ...

Tips for using MatTableDataSource in a custom Thingsboard widget

After creating multiple custom Thingsboard widgets, I've discovered that I can access a significant portion of @angular/material within my widget code. While I have successfully implemented mat-table, I now want to incorporate pagination, filtering, a ...

What is the method to have VIM recognize backticks as quotes?

Currently working in TypeScript, I am hoping to utilize commands such as ciq for modifying the inner content of a template literal. However, it appears that the q component of the command only recognizes single and double quotation marks as acceptable ch ...

Obtaining the value of an ion-toggle in Ionic2 using the ionChange

Below is the toggle I am referring to: <ion-toggle (ionChange)="notify(value)"></ion-toggle> I am looking for a way to retrieve the value of the toggle when it is clicked in order to pass it as a parameter to the notify method. Any suggestion ...

The PrimeNG pie chart will sporadically appear and adjust its display when the resolution changes

I recently encountered an issue with a primeng pie chart that I am using, which pulls dynamic data from the back-end. Previously, when using static data, the pie chart functioned perfectly. However, after integrating dynamic data, the chart now seems to di ...

Retrieving the row value of a checkbox in an Angular table

I'm facing a challenge where I have a table with three columns, one of which is a checkbox. Here is an image for reference: https://i.sstatic.net/4U6vP.png Here is the code snippet: <div nz-row> <nz-table nz-col nzSpan="22" [nzLoading] ...

Steps to incorporate HTML elements into an Angular component

I have created a custom component with the following structure: @Component({ selector: 'panel-top-page', template: ` <div class="border-radius" nz-row [nzGutter]="5"> </div> `, }) export class PanelT ...

What is the specific category of Mongoose.startSession in the realm of Typescript?

In my Express/Typescript project with mongoose, I implemented a loader as follows: import mongoose from 'mongoose'; import { Db } from 'mongodb'; import config from '../config'; export default async (): Pr ...

What is the C sharp version of this code structure?

I'm curious to know what the C# syntax is for declaring a property like this: filters: { [arg: string]: string }; ...

The mat-datepicker is being displayed beneath the content

My Angular material design datepicker is displaying underneath the content. Within my module, I have a component that is rendered inside app-root. This component includes a mat-stepper with a mat-datepicker inside one of the steps. This is how the genera ...

Having trouble displaying real-time camera RTSP streaming using Angular

I am currently in the process of developing a web application using Angular and I need to incorporate a window that displays live RTSP streaming. Upon conducting research, I discovered that this can be achieved by utilizing the JSMpeg JavaScript library. ...

Troubleshooting property assignment issues within an Angular service

I have created a simple injectable service in TypeScript for Angular 4. This service is designed to fetch a JSON file from the network and store the data as an array in its property called data. Other classes can then access this data using the method getD ...

The form is not valid because the CustomValidator was triggered on a field that was not required

Here is the form setup: this.form = new FormGroup({ someField: new FormControl(''), someOtherField: new FormControl(''), cpf: new FormControl('', [ CustomFormValidators.isValidCPF ]), }); And t ...

Issue with React TSX component in NextJs 14.0.4: Local MP3 files cannot be played, only external online MP3 files work

I have created a component that wraps around HTML audio and source tags. It functions perfectly when playing mp3 files from an external source, like this sound clip . However, it returns a GET 404 error when trying to access local mp3 files. Can anyone exp ...

Tips for choosing the node_modules distribution flavor to include in your webpack bundle

Issue: Following the update of AJV.js to Version 6.4, my vendor bundle now includes the "uri-js" ESNEXT version instead of the ES5 version, causing compatibility issues with IE11. Analysis: Upon investigation, I discovered that AJV references uri-js usi ...

In tsconfig.json, the compiler is not utilizing other tsconfig.json files when using the "extends"

I'm attempting to streamline my project by breaking up my tsconfig.json into separate files. I have one for the source files and another for the tests. However, when I utilize the extends field, it seems that only the base tsconfig.json is being utili ...

Utilizing Angular2 (Frontend) and Spring Boot (Backend) for Excel file uploading

As someone who is new to Angular 2, I am currently attempting to upload an Excel file from the Angular 2 frontend. My goal is to upload the Excel file from the Angular 2 frontend, send it to the Spring Boot backend for necessary modifications, and th ...

Mocking an HttpActionDescriptor using NSubstitute for an ActionName

Currently, I am in the process of creating test cases for my custom AuthorizeAttribute class designed for use in my Web API Controllers. The main objective is to establish a way to simulate the necessary variables required to be configured within my class ...

The received data object appears to be currently undefined

I am currently using MapBox to display multiple coordinates on a map, but I am encountering an issue where the longitude and latitude values in my return object are showing up as undefined. Could there be a missing configuration that is preventing the data ...