{"id":13485062,"url":"https://github.com/udibo/mock","last_synced_at":"2025-03-27T17:30:47.440Z","repository":{"id":42024859,"uuid":"260959829","full_name":"udibo/mock","owner":"udibo","description":"Utilities to help mock behavior, spy on function calls, stub methods, and fake time for tests.","archived":true,"fork":false,"pushed_at":"2022-04-17T18:14:17.000Z","size":283,"stargazers_count":30,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-20T09:41:55.787Z","etag":null,"topics":["deno","faketime","javascript","mock","spy","stub","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/udibo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-03T15:28:22.000Z","updated_at":"2025-01-25T17:01:27.000Z","dependencies_parsed_at":"2022-08-12T02:40:40.350Z","dependency_job_id":null,"html_url":"https://github.com/udibo/mock","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udibo%2Fmock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udibo%2Fmock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udibo%2Fmock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/udibo%2Fmock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/udibo","download_url":"https://codeload.github.com/udibo/mock/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245892459,"owners_count":20689506,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","faketime","javascript","mock","spy","stub","typescript"],"created_at":"2024-07-31T17:01:44.652Z","updated_at":"2025-03-27T17:30:47.048Z","avatar_url":"https://github.com/udibo.png","language":"TypeScript","readme":"# Mock\n\n[![release](https://img.shields.io/badge/release-0.15.2-success)](https://github.com/udibo/mock/releases/tag/0.15.2)\n[![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/mock@0.15.2/mod.ts)\n[![CI](https://github.com/udibo/mock/workflows/CI/badge.svg)](https://github.com/udibo/mock/actions?query=workflow%3ACI)\n[![codecov](https://codecov.io/gh/udibo/mock/branch/main/graph/badge.svg?token=TXORMSEHM7)](https://codecov.io/gh/udibo/mock)\n[![license](https://img.shields.io/github/license/udibo/mock)](https://github.com/udibo/mock/blob/master/LICENSE)\n\nUtilities to help mock behavior, spy on function calls, stub methods and fake\ntime for tests.\n\nThis module is being archived. It has been added to Deno's standard library in\nthe testing directory. Use Deno's standard library instead.\n\n## Features\n\n- Spy on functions and instance methods to record information about calls\n- Stub methods to simulate behavior and record information about calls\n- Fake time for testing time sensitive logic\n\n## Installation\n\nThis is an ES Module written in TypeScript and can be used in Deno projects. ES\nModules are the official standard format to package JavaScript code for reuse. A\nJavaScript bundle is provided with each release so that it can be used in\nNode.js packages or web browsers.\n\n### Deno\n\nTo include it in a Deno project, you can import directly from the TS files. This\nmodule is available in Deno's third part module registry but can also be\nimported directly from GitHub using raw content URLs.\n\n```ts\n// Import from Deno's third party module registry\nimport { spy, stub } from \"https://deno.land/x/mock@0.15.2/mod.ts\";\n// Import from GitHub\nimport { spy, stub } \"https://raw.githubusercontent.com/udibo/mock/0.15.2/mod.ts\";\n```\n\nIf you do not need all of the sub-modules, you can choose to just import the\nsub-modules you need.\n\n```ts\n// Import from Deno's third party module registry\nimport { spy, stub } from \"https://deno.land/x/mock@0.15.2/mock.ts\";\n// Import from GitHub\nimport {\n  spy,\n  stub,\n} from \"https://raw.githubusercontent.com/udibo/mock/0.15.2/mock.ts\";\n```\n\n#### Sub-modules\n\n`mock.ts` module is for spying on functions and instance methods with or without\nchanging behavior.\n\n`time.ts` module is for controlling the Date object and timers.\n\n`callbacks.ts` module contains a set of functions you may want to use when\nstubbing instance methods.\n\n`asserts.ts` module contains a set of functions for making assertions about\ncalls to spys and stubs.\n\n### Node.js\n\nNode.js fully supports ES Modules.\n\nIf a Node.js package has the type \"module\" specified in its package.json file,\nthe JavaScript bundle can be imported as a `.js` file.\n\n```js\nimport { spy, stub } from \"./mock_0.15.2.js\";\n```\n\nThe default type for Node.js packages is \"commonjs\". To import the bundle into a\ncommonjs package, the file extension of the JavaScript bundle must be changed\nfrom `.js` to `.mjs`.\n\n```js\nimport { spy, stub } from \"./mock_0.15.2.mjs\";\n```\n\nSee [Node.js Documentation](https://nodejs.org/api/esm.html) for more\ninformation.\n\n### Browser\n\nMost modern browsers support ES Modules.\n\nThe JavaScript bundle can be imported into ES modules. Script tags for ES\nmodules must have the type attribute set to \"module\".\n\n```html\n\u003cscript type=\"module\" src=\"main.js\"\u003e\u003c/script\u003e\n```\n\n```js\n// main.js\nimport { spy, stub } from \"./mock_0.15.2.js\";\n```\n\nYou can also embed a module script directly into an HTML file by placing the\nJavaScript code within the body of the script tag.\n\n```html\n\u003cscript type=\"module\"\u003e\n  import { spy, stub } from \"./mock_0.15.2.js\";\n\u003c/script\u003e\n```\n\nSee\n[MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)\nfor more information.\n\n## Usage\n\nBelow are some examples of how to use Spys, Stubs, and FakeTime in tests. When\nspying/stubing instance methods, you should wrap the calls and expectations with\na try block then restore the function in a finally block to ensure the original\ninstance method is restored before continuing to other tests. The same applies\nwhen using fake time.\n\nSee [deno docs](https://doc.deno.land/https/deno.land/x/mock@0.15.2/mod.ts) for\nmore information.\n\n### Spy\n\nWhen spying on a function or instance method, all arguments and return values\nare recorded but the behavior of that function is unchanged. This gives you the\nability to verify that the code you are testing calls functions it depends on\ncorrectly and that they return the responses you expect them to.\n\nIf you have a function that takes a callback but you don't need it to do\nanything, you can create an empty spy. An empty spy will just return undefined\nfor any calls made to it.\n\n```ts\nimport { assertEquals } from \"https://deno.land/std@0.135.0/testing/asserts.ts\";\nimport { assertSpyCall, spy } from \"https://deno.land/x/mock@0.15.2/mod.ts\";\n\nfunction add(\n  a: number,\n  b: number,\n  callback: (error: Error | void, value?: number) =\u003e void,\n): void {\n  const value: number = a + b;\n  if (typeof value === \"number\" \u0026\u0026 !isNaN(value)) callback(undefined, value);\n  else callback(new Error(\"invalid input\"));\n}\n\nDeno.test(\"add calls fake callback\", () =\u003e {\n  const callback = spy();\n\n  assertEquals(add(2, 3, callback), undefined);\n  assertSpyCall(callback, 0, { args: [undefined, 5] });\n  assertEquals(add(5, 4, callback), undefined);\n  assertSpyCall(callback, 1, { args: [undefined, 9] });\n});\n```\n\nIf you have a function that takes a callback that needs to still behave\nnormally, you can wrap it with a spy.\n\n```ts\nimport { assertEquals } from \"https://deno.land/std@0.135.0/testing/asserts.ts\";\nimport {\n  assertSpyCall,\n  assertSpyCalls,\n  spy,\n} from \"https://deno.land/x/mock@0.15.2/mod.ts\";\n\nfunction filter\u003cT\u003e(values: T[], callback: (value: T) =\u003e boolean): any[] {\n  return values.filter(callback);\n}\n\nfunction isEven(value: number): boolean {\n  return value % 2 === 0;\n}\n\nDeno.test(\"filter calls real callback\", () =\u003e {\n  const callback = spy(isEven);\n  const values: number[] = [5, 6, 7, 8];\n\n  assertEquals(filter(values, callback), [6, 8]);\n  assertSpyCall(callback, 0, { args: [5, 0, values], returned: false });\n  assertSpyCall(callback, 1, { args: [6, 1, values], returned: true });\n  assertSpyCall(callback, 2, { args: [7, 2, values], returned: false });\n  assertSpyCall(callback, 3, { args: [8, 3, values], returned: true });\n  assertSpyCalls(callback, 4);\n});\n```\n\nIf you have an instance method that needs to still behave normally, you can wrap\nit with a spy. When you are done spying on a method, you need to call the\nrestore function on the spy object to remove the wrapper from the instance\nmethod. If it is not restored and you attempt to wrap it again, it will throw a\nspy error saying \"already spying on function\".\n\n```ts\nimport { assertEquals } from \"https://deno.land/std@0.135.0/testing/asserts.ts\";\nimport {\n  assertSpyCall,\n  assertSpyCalls,\n  spy,\n} from \"https://deno.land/x/mock@0.15.2/mod.ts\";\n\nclass Database {\n  // deno-lint-ignore no-explicit-any\n  private queries: any;\n  constructor() {\n    this.queries = {\n      \"select id, first_name from USERS where last_name=?\": {\n        \"Doe\": [[1, \"Jane\"], [2, \"John\"]],\n        \"Smith\": [[3, \"Jane\"]],\n      },\n      \"select id, last_name from USERS where first_name=?\": {\n        \"Jane\": [[1, \"Doe\"], [3, \"Smith\"]],\n        \"John\": [[2, \"Doe\"]],\n      },\n    };\n  }\n\n  // deno-lint-ignore no-explicit-any\n  query(query: string, params: any[]): any[][] {\n    return this.queries[query][params[0]]; // implementation not important for example\n  }\n}\n\nfunction getNamesByFirstName(db: Database, firstName: string): string[] {\n  return db\n    .query(\n      \"select id, last_name from USERS where first_name=?\",\n      [firstName],\n    )\n    .map((row) =\u003e `${firstName} ${row[1]}`);\n}\n\nfunction getNamesByLastName(db: Database, lastName: string): string[] {\n  return db\n    .query(\n      \"select id, first_name from USERS where last_name=?\",\n      [lastName],\n    )\n    .map((row) =\u003e `${row[1]} ${lastName}`);\n}\n\nDeno.test(\"functions call db.query\", () =\u003e {\n  const db = new Database();\n  const query = spy(db, \"query\");\n\n  assertEquals(getNamesByFirstName(db, \"Jane\"), [\"Jane Doe\", \"Jane Smith\"]);\n  assertSpyCall(query, 0, {\n    args: [\"select id, last_name from USERS where first_name=?\", [\"Jane\"]],\n    self: db,\n    returned: [[1, \"Doe\"], [3, \"Smith\"]],\n  });\n  assertEquals(getNamesByLastName(db, \"Doe\"), [\"Jane Doe\", \"John Doe\"]);\n  assertSpyCall(query, 1, {\n    args: [\"select id, first_name from USERS where last_name=?\", [\"Doe\"]],\n    self: db,\n    returned: [[1, \"Jane\"], [2, \"John\"]],\n  });\n  assertEquals(getNamesByFirstName(db, \"John\"), [\"John Doe\"]);\n  assertSpyCall(query, 2, {\n    args: [\"select id, last_name from USERS where first_name=?\", [\"John\"]],\n    self: db,\n    returned: [[2, \"Doe\"]],\n  });\n  assertEquals(getNamesByLastName(db, \"Smith\"), [\"Jane Smith\"]);\n  assertSpyCall(query, 3, {\n    args: [\"select id, first_name from USERS where last_name=?\", [\"Smith\"]],\n    self: db,\n    returned: [[3, \"Jane\"]],\n  });\n  assertSpyCalls(query, 4);\n});\n```\n\n### Stub\n\nWhen stubbing an instance method, all arguments and return values are recorded\nbut the behavior of that instance method is fake. This gives you the ability to\nverify that the code you are testing calls an instance method and that it\nhandles the expected behavior correctly.\n\nIf you have an instance method but you don't need it to do or return anything,\nyou can create an empty stub. An empty stub will just return undefined for any\ncalls made to it.\n\n```ts\nimport { assertEquals } from \"https://deno.land/std@0.135.0/testing/asserts.ts\";\nimport {\n  assertSpyCall,\n  assertSpyCalls,\n  stub,\n} from \"https://deno.land/x/mock@0.15.2/mod.ts\";\n\nclass Cat {\n  action(name: string): any {\n    throw new Error(\"unimplemented\");\n  }\n}\n\nfunction doAction(cat: Cat, action: string): any {\n  return cat.action(action);\n}\n\nDeno.test(\"doAction\", () =\u003e {\n  const cat = new Cat();\n  const action = stub(cat, \"action\");\n\n  assertEquals(doAction(cat, \"walk\"), undefined);\n  assertSpyCall(action, 0, {\n    self: cat,\n    args: [\"walk\"],\n    returned: undefined,\n  });\n\n  assertEquals(doAction(cat, \"jump\"), undefined);\n  assertSpyCall(action, 1, {\n    self: cat,\n    args: [\"jump\"],\n    returned: undefined,\n  });\n\n  assertSpyCalls(action, 2);\n});\n```\n\nIf you have an instance method but need it to return specific values for each\ncall, you can create a stub with a function that returns those values. The stub\nfunction will have all the same arguments available to it if you would like to\ngenerate return values based on the arguments.\n\nA callback helper is provided for converting an iterable into a callback for\nyour stub function. You can add more return values after initialization by\npushing onto the array as long as the iterator has not completed. An iterator is\nconsidered complete if called after all values have been returned. The callback\nwill return undefined to each call after the iterator is done.\n\n```ts\nimport { assertEquals } from \"https://deno.land/std@0.135.0/testing/asserts.ts\";\nimport {\n  assertSpyCallAsync,\n  assertSpyCalls,\n  resolvesNext,\n  stub,\n} from \"https://deno.land/x/mock@0.15.2/mod.ts\";\n\nclass Database {\n  query(_query: string, _params: unknown[]): Promise\u003cunknown[][]\u003e {\n    throw new Error(\"unimplemented\");\n  }\n}\n\nasync function getUsers(\n  db: Database,\n  lastName: string,\n  firstName?: string,\n): Promise\u003cstring[]\u003e {\n  return (await db\n    .query(\n      \"SELECT id, username FROM users WHERE last_name=?\" +\n        (firstName ? \" and first_name=?\" : \"\"),\n      firstName ? [lastName, firstName] : [lastName],\n    ))\n    .map((row) =\u003e `${row[0]} ${row[1]}`);\n}\n\nDeno.test(\"getUsers\", async () =\u003e {\n  const db = new Database();\n  const resolves: [number, string][][] = [\n    [[1, \"jd\"], [2, \"johnd\"], [3, \"janedoe\"]],\n    [[2, \"johnd\"]],\n  ];\n  const query = stub(db, \"query\", resolvesNext(resolves));\n\n  try {\n    assertEquals(await getUsers(db, \"doe\"), [\"1 jd\", \"2 johnd\", \"3 janedoe\"]);\n    assertEquals(await getUsers(db, \"doe\", \"john\"), [\"2 johnd\"]);\n\n    resolves.push([[3, \"janedoe\"]]);\n    assertEquals(await getUsers(db, \"doe\"), [\"3 janedoe\"]);\n\n    await assertSpyCallAsync(query, 0, {\n      args: [\n        \"SELECT id, username FROM users WHERE last_name=?\",\n        [\"doe\"],\n      ],\n      self: db,\n      returned: [[1, \"jd\"], [2, \"johnd\"], [3, \"janedoe\"]],\n    });\n\n    await assertSpyCallAsync(query, 1, {\n      args: [\n        \"SELECT id, username FROM users WHERE last_name=? and first_name=?\",\n        [\"doe\", \"john\"],\n      ],\n      self: db,\n      returned: [[2, \"johnd\"]],\n    });\n\n    await assertSpyCallAsync(query, 2, {\n      args: [\n        \"SELECT id, username FROM users WHERE last_name=?\",\n        [\"doe\"],\n      ],\n      self: db,\n      returned: [[3, \"janedoe\"]],\n    });\n\n    assertSpyCalls(query, 3);\n  } finally {\n    query.restore();\n  }\n});\n```\n\n### FakeTime\n\nOverrides the real Date object and timer functions with fake ones that can be\ncontrolled through the fake time instance.\n\n```ts\nimport {\n  assertSpyCalls,\n  FakeTime,\n  Spy,\n  spy,\n} from \"https://deno.land/x/mock@0.15.2/mod.ts\";\n\nfunction secondInterval(cb: () =\u003e void): void {\n  setInterval(cb, 1000);\n}\n\nDeno.test(\"secondInterval calls callback every second\", () =\u003e {\n  const time = new FakeTime();\n  const cb = spy();\n\n  try {\n    secondInterval(cb);\n    assertSpyCalls(cb, 0);\n    time.tick(500);\n    assertSpyCalls(cb, 0);\n    time.tick(500);\n    assertSpyCalls(cb, 1);\n    time.tick(3500);\n    assertSpyCalls(cb, 4);\n  } finally {\n    time.restore();\n  }\n});\n```\n\n## License\n\n[MIT](LICENSE)\n","funding_links":[],"categories":["TypeScript","基础设施"],"sub_categories":["Deno 源"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fudibo%2Fmock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fudibo%2Fmock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fudibo%2Fmock/lists"}