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.