How to get a Keycloak mock to work with Jest

Viewed 3550

I am trying to set up a mock function for the Keycloak library for use in my ReactJS application, but so far efforts have been frustrated. I have looked at other questions on mocking node modules in Jest but the answers have not helped as I feel there is missing information in the answers to explain it to non-advanced JS coders like myself.

Here is the mock function I have created for keycloak:

export function useKeyCloak() {
  const token = "A random string that is non zero length";
... OTHER CONSTANTS ...

  return {
    authenticated,
... OTHER PROPERTIES AND METHODS ...
    token,
  };
}

Per the instructions on mocking node modules in jest, I have put my mock keycloak function in the appropriate folder thus:

src
- __mocks__
  - @react-keycloak
    - web.js

My setupTests.js file looks like this:

import Adapter from 'enzyme-adapter-react-16';
import { configure } from 'enzyme';
import fetch from './__mocks__/fetch';
 
global.fetch = fetch;
 
configure({adapter: new Adapter()});

And lastly, an actual test that wants to run the mock keycloak call looks like this:

import { useKeycloak } from "@react-keycloak/web";
import { shallow } from "enzyme/build";
import User from "../components/User";
import UserInfo from "../components/UserInfo";

jest.mock(useKeycloak);

describe("Keycloak instance", () => {
  it("loads keyCloak and returns a token, realmAccess roles and a userProfile", () => {
    const userComp = shallow(<User />);
    expect(userComp.containsMatchingElement(<UserInfo />)).toBe(true);
  });
});

However, this produces the following error when run:

TypeError: (0 , _web.useKeycloak) is not a function

Based on other questions, I understand this is something to do with hoisting, but I just can't seem to get the correct syntax. I've also tried the following in place of jest.mock(useKeycloak):

jest.mock("@react-keycloak/web");

or

jest.mock("@react-keycloak/web", () => ({ useKeycloak: jest.fn() }));

Could someone who is more advanced in JS than me tell me what will work on this line? Ideally a syntax that is confirmed to work in a test. Would be greatly appreciated.

2 Answers

Two errors:

  • useKeyCloak instead of useKeycloak (camel-case mismatch)
  • I was returning an object that could not be destructured. Instead the mock module should have read:
export function useKeycloak() {
  const token = "A random string that is non zero length";
  const userProfile = { username: "test", email: "test@testdomain.com", firstName: "Test", lastName: "User" };
  const realmAccess = { roles: ["admin", "auditor", "user"] };
  let authenticated = false;

  const authClient = {
    authenticated,
    hasRealmRole(role) {
      return true;
    },
    hasResourceRole(role) {
      return true;
    },
    idToken: token,
    initialized: true,
    loadUserProfile() {
      return Promise.resolve({ userProfile });
    },
    login() {
      authenticated = true;
    },
    logout() {
      authenticated = false;
    },
    profile: userProfile,
    realm: "DemoRealm",
    realmAccess,
    refreshToken: token,
    token,
  };
  return { initialized: true, keycloak: authClient };
}

Typescript version (@react-keycloak/web version 3.4.0):

I got this working in TypeScript, though it's far from pretty.

jest.mock("@react-keycloak/web", () => ({ useKeycloak: mockUseKeycloak }));

// ...

function mockUseKeycloak() {
  const token = "A random string that is non zero length";
  const userProfile: KeycloakProfile = {
    username: "test",
    email: "test@example.com",
    firstName: "Test",
    lastName: "User",
  };
  const realmAccess = { roles: ["user"] };

  const authClient: Keycloak = {
    authenticated: true,
    hasRealmRole(ignored: string) {
      return true;
    },
    hasResourceRole(ignored: string) {
      return true;
    },
    idToken: token,
    profile: userProfile,
    realm: "TestRealm",
    realmAccess,
    refreshToken: token,
    token,
  } as Keycloak;
  return { initialized: true, keycloak: authClient };
}

I have tried using the code from react-keycloak itself, but somehow I failed there. Maybe, to someone else, this reference may be useful: https://github.com/react-keycloak/react-keycloak/blob/f2d15949e87c7fc6de0dbba9419999f6a6b9160e/packages/core/test/test-utils.tsx#L8 https://github.com/react-keycloak/react-keycloak/blob/f2d15949e87c7fc6de0dbba9419999f6a6b9160e/packages/core/test/provider.spec.tsx#L30

Related