Step-by-step guide on monitoring a service function that invokes another service and receives a promise in TypeScript

Parent Service:

module proj.Stuff {
  export class ParentService {
    //...properties, constructor, etc

    public updateContent(id: number) {
      this.dataService
        .getContent(id)
        .then((response) => this.content = response);
      }
   }
}

Child service:

module proj.Stuff {
  export class ChildService{
    //... properties, constructor, etc

    public getContent(id: number) {

      var request: IPromise<any> = this.$http.get(
      ChildService.apiUrlBase + "getContent/" + id
      );

      return request
       .then(response => {
         return response.data.value;
      }, response => {
        this.$log.error("unable to get...");
      });
    }
  }
}

Tests for the parent service:

describe("ParentService", () => {

  // (property declarations omitted for brevity)

  beforeEach(angular.mock.module(["$provide", ($provide) => {

    var obj = {
      getContent: (id: number) => {
        functionCalled = true;
        return {
          then: (callback) => {
            return callback(["result"]);
          }
        };
      }
    };

    $provide.value("ChildService", obj);
  }]));

  beforeEach(mock.inject((_$http_, _$log_, _$q_, _$httpBackend_, _$rootScope_, _ChildService_) => {
    cService = _ChildService_;
    pParentService = new ParentService(cService);
  }));

  it("can be created", () => {
    expect(pParentService).toBeDefined();
    expect(pParentService).not.toBeNull();
  });

  it("can update content", () => {
    pParentService.updateContent(1);
    expect(pParentService.content).toEqual(["result"]);
    expect(functionCalled).toBeTruthy();

    // ***** what I want to do: *****
    // expect(cService.getContent).toHaveBeenCalled();
  });
});

I'm wondering how can I spy on cService.getContent instead of using the 'functionCalled' boolean?

When I try to spy on it, it complains that .then isn't defined - e.g. in the first beforeEach if I try spyOn(obj, "getContent") it doesn't like it.

The tests pass as is, but would rather spyOn instead of using the boolean.

Answer №1

Using Angular DI for unit testing is a great way to eliminate the need for method mocks, allowing seamless integration of unmocked promises.

  beforeEach(angular.mock.module(["$provide", ($provide) => {
    // Easy injection of $q with $provide.factory
    $provide.factory("ChildService", ($q) => ({
      // Generating fresh promises for testing
      getStuff: jasmine.createSpy('getStuff').and.callFake(() => $q.when('result'))
    }));
  }]));

To enhance your testing experience, consider using Jasmine promise matchers. If not, stick to standard promise specs:

var result;
...then((_result) => { result = _result; })
$rootScope.$digest();
expect(result)...

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

Updating Angular-footable values when REST calls are initiated: A complete guide

