CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-sinon

JavaScript test spies, stubs and mocks for framework-agnostic unit testing.

Pending
Overview
Eval results
Files

promises.mddocs/

Promise Utilities

Sinon's promise utilities provide controllable Promise implementations for testing asynchronous code without relying on timing or external Promise resolution. These utilities allow you to manually control when Promises resolve or reject, making async tests deterministic and fast.

Capabilities

Creating Controllable Promises

Create Promise instances that can be manually resolved or rejected at any time during testing.

/**
 * Creates a controllable Promise for testing
 * @param executor - Optional executor function (same as standard Promise)
 * @returns Controllable promise with additional methods and properties
 */
function promise<T = any>(executor?: PromiseExecutor<T>): SinonPromise<T>;

interface SinonPromise<T> extends Promise<T> {
  /** Current status of the promise */
  status: "pending" | "resolved" | "rejected";
  
  /** Value the promise resolved with (if resolved) */
  resolvedValue?: T;
  
  /** Reason the promise rejected with (if rejected) */
  rejectedValue?: any;
  
  /** Manually resolve the promise with a value */
  resolve(value?: T): SinonPromise<T>;
  
  /** Manually reject the promise with a reason */
  reject(reason?: any): Promise<void>;
}

Usage Examples:

import { promise } from "sinon";

// Create a controllable promise
const controllablePromise = promise();

// Check initial status
console.log(controllablePromise.status); // "pending"

// Set up promise handlers
controllablePromise.then(value => {
  console.log("Resolved with:", value);
}).catch(error => {
  console.log("Rejected with:", error);
});

// Manually resolve
controllablePromise.resolve("test data");
console.log(controllablePromise.status); // "resolved"
console.log(controllablePromise.resolvedValue); // "test data"

Promise with Executor

Create controllable promises with initial executor logic.

type PromiseExecutor<T> = (
  resolve: (value: T | PromiseLike<T>) => void,
  reject: (reason?: any) => void
) => void;

Usage Examples:

import { promise } from "sinon";

// Promise with executor that doesn't resolve immediately
const delayedPromise = promise((resolve, reject) => {
  // Executor runs but doesn't resolve yet
  console.log("Promise created but not resolved");
});

// Later in test, manually control resolution
delayedPromise.resolve("manual resolution");

Testing Async Functions

Use controllable promises to test functions that work with async operations.

Usage Examples:

import sinon from "sinon";

// Function under test
async function processData(dataFetcher) {
  try {
    const data = await dataFetcher();
    return { success: true, data };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

// Test with controllable promise
describe("processData", () => {
  it("should handle successful data fetch", async () => {
    const mockFetcher = sinon.stub();
    const controllablePromise = sinon.promise();
    
    mockFetcher.returns(controllablePromise);
    
    // Start the async operation
    const resultPromise = processData(mockFetcher);
    
    // Verify promise is still pending
    expect(controllablePromise.status).to.equal("pending");
    
    // Manually resolve with test data
    controllablePromise.resolve({ id: 1, name: "Test" });
    
    // Await the result
    const result = await resultPromise;
    
    expect(result.success).to.be.true;
    expect(result.data).to.deep.equal({ id: 1, name: "Test" });
    expect(controllablePromise.status).to.equal("resolved");
  });
  
  it("should handle failed data fetch", async () => {
    const mockFetcher = sinon.stub();
    const controllablePromise = sinon.promise();
    
    mockFetcher.returns(controllablePromise);
    
    const resultPromise = processData(mockFetcher);
    
    // Manually reject with test error
    controllablePromise.reject(new Error("Network error"));
    
    const result = await resultPromise;
    
    expect(result.success).to.be.false;
    expect(result.error).to.equal("Network error");
    expect(controllablePromise.status).to.equal("rejected");
  });
});

Promise Status Inspection

Monitor promise state changes during testing for precise async behavior verification.

Usage Examples:

import { promise } from "sinon";

const testPromise = promise();

// Initial state
assert.equal(testPromise.status, "pending");
assert.isUndefined(testPromise.resolvedValue);
assert.isUndefined(testPromise.rejectedValue);

// After resolution
testPromise.resolve("success");
assert.equal(testPromise.status, "resolved");
assert.equal(testPromise.resolvedValue, "success");
assert.isUndefined(testPromise.rejectedValue);

// Testing rejection
const rejectPromise = promise();
rejectPromise.reject("failure");
assert.equal(rejectPromise.status, "rejected");
assert.equal(rejectPromise.rejectedValue, "failure");
assert.isUndefined(rejectPromise.resolvedValue);

Integration with Stubs

Combine controllable promises with stubs for comprehensive async testing.

Usage Examples:

import sinon from "sinon";

class ApiClient {
  async fetchUser(id) {
    // Implementation that makes HTTP request
    return fetch(`/api/users/${id}`).then(r => r.json());
  }
}

describe("ApiClient", () => {
  let client, fetchStub, controllablePromise;
  
  beforeEach(() => {
    client = new ApiClient();
    controllablePromise = sinon.promise();
    
    // Stub fetch to return controllable promise
    fetchStub = sinon.stub(global, "fetch");
    fetchStub.returns(controllablePromise);
  });
  
  afterEach(() => {
    sinon.restore();
  });
  
  it("should fetch user data", async () => {
    const userPromise = client.fetchUser(123);
    
    // Verify fetch was called correctly
    sinon.assert.calledOnce(fetchStub);
    sinon.assert.calledWith(fetchStub, "/api/users/123");
    
    // Simulate successful HTTP response
    const mockResponse = {
      json: sinon.stub().returns(Promise.resolve({ id: 123, name: "John" }))
    };
    
    controllablePromise.resolve(mockResponse);
    
    const user = await userPromise;
    expect(user).to.deep.equal({ id: 123, name: "John" });
  });
});

Types

type PromiseExecutor<T> = (
  resolve: (value: T | PromiseLike<T>) => void,
  reject: (reason?: any) => void
) => void;

interface SinonPromise<T> extends Promise<T> {
  status: "pending" | "resolved" | "rejected";
  resolvedValue?: T;
  rejectedValue?: any;
  resolve(value?: T): SinonPromise<T>;
  reject(reason?: any): Promise<void>;
}

Install with Tessl CLI

npx tessl i tessl/npm-sinon

docs

assertions.md

fakes.md

index.md

matchers.md

mocks.md

promises.md

sandbox.md

spies.md

stubs.md

timers.md

tile.json