Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(internal,expect,testing): @std/expect jest compatible fn and unified mock experience with @std/testing/mock #6317

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

anion155
Copy link

@anion155 anion155 commented Dec 30, 2024

Summary

Made an implementation of jest compatible @std/expect's fn utility.
Implementation made with side eyeing latest version of this api in jest https://jestjs.io/docs/mock-function-api .

interface ExpectMockInstance<Args extends unknown[], Return> {
  /**
   * Sets current implementation.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", () => {
   *   const mockFn = fn().mockImplementation((a: number, b: number) => a + b);
   *   expect(mockFn()).toEqual(3);
   * });
   * ```
   */
  mockImplementation(stub: (...args: Args) => Return): this;

  /**
   * Adds one time stub implementation.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", () => {
   *   const mockFn = fn((a: number, b: number) => a + b);
   *   mockFn.mockImplementationOnce((a, b) => a - b);
   *   expect(mockFn(1, 2)).toEqual(-1);
   *   expect(mockFn(1, 2)).toEqual(3);
   * });
   * ```
   */
  mockImplementationOnce(stub: (...args: Args) => Return): this;

  /**
   * Sets current implementation's return value.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", () => {
   *   const mockFn = fn().mockReturnValue(5);
   *   expect(mockFn(1, 2)).toEqual(5);
   * });
   * ```
   */
  mockReturnValue(value: Return): this;

  /**
   * Adds one time stub implementation that returns provided value.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", () => {
   *   const mockFn = fn((a: number, b: number) => a + b);
   *   mockFn.mockReturnValueOnce(5);
   *   expect(mockFn(1, 2)).toEqual(5);
   *   expect(mockFn(1, 2)).toEqual(3);
   * });
   * ```
   */
  mockReturnValueOnce(value: Return): this;

  /**
   * Sets current implementation's that returns promise resolved to value.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", async () => {
   *   const mockFn = fn();
   *   mockFn.mockResolvedValue(5);
   *   await expect(mockFn(1, 2)).resolves.toEqual(5);
   *   expect(mockFn(3, 2)).toEqual(5);
   * });
   * ```
   */
  mockResolvedValue(value: Awaited<Return> | Return): this;

  /**
   * Adds one time stub implementation that returns promise resolved to value.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", async () => {
   *   const mockFn = fn((a: number, b: number) => a + b);
   *   mockFn.mockResolvedValueOnce(5);
   *   await expect(mockFn(1, 2)).resolves.toEqual(5);
   *   expect(mockFn(1, 2)).toEqual(3);
   * });
   * ```
   */
  mockResolvedValueOnce(value: Awaited<Return> | Return): this;

  /**
   * Sets current implementation's that returns promise rejects with reason.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", async () => {
   *   const mockFn = fn();
   *   mockFn.mockRejectedValue(new Error("test error"));
   *   await expect(mockFn(1, 2)).rejects.toThrow("test error");
   *   expect(mockFn(3, 2)).toEqual(5);
   * });
   * ```
   */
  mockRejectedValue(reason?: unknown): this;

  /**
   * Adds one time stub implementation that returns promise rejects with reason.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", async () => {
   *   const mockFn = fn((a: number, b: number) => a + b);
   *   mockFn.mockRejectedValueOnce(new Error("test error"));
   *   await expect(mockFn(1, 2)).rejects.toThrow("test error");
   *   expect(mockFn(1, 2)).toEqual(3);
   * });
   * ```
   */
  mockRejectedValueOnce(reason?: unknown): this;

  /**
   * Changes current implementation to provided stub.
   * Returns disposable resource that restores previous setup of stubs on dispose.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", () => {
   *   const mockFn = fn((a: number, b: number) => a + b);
   *   mockFn.mockReturnValueOnce(5);
   *   {
   *     using withMock = mockFn.withImplementation((a, b) => a - b);
   *     expect(mockFn(1, 2)).toEqual(-1);
   *   }
   *   expect(mockFn(1, 2)).toEqual(5);
   *   expect(mockFn(1, 2)).toEqual(3);
   * });
   * ```
   */
  withImplementation(stub: (...args: Args) => Return): Disposable;

  /**
   * Changes current implementation to provided stub.
   * Runs scope function and after it is final restores previous setup of stubs.
   * Also detects if scope function returns a promise and waits for it to resolve.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", async () => {
   *   const mockFn = fn((a: number, b: number) => a + b);
   *   mockFn.mockReturnValueOnce(5);
   *   await mockFn.withImplementation(async (a, b) => a - b, async () => {
   *     await expect(mockFn(1, 2)).resolves.toEqual(-1);
   *   });
   *   expect(mockFn(1, 2)).toEqual(5);
   *   expect(mockFn(1, 2)).toEqual(3);
   * });
   * ```
   */
  withImplementation<ScopeResult>(
    stub: (...args: Args) => Return,
    scope: () => ScopeResult
  ): ScopeResult;

  /**
   * Restores original implementation and discards one time stubs.
   * In case no original implementation was provided, the mock will be reset to an empty function.
   *
   * @example Usage
   * ```ts
   * import { fn, expect } from "@std/expect";
   *
   * Deno.test("example", () => {
   *   const mockFn = fn((a: number, b: number) => a + b).mockReturnValue(5).mockReturnValueOnce(1);
   *   mockFn.mockRestore();
   *   expect(mockFn()).toEqual(3);
   *   expect(mockFn()).toEqual(3);
   * });
   * ```
   */
  mockRestore(): void;
}

Issues that this pr covers:

@CLAassistant
Copy link

CLAassistant commented Dec 30, 2024

CLA assistant check
All committers have signed the CLA.

@anion155 anion155 changed the title [WIP] feat(internal,expect,testing) @std/expect jest compatible fn and unified mock experience with @std/testing/mock feat(internal,expect,testing) @std/expect jest compatible fn and unified mock experience with @std/testing/mock Dec 30, 2024
Copy link

codecov bot commented Dec 30, 2024

Codecov Report

Attention: Patch coverage is 97.08589% with 38 lines in your changes missing coverage. Please review.

Project coverage is 96.37%. Comparing base (08fe910) to head (7b4cf2a).

Files with missing lines Patch % Lines
expect/unstable_expect.ts 87.89% 27 Missing ⚠️
expect/_unstable_matchers.ts 97.38% 8 Missing ⚠️
expect/_unstable_mock_utils.ts 72.72% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6317      +/-   ##
==========================================
+ Coverage   96.34%   96.37%   +0.02%     
==========================================
  Files         547      555       +8     
  Lines       41671    42970    +1299     
  Branches     6314     6524     +210     
==========================================
+ Hits        40147    41411    +1264     
- Misses       1482     1517      +35     
  Partials       42       42              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@anion155 anion155 changed the title feat(internal,expect,testing) @std/expect jest compatible fn and unified mock experience with @std/testing/mock feat(internal,expect,testing): @std/expect jest compatible fn and unified mock experience with @std/testing/mock Dec 30, 2024
@anion155 anion155 force-pushed the feat/expect-jest-compatible-fn branch 2 times, most recently from c5e6556 to 605a7a7 Compare December 30, 2024 20:38
@anion155 anion155 force-pushed the feat/expect-jest-compatible-fn branch from 605a7a7 to 7b4cf2a Compare December 30, 2024 20:40
@anion155 anion155 marked this pull request as ready for review December 30, 2024 20:40
@anion155 anion155 requested a review from kt3k as a code owner December 30, 2024 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants