A collection of test utilities specifically designed for LoopBack 4 applications and TypeScript testing
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Complete Sinon.js integration for spies, stubs, and mocks with enhanced TypeScript experience and improved stubbing utilities.
Complete Sinon.js library re-export with full TypeScript support.
/**
* Complete Sinon.js library for creating test doubles
* Includes spies, stubs, mocks, and fake timers
*/
const sinon: sinon.SinonStatic;
/**
* Sinon spy type definition for TypeScript
*/
type SinonSpy = sinon.SinonSpy;Usage Examples:
import { sinon } from "@loopback/testlab";
// Creating spies
const spy = sinon.spy();
const objectSpy = sinon.spy(console, 'log');
// Creating stubs
const stub = sinon.stub();
stub.returns("mocked value");
stub.withArgs("specific").returns("specific response");
// Creating mocks
const mock = sinon.mock(console);
mock.expects("log").once().withArgs("Hello");
// Fake timers
const clock = sinon.useFakeTimers();
clock.tick(1000);
clock.restore();Improved stub instance creation with better TypeScript support and stubs accessor.
/**
* Creates a new object with the given functions as the prototype and stubs all
* implemented functions with enhanced TypeScript support
* @param constructor - Object or class to stub
* @returns A stubbed version with stubs accessor
*/
function createStubInstance<TType extends object>(
constructor: sinon.StubbableType<TType>
): StubbedInstanceWithSinonAccessor<TType>;
/**
* Type for stubbed instances with additional stubs accessor
* Provides both the stubbed interface and access to Sinon stub methods
*/
type StubbedInstanceWithSinonAccessor<T> = T & {
stubs: sinon.SinonStubbedInstance<T>;
};Usage Examples:
import { createStubInstance } from "@loopback/testlab";
class UserService {
async findUser(id: string): Promise<User> {
// Implementation
}
async createUser(data: UserData): Promise<User> {
// Implementation
}
}
// Create stubbed instance
const userServiceStub = createStubInstance(UserService);
// Configure stubs - both approaches work
userServiceStub.findUser.resolves({id: "123", name: "Alice"});
userServiceStub.stubs.findUser.resolves({id: "123", name: "Alice"});
// Use as normal instance
const user = await userServiceStub.findUser("123");
// Access stub methods for verification
expect(userServiceStub.stubs.findUser).to.have.been.calledWith("123");Comprehensive spy functionality for monitoring function calls.
// Spy creation methods (from Sinon)
sinon.spy(): sinon.SinonSpy;
sinon.spy(target: any, property: string): sinon.SinonSpy;
sinon.spy(func: Function): sinon.SinonSpy;
// Spy interface
interface SinonSpy {
(): any;
called: boolean;
callCount: number;
calledOnce: boolean;
calledTwice: boolean;
calledThrice: boolean;
firstCall: sinon.SinonSpyCall;
secondCall: sinon.SinonSpyCall;
thirdCall: sinon.SinonSpyCall;
lastCall: sinon.SinonSpyCall;
calledBefore(anotherSpy: sinon.SinonSpy): boolean;
calledAfter(anotherSpy: sinon.SinonSpy): boolean;
calledOn(obj: any): boolean;
alwaysCalledOn(obj: any): boolean;
calledWith(...args: any[]): boolean;
alwaysCalledWith(...args: any[]): boolean;
calledWithExactly(...args: any[]): boolean;
alwaysCalledWithExactly(...args: any[]): boolean;
neverCalledWith(...args: any[]): boolean;
threw(): boolean;
threw(exception: string | Function): boolean;
alwaysThrew(): boolean;
alwaysThrew(exception: string | Function): boolean;
returned(value: any): boolean;
alwaysReturned(value: any): boolean;
restore(): void;
reset(): void;
getCalls(): sinon.SinonSpyCall[];
}Advanced stubbing functionality with method chaining and conditional responses.
// Stub creation methods (from Sinon)
sinon.stub(): sinon.SinonStub;
sinon.stub(obj: any): sinon.SinonStubbedInstance<any>;
sinon.stub(obj: any, property: string): sinon.SinonStub;
// Stub interface (extends SinonSpy)
interface SinonStub extends SinonSpy {
returns(value: any): sinon.SinonStub;
returnsArg(index: number): sinon.SinonStub;
returnsThis(): sinon.SinonStub;
resolves(value?: any): sinon.SinonStub;
rejects(error?: any): sinon.SinonStub;
throws(error?: Error | string): sinon.SinonStub;
callsArg(index: number): sinon.SinonStub;
callsArgWith(index: number, ...args: any[]): sinon.SinonStub;
yields(...args: any[]): sinon.SinonStub;
yieldsTo(property: string, ...args: any[]): sinon.SinonStub;
withArgs(...args: any[]): sinon.SinonStub;
onCall(n: number): sinon.SinonStub;
onFirstCall(): sinon.SinonStub;
onSecondCall(): sinon.SinonStub;
onThirdCall(): sinon.SinonStub;
}Usage Examples:
import { sinon, expect } from "@loopback/testlab";
// Basic stubbing
const stub = sinon.stub();
stub.returns("default");
stub.withArgs("special").returns("special case");
expect(stub()).to.equal("default");
expect(stub("special")).to.equal("special case");
// Promise stubbing
const asyncStub = sinon.stub();
asyncStub.resolves("success");
asyncStub.withArgs("error").rejects(new Error("failed"));
const result = await asyncStub();
expect(result).to.equal("success");
// Object method stubbing
const obj = { method: () => "original" };
const methodStub = sinon.stub(obj, "method");
methodStub.returns("stubbed");
expect(obj.method()).to.equal("stubbed");
methodStub.restore();Mock objects with expectations and verification.
// Mock creation (from Sinon)
sinon.mock(obj: any): sinon.SinonMock;
// Mock interface
interface SinonMock {
expects(method: string): sinon.SinonExpectation;
restore(): void;
verify(): void;
}
// Expectation interface
interface SinonExpectation {
atLeast(n: number): sinon.SinonExpectation;
atMost(n: number): sinon.SinonExpectation;
never(): sinon.SinonExpectation;
once(): sinon.SinonExpectation;
twice(): sinon.SinonExpectation;
thrice(): sinon.SinonExpectation;
exactly(n: number): sinon.SinonExpectation;
withArgs(...args: any[]): sinon.SinonExpectation;
withExactArgs(...args: any[]): sinon.SinonExpectation;
returns(value: any): sinon.SinonExpectation;
throws(error?: Error | string): sinon.SinonExpectation;
}Usage Examples:
import { sinon } from "@loopback/testlab";
const logger = { log: (msg: string) => console.log(msg) };
// Create mock with expectations
const mock = sinon.mock(logger);
mock.expects("log").once().withArgs("Hello World");
// Use the mocked object
logger.log("Hello World");
// Verify expectations
mock.verify(); // Throws if expectations not met
mock.restore();Control time and async behavior in tests.
// Timer methods (from Sinon)
sinon.useFakeTimers(): sinon.SinonFakeTimers;
sinon.useFakeTimers(config: Partial<sinon.SinonFakeTimersConfig>): sinon.SinonFakeTimers;
interface SinonFakeTimers {
tick(ms: number): void;
next(): void;
runAll(): void;
restore(): void;
reset(): void;
}Usage Examples:
import { sinon, expect } from "@loopback/testlab";
// Test with fake timers
const clock = sinon.useFakeTimers();
let called = false;
setTimeout(() => { called = true; }, 1000);
expect(called).to.be.false();
clock.tick(1000);
expect(called).to.be.true();
clock.restore();