简体   繁体   中英

How to properly mock an overloaded Typescript function using Jest

I am trying to mock a single function on an instance using Typescript and Jest. Then instance is an OAuth2Client from Google's Auth Library. Here is my module under test:

sample.ts

import {OAuth2Client } from "google-auth-library";

export async function accessToken(): Promise<string | undefined> {
    const client: OAuth2Client = new OAuth2Client(
        "clientId", "clientSecret", "https://redirect.com"
    );
    const response = await client.getToken("code");
    return response?.tokens.access_token;
}

For the test, I'd like to return a fixed response from the getToken call. What makes this a bit more complicated, is that getToken is overloaded and that the google-auth-library is Typescript.

getToken(code: string): Promise<GetTokenResponse>;
getToken(options: GetTokenOptions): Promise<GetTokenResponse>;
getToken(code: string, callback: GetTokenCallback): void;
getToken(options: GetTokenOptions, callback: GetTokenCallback): void;

This is my test:

sample.test.ts

import { OAuth2Client } from 'google-auth-library';
import {accessToken} from "./sample";
import {GetTokenResponse} from "google-auth-library/build/src/auth/oauth2client";

type GetTokenByCode = (code: string) => Promise<GetTokenResponse>

const oAuth2Client = jest.spyOn(OAuth2Client.prototype, 'getToken') as unknown as jest.MockedFunction<GetTokenByCode>;
oAuth2Client.mockImplementation((code: string) => {
    if (code != "code") return Promise.reject("Unexpected code.");

    const response = {
        tokens: {
            refresh_token: `abc${code}`,
            access_token: "def"
        },
        res: null
    }
    return Promise.resolve(response);
});


describe('Sample', function () {
    beforeEach(() => {
        jest.resetModules();
        jest.restoreAllMocks();
    });

    it('can get a token', async () => {
        const token = await accessToken();
        expect(token).toBeDefined();
        expect(oAuth2Client).toHaveBeenCalled();
    });
});

When I run this test, I receive the following error - which indicates that the client is trying the real call.

Error: invalid_client

    at Gaxios._request (/Users/foo/dev/token-service/node_modules/gaxios/src/gaxios.ts:158:15)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at OAuth2Client.getTokenAsync (/Users/foo/dev/token-service/node_modules/google-auth-library/build/src/auth/oauth2client.js:124:21)
    at Object.accessToken (/Users/foo/dev/token-service/src/functions/sample.ts:7:22)
    at Object.<anonymous> (/Users/foo/dev/token-service/src/functions/sample.test.ts:29:23)

Looking at the file .. google-auth-library/build/src/auth/oauth2client.js , it seems that the TS transpiler output a js-function with signature getToken(codeOrOptions, callback) - which then calls getTokenAsync .

I am a bit lost here, am I mocking the wrong overload with jest.spy ?

I believe the problem is this:

const client: OAuth2Client = new OAuth2Client(
  "clientId", "clientSecret", "https://redirect.com"
);

While what you are actually trying to mock is the prototype parent object.

const oAuth2Client = jest.spyOn(OAuth2Client.prototype, 'getToken') as unknown as jest.MockedFunction<GetTokenByCode>;

I would try the following:

  • Nuke prototype off the mock
  • See if you can avoid creating instances in the system under test via Dependency Injection

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