CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-loopback--testlab

A collection of test utilities specifically designed for LoopBack 4 applications and TypeScript testing

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

request-response-mocking.mddocs/

Request/Response Mocking

Shot-based HTTP request/response stubs for testing without running servers, including Express-specific context stubbing.

Capabilities

Shot Integration

Shot library integration for HTTP request/response injection and mocking.

/**
 * Shot injection function for simulating HTTP requests
 * @param dispatchFunc - Function that handles the request
 * @param options - Request configuration options
 * @returns Promise resolving to response object
 */
function inject(
  dispatchFunc: ShotListener,
  options: ShotRequestOptions
): Promise<ResponseObject>;

/**
 * Shot request options interface
 */
interface ShotRequestOptions {
  url: string;
  method?: string;
  headers?: { [key: string]: string };
  payload?: string | Buffer | object;
  credentials?: any;
  artifacts?: any;
  app?: any;
  plugins?: any;
  validate?: boolean;
}

/**
 * Shot listener function type
 */
type ShotListener = (req: IncomingMessage, res: ServerResponse) => void;

Usage Examples:

import { inject } from "@loopback/testlab";
import { IncomingMessage, ServerResponse } from "http";

// Handler function to test
function handler(req: IncomingMessage, res: ServerResponse) {
  res.writeHead(200, {"Content-Type": "application/json"});
  res.end(JSON.stringify({url: req.url, method: req.method}));
}

// Inject request and get response
const response = await inject(handler, {
  url: "/test",
  method: "GET"
});

expect(response.statusCode).to.equal(200);
expect(JSON.parse(response.payload)).to.eql({
  url: "/test",
  method: "GET"
});

// POST request with payload
const postResponse = await inject(handler, {
  url: "/users",
  method: "POST",
  payload: JSON.stringify({name: "Alice"}),
  headers: {"Content-Type": "application/json"}
});

Server Request Stubbing

Create stubbed HTTP server request objects for testing.

/**
 * Creates a stubbed HTTP server request object
 * @param options - Request configuration options
 * @returns IncomingMessage stub with request properties
 */
function stubServerRequest(options: ShotRequestOptions): IncomingMessage;

Usage Examples:

import { stubServerRequest, expect } from "@loopback/testlab";

// Create stubbed request
const request = stubServerRequest({
  url: "/api/users?limit=10",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer token123"
  },
  payload: JSON.stringify({name: "Alice", email: "alice@example.com"})
});

// Test request properties
expect(request.url).to.equal("/api/users?limit=10");
expect(request.method).to.equal("POST");
expect(request.headers["content-type"]).to.equal("application/json");
expect(request.headers.authorization).to.equal("Bearer token123");

// Use in handler testing
function authMiddleware(req: IncomingMessage, res: ServerResponse, next: Function) {
  if (!req.headers.authorization) {
    res.writeHead(401);
    res.end("Unauthorized");
    return;
  }
  next();
}

// Test middleware with stubbed request
const mockRes = { writeHead: sinon.stub(), end: sinon.stub() };
const next = sinon.stub();

authMiddleware(request, mockRes as any, next);
expect(next).to.have.been.called();

Server Response Stubbing

Create stubbed HTTP server response objects for testing.

/**
 * Creates a stubbed HTTP server response object
 * @param request - Associated request object
 * @param onEnd - Callback called when response ends
 * @returns ServerResponse stub
 */
function stubServerResponse(
  request: IncomingMessage,
  onEnd: ShotCallback
): ServerResponse;

/**
 * Callback function for Shot response completion
 */
type ShotCallback = (response: ResponseObject) => void;

/**
 * Shot response constructor type
 */
type ShotResponseCtor = new (
  request: IncomingMessage,
  onEnd: ShotCallback
) => ServerResponse;

/**
 * Observed response type (alias for ResponseObject)
 */
type ObservedResponse = ResponseObject;

Usage Examples:

import { stubServerRequest, stubServerResponse, expect } from "@loopback/testlab";

// Create request and response stubs
const request = stubServerRequest({url: "/test"});
let capturedResponse: any;

const response = stubServerResponse(request, (res) => {
  capturedResponse = res;
});

// Test response operations
response.writeHead(200, {"Content-Type": "application/json"});
response.write(JSON.stringify({message: "Hello"}));
response.end();

// Verify captured response
expect(capturedResponse.statusCode).to.equal(200);
expect(capturedResponse.headers["content-type"]).to.equal("application/json");
expect(JSON.parse(capturedResponse.payload)).to.eql({message: "Hello"});

Handler Context Stubbing

Create complete handler context stubs with request, response, and result promise.

/**
 * Creates a stubbed handler context for testing
 * @param requestOptions - Optional request configuration
 * @returns Handler context stub with request, response, and result promise
 */
function stubHandlerContext(
  requestOptions?: ShotRequestOptions
): HandlerContextStub;

/**
 * Handler context stub interface
 */
interface HandlerContextStub {
  request: IncomingMessage;
  response: ServerResponse;
  result: Promise<ObservedResponse>;
}

Usage Examples:

import { stubHandlerContext, expect } from "@loopback/testlab";

// Create handler context
const context = stubHandlerContext({
  url: "/api/test",
  method: "GET"
});

