Home > Net >  How to mock service's function in component's test with Jest
How to mock service's function in component's test with Jest

Time:04-11

I'm having a hard time trying to test an angular component with Jest.

I have this component:

media-image.component.ts

import { Component, Input } from '@angular/core'
import { SanityService } from '@services/sanity/sanity.service'
import Waypoint from '@interfaces/waypoint'

@Component({
  selector: 'app-media-image',
  templateUrl: './media-image.component.html',
  styleUrls: ['./media-image.component.scss']
})
export class MediaImageComponent {
  @Input() mediaData: Waypoint = null

  constructor(private _sanity: SanityService) {}

  imageUrl(source: any) {
    return this._sanity.urlFor(source)
  }
}

imageUrl is called in the template

This component needs the SanityService

sanity.service.ts

import { Injectable } from '@angular/core'
import { environment } from '@environments/environment'
import imageUrlBuilder from '@sanity/image-url'
import sanityClient from '@sanity/client'

@Injectable({
  providedIn: 'root'
})
export class SanityService {
  sanityClientCredentials = {
    option: sanityClient({
      projectId: environment.sanity.projectId,
      dataset: environment.sanity.dataset,
      apiVersion: environment.sanity.apiVersion
    })
  }

  urlFor(source: any) {
    return imageUrlBuilder(this.sanityClientCredentials.option).image(source).url()
  }


}

I would like to mock the urlFor function of the service, just to check if it is called with the correct argument.

Here are my attempts:

import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'
import { IonicModule } from '@ionic/angular'
import { MediaImageComponent } from './media-image.component'
import { SanityService } from '../../../services/sanity/sanity.service'

import { waypointImage } from '../../../mocks/waypoint.mocks'

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [MediaImageComponent],
        providers: [{ provide: SanityService }],
        imports: [IonicModule.forRoot()]
      }).compileComponents()

      fixture = TestBed.createComponent(MediaImageComponent)
      component = fixture.componentInstance
      component.mediaData = waypointImage
      fixture.detectChanges()
    })
  )

  it('should create', () => {
    // First
    jest.mock('../../../services/sanity/sanity.service', () => {
      return {
       urlFor: jest.fn()
      }
    })
   
   // Second
   const mockSanityService = SanityService as jest.Mock<SanityService> // to avoid typescript alerts
   const mockService = jest
      .spyOn(mockSanityService.prototype, 'urlFor')
      .mockImplementation((source) => {return 'test'})
    })

    expect(mockService).toHaveBeenCalled()
    expect(component.imageUrl).toHaveBeenCalled()
    expect(component).toBeTruthy()
  })
})

It' seems my mock is ignored. I always get an error from the @sanity/image-url package which waits for specific data.

What I am doing wrong? What I am not understanding?

Thank you for your help!

CodePudding user response:

I'm not sure it will help, but what I do in my Jest tests when I need to mock a service is the following:

jest.mock('...../myService');

describe('...', () => {
    let myServiceMock: MyService;
    ...

    beforeEach(() => {
        myServiceMock = TestBed.inject(myServiceMock);
        ...

        jest.spyOn(myServiceMock, 'someServiceMethod').mock...
    });
});

CodePudding user response:

Finally find a way to achieve that, thanks to this post : Testing Angular Component using JEST

Here is my test:

import { ComponentFixture, TestBed } from '@angular/core/testing'
import { IonicModule } from '@ionic/angular'
import { MediaImageComponent } from './media-image.component'
import { SanityService } from '../../../services/sanity/sanity.service'

import { waypointImage } from '../../../mocks/waypoint.mocks'

const mockSanityService = {
  urlFor: jest.fn()
}

describe('MediaImageComponent', () => {
  let component: MediaImageComponent
  let fixture: ComponentFixture<MediaImageComponent>
  let spy
  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [MediaImageComponent],
        providers: [{ provide: SanityService, useValue: mockSanityService }],
        imports: [IonicModule.forRoot()]
      }).compileComponents()

      fixture = TestBed.createComponent(MediaImageComponent)
      component = fixture.componentInstance
      component.mediaData = waypointImage
      spy = jest.spyOn(component, 'imageUrl')
      fixture.detectChanges()
    })
  )

  afterEach(() => {
    if (fixture) {
      fixture.destroy()
    }
    mockSanityService.urlFor.mockReset()
    spy.mockClear()
  })

  it('should create', () => {
    mockSanityService.urlFor.mockImplementationOnce(() => 'plop')

    expect(mockSanityService.urlFor).toHaveBeenCalled()
    expect(spy).toHaveBeenCalled()
    expect(component).toBeTruthy()
  })
})

I hope it will be useful for someone else :)

  • Related