Test utilities built for Jaypie
npm install --save-dev @jaypie/testkit
The testkit provides a complete mock for Jaypie including:
- Log spying (
expect(log.warn).toHaveBeenCalled()
) - Default responses for runtime-only functions (
connect
,sendMessage
,submitMetric
) - No automatic error handling for handlers (which is good in production but obfuscates tests)
- Most non-utility functions are mocked to allow simple testing
vi.mock("jaypie", async () => vi.importActual("@jaypie/testkit/mock"));
import { ConfigurationError } from "@jaypie/core";
vi.mock("jaypie", async () => vi.importActual("@jaypie/testkit/mock"));
test("ConfigurationError", () => {
try {
throw new ConfigurationError("Sorpresa!");
} catch (error) {
expect(error).toBeJaypieError();
expect(ConfigurationError).toHaveBeenCalled();
}
});
import { log } from "jaypie";
vi.mock("jaypie", async () => vi.importActual("@jaypie/testkit/mock"));
afterEach(() => {
vi.clearAllMocks();
});
test("log", () => {
expect(vi.isMockFunction(log.warn)).toBe(true);
expect(log.warn).not.toHaveBeenCalled();
log.warn("Danger");
expect(log.warn).toHaveBeenCalled();
expect(log.error).not.toHaveBeenCalled();
});
👺 Logging Conventions:
- Only use
log.trace
orlog.var
during "happy path" - Use
log.debug
for edge cases - Now you can add an "observability" test that will fail as soon as new code triggers an unexpected edge condition
describe("Observability", () => {
it("Does not log above trace", async () => {
// Arrange
// TODO: "happy path" setup
// Act
await myNewFunction(); // TODO: add any "happy path" parameters
// Assert
expect(log).not.toBeCalledAboveTrace();
// or individually:
expect(log.debug).not.toHaveBeenCalled();
expect(log.info).not.toHaveBeenCalled();
expect(log.warn).not.toHaveBeenCalled();
expect(log.error).not.toHaveBeenCalled();
expect(log.fatal).not.toHaveBeenCalled();
});
});
👺 Follow the "arrange, act, assert" pattern
testSetup.js
import { matchers as jaypieMatchers } from "@jaypie/testkit";
import * as extendedMatchers from "jest-extended";
import { expect } from "vitest";
expect.extend(extendedMatchers);
expect.extend(jaypieMatchers);
test.spec.js
import { ConfigurationError } from "@jaypie/core";
const error = new ConfigurationError();
const json = error.json();
expect(error).toBeJaypieError();
expect(json).toBeJaypieError();
import {
LOG,
jsonApiErrorSchema,
jsonApiSchema,
matchers,
mockLogFactory,
restoreLog,
spyLog,
} from '@jaypie/testkit'
LOG
constant provided by @jaypie/core
for convenience
import { log } from "@jaypie/core";
import { LOG } from "@jaypie/testkit";
const libLogger = log.lib({ level: LOG.LEVEL.WARN, lib: "myLib" });
A JSON Schema validator for the JSON:API error schema. Powers the toBeJaypieError
matcher (via toMatchSchema
).
A JSON Schema validator for the JSON:API data schema.
export default {
toBeCalledAboveTrace,
toBeCalledWithInitialParams,
toBeClass,
toBeJaypieError,
toBeValidSchema: jsonSchemaMatchers.toBeValidSchema,
toMatchBase64,
toMatchJwt,
toMatchMongoId,
toMatchSchema: jsonSchemaMatchers.toMatchSchema,
toMatchSignedCookie,
toMatchUuid4,
toMatchUuid5,
toMatchUuid,
toThrowBadGatewayError,
toThrowBadRequestError,
toThrowConfigurationError,
toThrowForbiddenError,
toThrowGatewayTimeoutError,
toThrowInternalError,
toThrowJaypieError,
toThrowNotFoundError,
toThrowUnauthorizedError,
toThrowUnavailableError,
};
testSetup.js
import { matchers as jaypieMatchers } from "@jaypie/testkit";
import * as extendedMatchers from "jest-extended";
import { expect } from "vitest";
expect.extend(extendedMatchers);
expect.extend(jaypieMatchers);
import { log } from "@jaypie/core";
log.trace("Hello, World!");
expect(log).not.toBeCalledAboveTrace();
log.warn("Look out, World!");
expect(log).toBeCalledAboveTrace();
Validates instance objects:
try {
throw new Error("Sorpresa!");
} catch (error) {
expect(error).not.toBeJaypieError();
}
Validates plain old JSON:
expect({ errors: [ { status, title, detail } ] }).toBeJaypieError();
Jaypie errors, which are ProjectErrors
, all have a .json()
to convert
import { jsonApiErrorSchema, jsonApiSchema } from "@jaypie/testkit";
expect(jsonApiErrorSchema).toBeValidSchema();
expect(jsonApiSchema).toBeValidSchema();
expect({ project: "mayhem" }).not.toBeValidSchema();
From jest-json-schema
toBeValidSchema.js (not documented in README)
import { jsonApiErrorSchema, jsonApiSchema } from "@jaypie/testkit";
import { ConfigurationError } from "@jaypie/core";
const error = new ConfigurationError();
const json = error.json();
expect(json).toMatchSchema(jsonApiErrorSchema);
expect(json).not.toMatchSchema(jsonApiSchema);
From jest-json-schema
; see README
Note: these regular expressions matchers so not verify the value is value, only that it matches the pattern (it "looks like" something). For example, expect("123e4567-e89b-12d3-a456-426614174000").toMatchUuid()
will pass because the string matches a UUID pattern, even though it is not a valid UUID.
toMatchBase64
toMatchJwt
toMatchMongoId
toMatchSignedCookie
toMatchUuid4
toMatchUuid5
toMatchUuid
import { ConfigurationError } from "@jaypie/core";
const error = new ConfigurationError();
expect(() => {
throw error;
}).toThrowJaypieError();
Do not forget to await expect
when passing async
functions:
import { ConfigurationError } from "@jaypie/core";
const error = new ConfigurationError();
await expect(async () => {
throw error;
}).toThrowJaypieError();
// Breaks and causes a false-positive because `expect` did not `await`
// expect(async () => {}).toThrowJaypieError();
// > Error: Expected function to throw a JaypieError, but it did not throw.
Creates a mock of the log
provided by @jaypie/core
.
import { mockLogFactory } from "@jaypie/testkit";
const log = mockLogFactory();
log.warn("Danger");
expect(log.warn).toHaveBeenCalled();
expect(log.error).not.toHaveBeenCalled();
Restores the log
provided by @jaypie/core
, commonly performed afterEach
with spyLog
in beforeEach
. See example with spyLog
.
Spies on the log
provided by @jaypie/core
, commonly performed beforeEach
with restoreLog
in afterEach
. Not necessary when mocking the entire Jaypie module.
import { restoreLog, spyLog } from "@jaypie/testkit";
import { log } from "@jaypie/core";
beforeEach(() => {
spyLog(log);
});
afterEach(() => {
restoreLog(log);
vi.clearAllMocks();
});
test("log", () => {
log.warn("Danger");
expect(log.warn).toHaveBeenCalled();
expect(log.error).not.toHaveBeenCalled();
});
Generates an event object for testing SQS Lambda functions with as many messages as provided. Note, test will accept more than ten messages, but AWS will only send ten at a time.
import { sqsTestRecords } from "@jaypie/testkit";
const event = sqsTestRecords(
{ MessageId: "1", Body: "Hello, World!" },
{ MessageId: "2", Body: "Goodbye, World!" }
);
- matcher toBeHttpStatus
- matcher toBeJaypieAny
- matcher toBeJaypieData
- matcher toBeJaypieDataObject
- matcher toBeJaypieDataArray
- ...@knowdev/jest
Date | Version | Summary |
---|---|---|
9/15/2024 | 1.0.29 | All errors exported as mocks |
9/14/2024 | 1.0.28 | Matchers toThrowBadGatewayError , toThrowGatewayTimeoutError , toThrowUnavailableError
|
9/13/2024 | 1.0.27 | Matcher toBeCalledAboveTrace
|
7/16/2024 | 1.0.21 | Export Jaypie mock as default |
3/20/2024 | 1.0.2 | Export LOG
|
3/16/2024 | 1.0.0 | Artists ship |
3/15/2024 | 0.1.0 | Initial deploy |
3/15/2024 | 0.0.1 | Initial commit |
MIT License. Published by Finlayson Studio