Understanding Unit Testing in Angular: Mocked Service API Calls and Component Rendering

In the realm of Angular development, unit testing is an essential practice that ensures your application behaves as expected. One common scenario is testing components that rely on services to fetch data. This article will walk you through a practical example of how to mock a service in an Angular unit test and verify that the component renders the expected output.

Mock Service API Calls Example Scenario

Imagine you have an Angular component, AppComponent, which displays product information fetched from a service, AppService. We want to write a unit test to ensure that AppComponent correctly renders the product title.

Here’s the unit test code we’ll be examining:

import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { AppService } from './app.service';
import { of } from 'rxjs/internal/observable/of';

describe('AppComponent', () => {

  beforeEach( () => {
    TestBed.configureTestingModule({
      imports: [AppComponent],
      providers: [AppService],
    }).compileComponents();
  });

  it('should render with mocked title', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const service = fixture.debugElement.injector.get(AppService);
    let mockData = {
      id: 1,
      title: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptop',
      price: 109.95,
      description:
        'Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday',
      category: "women's clothing",
      image: 'https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg',
      rating: { rate: 3.9, count: 120 },
    };

    spyOn(service, 'getData').and.returnValue(of(mockData));
  
    fixture.detectChanges();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('h1')?.textContent).toContain('Hello, Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptop');
    
  });


});

Breaking Down the Test

Let’s dissect this code to understand each part and its purpose.

Setting Up the Test Environment

The first step is to create a testing module for the AppComponent:

const fixture = TestBed.createComponent(AppComponent);

TestBed.createComponent(AppComponent) sets up an instance of AppComponent within a testing environment. The fixture is a wrapper around the component instance, providing utilities to interact with it.

Injecting the Service

Next, we retrieve the AppService from the component’s injector:

const service = fixture.debugElement.injector.get(AppService);

This allows us to mock the service’s methods and control the data returned to the component.

Mocking Service Data

We define mock data that represents the product information:

let mockData = {
  id: 1,
  title: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptop',
  price: 109.95,
  description: 'Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday',
  category: "women's clothing",
  image: 'https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg',
  rating: { rate: 3.9, count: 120 },
};

Mocking Service API Calls

We then use spyOn to mock the getData method of the service to return this mock data:

spyOn(service, 'getData').and.returnValue(of(mockData));

spyOn replaces the getData method with a function that returns an Observable emitting mockData.

Initializing the Component

We obtain the component instance and trigger change detection:

const component = fixture.componentInstance;
fixture.detectChanges();

fixture.detectChanges() processes the bindings and lifecycle hooks, ensuring the component reflects the mocked data.

Verifying the Output

Finally, we check that the component’s rendered output contains the expected title:

const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptop');

compiled refers to the native DOM element of the component. We use querySelector to find the <h1> element and assert that its text content includes the product title from our mock data.

Conclusion

Unit testing in Angular involves creating a controlled environment where components and services can be isolated and tested independently. By mocking services, we can ensure that components render correctly based on various data scenarios. This practice helps maintain robust and reliable applications, catching issues early in the development process.

In this example, we’ve demonstrated how to mock a service in Angular and verify that a component renders the expected content. Mastering these techniques empowers you to write comprehensive tests that cover a wide range of use cases, ensuring your Angular applications perform flawlessly in production.

Leave a Reply

Your email address will not be published. Required fields are marked *