@jaypie/testkit v1.1.0
Jaypie Testkit š¦āā¬š«
Test utilities built for Jaypie
š Usage
Installation
npm install --save-dev @jaypie/testkit
Example
Mocking Jaypie
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"));
Error Spying
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();
}
});
Log Spying
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
Test Matchers
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();
š Reference
import {
LOG,
jsonApiErrorSchema,
jsonApiSchema,
matchers,
mockLogFactory,
restoreLog,
spyLog,
} from '@jaypie/testkit'
LOG
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" });
jsonApiErrorSchema
A JSON Schema validator for the JSON:API error schema. Powers the toBeJaypieError
matcher (via toMatchSchema
).
jsonApiSchema
A JSON Schema validator for the JSON:API data schema.
matchers
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);
expect(subject).toBeCalledAboveTrace()
import { log } from "@jaypie/core";
log.trace("Hello, World!");
expect(log).not.toBeCalledAboveTrace();
log.warn("Look out, World!");
expect(log).toBeCalledAboveTrace();
expect(subject).toBeJaypieError()
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
expect(subject).toBeValidSchema()
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)
expect(subject).toMatchSchema(schema)
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
expect(subject).toMatch*()
Regular Expression Matchers
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
expect(subject).toThrowJaypieError()
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.
mockLogFactory()
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();
restoreLog(log)
Restores the log
provided by @jaypie/core
, commonly performed afterEach
with spyLog
in beforeEach
. See example with spyLog
.
spyLog(log)
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();
});
sqsTestRecords(message, message, ...)
or sqsTestRecords([...])
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!" }
);
š Wishlist
- matcher toBeHttpStatus
- matcher toBeJaypieAny
- matcher toBeJaypieData
- matcher toBeJaypieDataObject
- matcher toBeJaypieDataArray
- ...@knowdev/jest
š Changelog
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 |
š License
Published by Finlayson Studio. All rights reserved
9 months ago
10 months ago
10 months ago
10 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago