简体   繁体   中英

How to mock implementation of a function called by a service being tested?

I have a NestJS project I'm working on, and I need to write unit tests of my services.

I have a service called BigQueryService that uses @google-cloud/bigquery to access a Big Query dataset and perform a query. I also have another service (lets call it MyService) whose job is to build the query that I need depending on some other logic, and pass it to the BigQueryService, receive the query result from it and return it to the controller, which will in turn send the data through an endpoint.

I need to write unit tests for MyService, and for that I need to mock the BigQueryService in a way that doesn't require it to resolve BigQueryService's dependencies. Here's some of my code:

bigquery.service.ts:

import { Injectable } from '@nestjs/common';
import { BigQuery } from '@google-cloud/bigquery';
...
@Injectable()
export class BigqueryService {
  ...
  constructor(
    ...
  ) {
    ...
  }

  async executeQuery(querySentence: string): Promise<Array<any>> {
    ...
    return response;
  }
}

MyService.service.ts:

import { Injectable } from '@nestjs/common';
import { BigqueryService } from '../bigquery/bigquery.service';
//the following is just a service that helps log the results of this service
import { MyLogger } from '../../config-service/logger.service';
...
@Injectable()
export class MyService {
  constructor(
    private readonly bigqueryService: BigqueryService,
    private readonly logger: MyLogger,
  ) { }
  ...
  async myFunc(request: RequestInterface): Promise<Array<ResponseInterface>> {
    let query = (some code to create a query i want)
    return await this.bigqueryService.executeQuery(query);
  }

For the tests, I followed the answers in this thread: Mock a method of a service called by the tested one when using Jest

jest.mock('../services/bigquery/bigquery.service', () => jest.fn()) 
const bq = require('../services/bigquery/bigquery.service')
jest.mock('../config-service/logger.service', () => jest.fn())
const ml = require('../config-service/logger.service')

const executeQuery = jest.fn()
executeQuery.mockReturnValue('desired value')
bq.mockImplementation(() => ({executeQuery}))


describe("Testing consumption moment service function", () => {

  it("should call the mock service", () => {
    const ms = new MyService(bq,ml)
    ms.myFunc(requestBody) //requestBody is a RequestInterface
    expect(bq.executeQuery).toHaveBeenCalled
    expect(bq.executeQuery).toHaveReturned
 });
});

That test passes, so I assume I correctly mocked the bigquery service. But when I try to assert that the value returned is the correct one, I make the test async so that the test wont finish until myFunc is actually done running and I can have a result to compare.

 it("should call the mock service", async () => {
   const ms = new MyService(bq,ml) 
   await ms.myFunc(requestBody)
   expect(bq.executeQuery).toHaveBeenCalled
   expect(bq.executeQuery).toHaveReturned
 });

This gets an error: TypeError: this.bigqueryService.executeQuery is not a function The error points to the line where myFunc calls this.bigqueryService.executeQuery.

I've tried different examples of mocking so that I can mock the call to this function but none got as close as the example above. I also tried to use

jest.spyOn(bq, 'executeQuery') 

But that also said that executeQuery wasn't a function: Cannot spy the executeQuery property because it is not a function; undefined given instead

Can someone point me in the right direction here? Is there something I'm missing to make this test work? I thank you all in advance for any help you can give me.

bq.mockImplementation(() => ({executeQuery}))

Isn't async, try returning a promise

bq.mockImplementation(() => (Promise.resolve({executeQuery})))

I ended up figuring it out, so if anyone is in the same situation, here is where I found the answer: https://jestjs.io/docs/en/jest-object

The test was fixed like this:

jest.mock('../config-service/logger.service', () => jest.fn())
const ml = require('../config-service/logger.service')
const executeQuery = jest.fn()

describe("Testing service function", () => {

  it("should call the mock service", async () => {
    jest.mock('../services/bigquery/bigquery.service', () => {
      return {
        executeQuery: jest.fn(() => 'desired output'),
      };
    })
    const bq = require('../services/bigquery/bigquery.service')

    const ms = new MyService(bq,ml)
    const p = await ms.myFunc(requestBody) //requestBody is a RequestInterface
    expect(bq.executeQuery).toHaveBeenCalled
    expect(bq.executeQuery).toHaveReturned
    expect(p).toEqual(desired result)
 });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM