What is the best way to verify if an object is an instance of a particular class using Jasmine testing?

In the process of developing an Angular application, I am currently working on testing a component using this guide. My goal is to ensure that when my ngOnInit() method is called, it correctly initializes my foos property with an array of Foo objects based on a provided (mock) service.

it('should have foos after Angular calls ngOnInit', () => {
  component.ngOnInit();
  expect(component.foos).toBeInstanceOf(Foo);
});

While the test works for a single object, it doesn't fulfill the requirement for an array. Testing against Array passes but lacks meaningful validation.

expect(component.foos).toBeInstanceOf(Array);

Attempting to use Foo[] resulted in an error message.

An element access expression should take an argument.

Although TypeScript already provides type checking, I still want to confirm this behavior as a matter of principle.

Answer №1

There isn't a built-in way to perform this validation in Jasmine, as far as I know. However, you can incorporate additional code to accomplish the desired outcome.

  • You have the option to manually compare the contents using Array#every() and then utilize the toBeTrue() matcher.

    expect(someArray.every(x => x instanceof Foo))
      .toBeTrue("Some items don't match");
    
  • Iterate through the array and apply the toBeInstanceOf() matcher for each element. You can also include withContext() to provide more details in case of a mismatch:

    for (const [index, x] of someArray.entries()) {
      expect(x)
        .withContext(`index [${index}]`)
        .toBeInstanceOf(Foo, "hello");
    }
    
  • Loop through the array and trigger fail() with an error message when necessary:

    for (const [index, x] of someArray.entries()) {
      if (!(x instanceof Foo)) 
        fail(`index [${index}] is not Foo`, x);
    }
    

class Foo { constructor(num) { this._num = num} }; // dummy implementation
class Bar { constructor(str) { this._srr = str} }; // dummy implementation

const arrayOfFoos  = [new Foo(1), new Foo(2), new Foo(3)];
const arrayOfBars  = [new Bar("one"), new Bar("two"), new Bar("three")];
const arrayOfMixed = [new Foo(1), new Bar("two"), new Foo(3)];

describe("All Foos", function() {
  beforeEach(function() {
    this.array = arrayOfFoos;
  });
  
  it("every() + toBeTrue()", function() {
    expect(this.array.every(x => x instanceof Foo))
      .toBeTrue("Some items don't match");
  });
  
  it("loop + toBeInstanceOf()", function() {
    for (const [index, x] of this.array.entries()) {
      expect(x)
        .withContext(`index [${index}]`)
        .toBeInstanceOf(Foo);
    }
  });
  
  it("loop + fail()", function() {
    for (const [index, x] of this.array.entries()) {
      if (!(x instanceof Foo)) 
        fail(`index [${index}] is not Foo`, x);
    }
  });
});

describe("All Bars", function() {
  beforeEach(function() {
    this.array = arrayOfBars;
  });
  
  it("every() + toBeTrue()", function() {
    expect(this.array.every(x => x instanceof Foo))
      .toBeTrue("Some items don't match");
  });
  
  it("loop + toBeInstanceOf()", function() {
    for (const [index, x] of this.array.entries()) {
      expect(x)
        .withContext(`index [${index}]`)
        .toBeInstanceOf(Foo);
    }
  });
  
  it("loop + fail()", function() {
    for (const [index, x] of this.array.entries()) {
      if (!(x instanceof Foo)) 
        fail(`index [${index}] is not Foo`, x);
    }
  });
});