// Use in handler
function testHandler(req: IncomingMessage, res: ServerResponse) {
  res.writeHead(200, {"Content-Type": "text/plain"});
  res.end("Hello World");
}

// Execute handler
testHandler(context.request, context.response);

// Wait for result
const result = await context.result;
expect(result.statusCode).to.equal(200);
expect(result.payload).to.equal("Hello World");
expect(result.headers["content-type"]).to.equal("text/plain");

// Test async handler
async function asyncHandler(req: IncomingMessage, res: ServerResponse) {
  // Simulate async operation
  await new Promise(resolve => setTimeout(resolve, 10));
  res.writeHead(200);
  res.end("Async response");
}

const asyncContext = stubHandlerContext();
asyncHandler(asyncContext.request, asyncContext.response);
const asyncResult = await asyncContext.result;
expect(asyncResult.payload).to.equal("Async response");

Express Context Stubbing

Create Express-specific context stubs with full Express request/response API.

/**
 * Creates a stubbed Express context for testing Express applications
 * @param requestOptions - Optional request configuration
 * @returns Express context stub with app, request, response, and result
 */
function stubExpressContext(
  requestOptions?: ShotRequestOptions
): ExpressContextStub;

/**
 * Express context stub interface
 */
interface ExpressContextStub extends HandlerContextStub {
  app: express.Application;
  request: express.Request;
  response: express.Response;
  result: Promise<ObservedResponse>;
}

Usage Examples:

import { stubExpressContext, expect } from "@loopback/testlab";
import express from "express";

// Create Express context
const context = stubExpressContext({
  url: "/users/123?include=profile",
  method: "GET",
  headers: {"Accept": "application/json"}
});

// Test Express request properties
expect(context.request.params).to.be.an.Object();
expect(context.request.query).to.eql({include: "profile"});
expect(context.request.get("Accept")).to.equal("application/json");
expect(context.app).to.be.an.instanceOf(express.application.constructor);

// Use with Express middleware
function parseUserId(req: express.Request, res: express.Response, next: express.NextFunction) {
  const userId = req.url.match(/\/users\/(\d+)/)?.[1];
  if (userId) {
    (req as any).userId = userId;
    next();
  } else {
    res.status(400).json({error: "Invalid user ID"});
  }
}

// Test middleware
const next = sinon.stub();
parseUserId(context.request, context.response, next);
expect((context.request as any).userId).to.equal("123");
expect(next).to.have.been.called();

// Use with Express route handler
function getUserHandler(req: express.Request, res: express.Response) {
  const userId = (req as any).userId;
  const includeProfile = req.query.include === "profile";
  
  res.json({
    id: userId,
    name: "Alice",
    ...(includeProfile && {profile: {age: 30}})
  });
}

getUserHandler(context.request, context.response);

const result = await context.result;
expect(result.statusCode).to.equal(200);
expect(JSON.parse(result.payload)).to.eql({
  id: "123",
  name: "Alice",
  profile: {age: 30}
});

Advanced Usage Patterns

Complex testing scenarios and patterns.

Usage Examples:

import { 
  stubExpressContext, 
  stubHandlerContext, 
  inject, 
  expect, 
  sinon 
} from "@loopback/testlab";

// Testing middleware chains
async function testMiddlewareChain() {
  const context = stubExpressContext({
    url: "/protected",
    headers: {"Authorization": "Bearer valid-token"}
  });

  const middlewares = [authMiddleware, validateToken, handler];
  
  // Simulate middleware chain
  for (const middleware of middlewares) {
    await new Promise((resolve) => {
      middleware(context.request, context.response, resolve);
    });
  }

  const result = await context.result;
  expect(result.statusCode).to.equal(200);
}

// Testing error handling
async function testErrorHandling() {
  const context = stubHandlerContext();
  
  function errorHandler(req: IncomingMessage, res: ServerResponse) {
    try {
      throw new Error("Something went wrong");
    } catch (error) {
      res.writeHead(500);
      res.end(JSON.stringify({error: error.message}));
    }
  }

  errorHandler(context.request, context.response);
  
  const result = await context.result;
  expect(result.statusCode).to.equal(500);
  expect(JSON.parse(result.payload)).to.eql({
    error: "Something went wrong"
  });
}

// Testing with POST data
async function testPostData() {
  const postData = {name: "Bob", email: "bob@example.com"};
  const context = stubExpressContext({
    url: "/users",
    method: "POST",
    payload: JSON.stringify(postData),
    headers: {"Content-Type": "application/json"}
  });

  function createUserHandler(req: express.Request, res: express.Response) {
    // In real Express app, body would be parsed by body-parser middleware
    // For testing, we can access the raw payload
    const body = JSON.parse((req as any).payload || "{}");
    
    res.status(201).json({
      id: Math.random().toString(36),
      ...body,
      createdAt: new Date().toISOString()
    });
  }

  createUserHandler(context.request, context.response);
  
  const result = await context.result;
  expect(result.statusCode).to.equal(201);
  const responseBody = JSON.parse(result.payload);
  expect(responseBody).to.have.property("id");
  expect(responseBody.name).to.equal("Bob");
  expect(responseBody.email).to.equal("bob@example.com");
}

docs

assertions.md

http-client.md

http-utilities.md

index.md

request-response-mocking.md

test-doubles.md

test-sandbox.md

validation-helpers.md

tile.json