Unit testing SharePoint Framework code requires mocking many parts of the SDK: spHttpClient, serviceScope, aadTokenProviderFactory. To make the process simpler, my colleague Marcin started a public project called spfx-ut-library and invited me to collaborate. I’ve created the mock for AadTokenProviderFactory. In this article, I describe how to use it with Jest.
Installation and configuration
To execute the library, a test runner is needed. Install Jest and optionally chai, as an assertion library. First, execute the command.
npm i jest @types/jest chai @types/chai ts-jest spfx-ut-library -D
In the meantime, add Jest config to the package.json file.
"jest": {
"setupFilesAfterEnv": [
"./tests/setup.ts"
],
"testEnvironment": "jsdom",
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
}
},
Note that testEnvironment is set to jsdom. Some other tests may require a “node” testEnvironment. This line can be replaced with docblock at the beginning of the test file.
/**
* @jest-environment jsdom
*/
The last configuration step is to add {projectFolder}/tests/setup.ts file with the following content.
///<reference types="jest" />
import { JestHelper } from "spfx-ut-library/lib/helpers/JestHelper";
JestHelper.registerMocks(jest);
Example
For the purpose of this tutorial I’ve created a TokenProvider class.
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { Log } from "@microsoft/sp-core-library";
export class TokenProvider {
constructor(private context: WebPartContext) {}
public async getToken(resource: string): Promise<string> {
try {
const tokenProvider = await this.context.aadTokenProviderFactory.getTokenProvider();
const token = await tokenProvider.getToken(resource);
return token;
} catch (error) {
Log.error("TokenProvider", error);
throw error;
}
}
}
This class should return a token for the requested resource, or throw and log an error. The class uses three objects that have to be mocked: aadTokenProviderFactory, aadTokenProvider, and Log.
///<reference types="jest" />
import { assert } from "chai";
import { TokenProvider } from "../src/TokenProvider";
import { SPWebPartContextMock } from "spfx-ut-library";
import { Log } from "@microsoft/sp-core-library";
jest.mock("@microsoft/sp-core-library", () => ({
Log: {
error: jest.fn(),
},
}));
describe("TokenProvider", () => {
let mockContext = new SPWebPartContextMock();
const tokenProvider = new TokenProvider(mockContext as any);
afterEach(() => {
mockContext.aadTokenProviderFactory.aadTokenProviderMock.clearMocks();
(Log.error as jest.Mock).mockClear();
});
test("Should return graph token", async () => {
const TOKEN = "token";
mockContext.aadTokenProviderFactory.aadTokenProviderMock.registerToken("https://graph.microsoft.com", TOKEN);
const token = await tokenProvider.getToken("https://graph.microsoft.com");
assert.equal(token, TOKEN);
});
test("Should throw error with errorCode = invalid_resource when resource is unavailable", async () => {
try {
await tokenProvider.getToken("https://fabricated.service.microsoft.com");
assert.fail("Should throw error");
} catch (error) {
assert.equal(error.errorCode, "invalid_resource");
}
});
});
The mock has to be created.
let mockContext = new SPWebPartContextMock();
Then the getToken function can be stubbed.
mockContext.aadTokenProviderFactory.aadTokenProviderMock.registerToken("https://graph.microsoft.com", TOKEN);
Jest mocks Log.error function, as it is a part of a different node package. In the second test case, the error is not mocked. The library will throw a default error which is almost identical to an error thrown by API when the resource is not available.
test("Should throw custom error", async () => {
mockContext.aadTokenProviderFactory.aadTokenProviderMock.registerError("https://fabricated.service.microsoft.com", {
errorCode: "custom_error",
errorMessage: "Custom error message",
message: "Custom error message",
name: "Custom error name",
stack: "Custom error stack",
});
try {
await tokenProvider.getToken("https://fabricated.service.microsoft.com");
assert.fail("Should throw error");
} catch (error) {
assert.equal(error.errorCode, "custom_error");
assert.equal(error.errorMessage, "Custom error message");
assert.equal(error.message, "Custom error message");
assert.equal(error.name, "Custom error name");
assert.equal(error.stack, "Custom error stack");
}
});
If you want to learn more about sfpx-ut-library please visit its GitHub. The full example is available in my GitHub repository.
I’m SharePoint enthusiast working currently at Avenga. I like to test new technologies, unconventional solutions and share my ideas. I’m dealing with the online version of SharePoint, Azure, and some other Office 365 applications but I have experience with on-premises as well.