describe("Mixed", function() {
  beforeEach(function() {
    this.array = arrayOfMixed;
  });
  
  it("every() + toBeTrue()", function() {
    expect(this.array.every(x => x instanceof Foo))
      .toBeTrue("Some items don't match");
  });
  
  it("loop + toBeInstanceOf()", function() {
    for (const [index, x] of this.array.entries()) {
      expect(x)
        .withContext(`index [${index}]`)
        .toBeInstanceOf(Foo);
    }
  });
  
  it("loop + fail()", function() {
    for (const [index, x] of this.array.entries()) {
      if (!(x instanceof Foo)) 
        fail(`index [${index}] is not Foo`, x);
    }
  });
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.6.0/jasmine.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.6.0/jasmine.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.6.0/jasmine-html.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.6.0/boot.min.js"></script>

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

What is causing the consistent occurrences of receiving false in Angular?

findUser(id:number):boolean{ var bool :boolean =false this.companyService.query().subscribe((result)=>{ for (let i = 0; i < result.json.length; i++) { try { if( id == result.json[i].user.id) ...

Tips for implementing multiple yield generators in redux-saga

Our redux-saga generator has multiple yield statements that return different results. I am struggling with typing them correctly. Here's an illustration: const addBusiness = function* addBusiness(action: AddBusinessActionReturnType): Generator< ...

Flashing Screens with Ionic 2

I am currently dealing with a situation where my login page and homepage are involved. I have implemented native storage to set an item that helps in checking if the user is already logged in (either through Facebook or Google authentication). The logic fo ...

Subclass method overloading in Typescript

Take a look at this example: class A { method(): void {} } class B extends A { method(a: number, b: string): void {} } An error occurs when trying to implement method() in class B. My goal is to achieve the following functionality: var b: B; b.met ...

What could be the cause of this malfunction in the Angular Service?

After creating an Angular app with a controller, I noticed that while I can successfully interact with the controller using Postman (as shown in the screenshot below), I faced issues with displaying data at the frontend. I implemented a new component alon ...

When using Framer Motion for page transitions alongside React Router DOM v6, the layout components, particularly the Sidebar, experience rerenders when changing pages

After implementing page transitions in my React app using Framer Motion and React-Router-DOM, I noticed that all layout components such as the sidebar and navbar were unexpectedly rerendering upon page change. Here's a snippet of my router and layout ...

Messages are not being emitted from the socket

I've been encountering an issue with message transmission from client to server while using React and ExpressJS. When I trigger the sendMessage function on the client side, my intention is to send a message to the server. However, for some reason, the ...

Autoplay feature on Ionic 3 dynamic image slider pauses when manually sliding images

When I retrieve image slider data from a provider's REST API, the autoplay feature works perfectly. However, after manually sliding through the images, the autoplay function stops working. When I try to use ionViewDidEnter(), it triggers an error... ...

How can I retrieve the row index in an Angular Mat-Table that has expandable content?

Having a mat-table with expandable content on a page, I am seeking a solution to record the row number of the table when clicked. While I successfully achieved this with a regular table in the HTML file using: <tr mat-row *matRowDef="let row; columns: ...

Sending a message through Discord.JS to a designated channel

Recently diving into Discord.JS, I am struggling to understand how to make my bot send a message to the General Chat when a new user joins. Many examples I've come across suggest using the following code: const channel = client.channels.cache.find(ch ...

ADAL-Node: Unable to locate tenant groups

When the authority URL is similar to (where the domain name belongs to your tenant), an error occurs: The Get Token request returned an HTTP error: 400 with the server response stating "error description AADSTS90002 Tenant 'organizations' not ...

Content obscuring dropdown menu

I am currently working on a screen design that resembles the following: return ( <SafeAreaView> <View style={styles.container}> <View style={styles.topContainer}> <View style={styles.searchFieldContainer}> ...

Challenges with date formatting arise for Spanish speakers when the date returns as NaN or an Invalid

I have been working on an Angular app Objective: My aim is to allow users to input dates in Spanish format (DD/MM/YYYY) and display them as such, while converting them back to English format when saving the data to the Database. Issue: One problem I enco ...

International replacement of external interface exportation

Currently, I am utilizing the @react-native-firebase/messaging library and invoking the method messaging().onNotificationOpenedApp(remoteMessage => ....) I am aiming to replace the property data within the type of remoteMessage in order to benefit from ...

What are the benefits of utilizing local JSON data in conjunction with useEffect in React?

Encountering the following error message: TS2345: Argument of type '({ Alcohol: number; "Malic Acid": number; Ash: number; "Alcalinity of ash": number; Magnesium: number; "Total phenols": number; Flavanoids: number; &qu ...

Next.js routing can sometimes be unpredictable, especially when navigating from a nested route

I recently set up a directory named dashboard within the pages folder, and it contains the following files: index.tsx customers.tsx invoice.tsx items.tsx When a user navigates to http://localhost:3000/dashboard, the index.tsx page is displayed. However, ...

Sorting through a list of strings by checking for a specific character within each string

After spending years dabbling in VBA, I am now delving into Typescript. I currently have an array of binary strings Each string represents a binary number My goal is to filter the array and extract all strings that contain '1' at position X I ...

Transforming a mongodb operation into an asynchronous function using await and async syntax

After calling the function to retrieve data from MongoDB, an undefined error occurs. It is suspected that converting the function to an async/await function may resolve this issue. However, there is uncertainty on how to make this conversion without disrup ...

"Combining the power of AngularJS2 beta with Spring Boot: the ultimate

I am currently using Atom 1.4.0 with the atom-typescript package to develop AngularJS2 modules in TypeScript. On the backend, I have a spring-boot application for handling the REST APIs. After making changes to the .ts files in Atom, it seems to compile t ...

Creating a JSON structure using an array in Typescript

Here is an example of my array structure: [ { "detail": "item1", "status": "active", "data": "item1_data" }, { "detail": "item2", "status": ...