@beecode/msh-test-contractor
v1.0.0
Published
Test Contractor
Downloads
1,109
Maintainers
Readme
msh-test-contractor
Idea
Inspired by J.B. Rainsberger — Integrated Tests Are A Scam. This tool makes unit tests more reliable by creating contracts between units (provider/consumer).
When writing unit tests, your unit usually makes a call outside of its boundaries, and you need to mock that call. With test-contractor you create a contract between the two units. This contract defines the obligation of the function (provider) — if certain parameters are passed to it, it will return a certain result. The contract then serves two purposes:
- Generate reliable mocks for consumer tests
- Validate the provider actually fulfills the contract
Features
- Contract-driven mocking — mocks stay in sync with real behavior
- Dual purpose — validates providers and generates mocks for consumers
- YAML-first contracts — readable, reviewable in PRs, no code required
- Vitest integration — generates
describe/itblocks automatically - Supports — plain functions, classes, constructors, method mocking, internal mocking
Install
npm i @beecode/msh-test-contractorQuick Start
1. Write a contract (YAML)
Create a .contract.yaml file next to your module:
# math.contract.yaml
subject: add
module: ./math.js
subjectType: function
methods:
__self__:
terms:
- params: [1, 2]
result: 3
- params: [10, 5]
result: 152. Run contracts
Create a test file that discovers and runs all contracts:
// contract.test.ts
import { contractorTestRunner } from '@beecode/msh-test-contractor/contract/contractor-test-runner.js'
await contractorTestRunner.dir('./src')This discovers all **/*.contract.yaml files and runs each term as a Vitest test case.
3. Use mocks in your own tests
Use the contract to generate a Vitest spy for a dependency:
import { mocker } from '@beecode/msh-test-contractor'
import myContract from './my-module.contract.yaml'
const { spy, mockRestore } = mocker.contract(myContract)
// spy is a vi.spyOn — use it in your tests
const result = myFunctionThatDependsOnMyModule()
expect(result).toBe('expected')
mockRestore() // restore after testsYAML Contract Reference
Function Contract
subject: simpleFunction
module: ./simple-function.js
subjectType: function
methods:
__self__: # __self__ references the function itself
terms:
- params: [1]
result: 1
- params: [11]
error: number is greater than tenClass Contract
subject: Calculator
module: ./calculator.js
subjectType: class
constructor:
terms:
- params: [1, 2]
result: { a: 1, b: 2 }
methods:
add:
terms:
- constructorParams: [1, 2]
params: [3]
result: 6
sub:
terms:
- constructorParams: [5, 3]
params: [1]
result: 1Contract with Mocks
subject: dummyFunction
module: ./dummy-function.js
subjectType: function
mock:
- ./logger.contract.yaml # mock external dependency
- ./calculator.contract.yaml
methods:
add:
terms:
- params: [1, 2]
result: 3
errorIfMoreThanTen:
terms:
- params: [1]
result: 1
- params: [11]
error: 'More then 10'Mock internal methods within the same subject using mockFunction:
subject: logger
module: ./logger.js
subjectType: function
methods:
_message:
mock:
- ./date-mock.yaml
terms:
- params: ['type', 'test-message']
result: '2020-01-01T00:00:00.000Z:TYPE:test-message'
debug:
mockFunction: [_message] # mock _message within same subject
terms:
- params: ['test-message']
result: '2020-01-01T00:00:00.000Z:DEBUG:test-message'Field Reference
| Field | Required | Description |
|-------|----------|-------------|
| subject | yes | Name of the exported function/class |
| module | yes | Relative path to the JS module |
| subjectType | yes | function or class |
| constructor | class only | Constructor term definitions |
| methods | yes | Map of method names to term definitions |
| methods.__self__ | — | For function subjects, references the function itself |
| mock | no | List of contract YAML paths to use as mocks |
| mockFunction | no | List of internal method names to mock within same subject |
| Term: params | yes | Input parameters array |
| Term: result | one of | Expected return value |
| Term: error | one of | Expected thrown error (string or object) |
| Term: constructorParams | class | Constructor args for class method calls |
API
mocker
import { mocker } from '@beecode/msh-test-contractor'mocker.contract(contract) — Spy on an entire subject (function, class, or object). Returns { spy, mockRestore }.
mocker.function(contract, fnName) — Spy on a single method. Returns { spy, mockRestore }.
contractor
import { contractor } from '@beecode/msh-test-contractor'contractor(contract, fnName) — Generate a Vitest describe/it block for one function's contract terms.
contractorTestRunner
import { contractorTestRunner } from '@beecode/msh-test-contractor/contract/contractor-test-runner.js'contractorTestRunner.dir(dirPath) — Discover and run all **/*.contract.yaml files in a directory.
contractorTestRunner.file(filePath) — Run a single YAML contract file.
contractorTestRunner.contract(contract) — Run a programmatically-built contract.