When my controller initializes, I fetch all the necessary data for my table from a rest call. Here is the code snippet: tableService.getList(function(response) { $scope.back = response blockUI.start("Wait..."); ...

Guide on importing and using types from an external library in my declaration file

Querying Library Types in a .d.ts Declaration File I am currently working on creating a custom namespace in a cf.d.ts file to define general-purpose types for my application. This allows me to avoid importing modules repeatedly whenever I need to referenc ...

Refreshing ng-repeat dynamically with $http without reloading the page

Just a quick heads-up, I'm new to AngularJS In my AngularJS application, I am using the $http service to fetch data. Each row has an update button that needs to trigger a server-side method (the API is already returning data) to sync with a third-par ...

Refreshing Angular navigation directive post user authentication

As I delve into mastering AngularJS and embark on my inaugural "real" project, I find myself at a crossroads. Despite hours of scouring the internet in search of answers, I have yet to stumble upon a suitable solution that speaks to me in layman's ter ...

What is the best way to display audio files for clients using a combination of Node.js and AngularJS?

After successfully converting my wav files to mp3 files using the ffmpeg converter, I am able to upload them to a designated "uploads" folder. However, I am facing difficulty in displaying these files on the browser. My current setup involves utilizing An ...

When clicking the button, the service function is not properly updating the view

Upon calling this.getLeaderboard(); in the ngOnInit() function within leaderboard.component.ts, the leaderboard is only displayed on page load or refresh, which is expected. However, I also want to retrieve and display the leaderboard upon clicking a butto ...

AngularJS Service that handles several independent queries

I'm struggling with creating an AngularJS service that fetches data from multiple HTTP requests. Despite my efforts, I can't seem to get it working. The REST call flow is as follows: Get /index which returns an array of URLs Call each URL and ...

What is the process of type checking in Typescript when passing arguments?

I'm curious about TypeScript and why the two function calls below result in different type checking outcomes. Can someone shed some light on this for me? interface LabelledValue { label: string; } function printLabel(labelledObj: LabelledValue) ...

An error occurred due to attempting to access properties of null while trying to read 'useMemo' in a Next.js TypeScript application

Currently engaged in a Next.js 13 project with TypeScript, utilizing the useDrag hook. No errors are being flagged in my Visual Studio Code editor; however, upon attempting to render the page, an error message surfaces. The issue points to a problem with t ...

The Angular JS Root scope is modified after submitting a form

I'm new to Angular JS and I'm trying to figure out how to save an object into $rootScope in my application. However, when I try to make a post request without including the object from rootScope, it doesn't work as expected. Now, on a newly ...

AngularJS - Dropdown Select Options appear out of sequence when used with ng-repeat

I am facing an issue with a select drop down menu that is displaying data in a different order than expected. The initial data and its output to the console in Chrome are as follows: { 8: "Something", 9: "Something Again!", 10: "And again", ...

Is it logical for `string & any[]` to lead to a `never` result?

There's something peculiar going on with TypeScript. I have a type union that includes array types (string[], number[]) and non-array types (string, number). When using type inference, everything behaves as expected: type bar = string | number | stri ...

How can one obtain the alphabet letters of a specific language?

Is there a way to programmatically retrieve the alphabet of a language (using its 2-letter ISO code) from an open-source repository? For instance: console.log(getAlphabet('en')); would result in: a b c d ... while console.log(getAlphabet(&apos ...

What is the process for importing files with nested namespaces in TypeScript?

Currently, I am in the process of transitioning an established Node.js project into a fully TypeScript-based system. In the past, there was a static Sql class which contained sub-objects with MySQL helper functions. For instance, you could access functions ...

I am unsure why it is displaying these errors

I created an auto-fill form that populates data based on ng-select information automatically. However, I am encountering an issue when attempting to delete selected data as it is throwing a Cannot read property 'pincode' of null error. Any help i ...

Tips for obtaining the width of a child element during a resize event in an Angular application

When resizing the window, I am attempting to determine the width of a specific sub-component. If I want to retrieve the entire app's width, I can use the following code: @HostListener('window:resize', ['$event']) onResize( ...

determining the data type based on the function parameter even when a specific type parameter is provided

Consider this example: type UpdateFieldValue<T extends Record<string, unknown>> = (key: keyof T, value: SomeType) => void The goal is to have SomeType represent the value type of the property (key) within object T, with key being provided t ...

What strategies can be used to streamline two React components with very similar logic?

Currently, I have two components named DateTimeFormField and TimeFormField that require refactoring due to their shared logic and output similarity. Initially, my idea was to export the functions from the functional component DateTimeFormField. However, t ...

I'm having trouble resolving this error that keeps popping up on this particular line of code: "registrationForm : FormGroup;" Can anyone help me troubleshoot this

***Issue: src/app/r-form-example/r-form-example.component.ts:11:3 - error TS2564: Property 'registrationForm' lacks an initializer and is not definitely set in the constructor. An error has been detected in the code snippet above. import { Comp ...

Angular HttpInterceptor failing to trigger with nested Observables

Utilizing a HttpInterceptor is essential for adding my Bearer token to all calls made to my WebApi. The interceptor seamlessly functions with all basic service calls. However, there is one specific instance where I must invoke 2 methods and utilize the re ...