{"id":13736338,"url":"https://github.com/crewdevio/merlin","last_synced_at":"2025-04-13T14:06:17.254Z","repository":{"id":39835722,"uuid":"280176153","full_name":"crewdevio/merlin","owner":"crewdevio","description":"Testing and Benchmarking framework for deno 🧙‍♂️","archived":false,"fork":false,"pushed_at":"2022-05-25T06:53:22.000Z","size":64,"stargazers_count":50,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-30T00:47:54.423Z","etag":null,"topics":["deno","deno-test","deno-testing","testing","testing-tools"],"latest_commit_sha":null,"homepage":"https://crewdevio.mod.land/projects/merlin","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/crewdevio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-16T14:30:53.000Z","updated_at":"2024-04-22T00:23:58.000Z","dependencies_parsed_at":"2022-08-29T05:10:24.980Z","dependency_job_id":null,"html_url":"https://github.com/crewdevio/merlin","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crewdevio%2Fmerlin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crewdevio%2Fmerlin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crewdevio%2Fmerlin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crewdevio%2Fmerlin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/crewdevio","download_url":"https://codeload.github.com/crewdevio/merlin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248328165,"owners_count":21085261,"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","deno-test","deno-testing","testing","testing-tools"],"created_at":"2024-08-03T03:01:20.056Z","updated_at":"2025-04-13T14:06:17.233Z","avatar_url":"https://github.com/crewdevio.png","language":"TypeScript","funding_links":[],"categories":["基础设施","Modules"],"sub_categories":["Deno 源","Testing","Assistants"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"http://pixelartmaker.com/art/b5da8523654c61c.png\" width=\"130px\" /\u003e\n  \u003ch3 align=\"center\"\u003eTesting and Benchmarking framework for deno 🧙‍♂️\u003c/h3\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n   \u003ca href=\"https://github.com/crewdevio/merlin/issues\"\u003e\n     \u003cimg alt=\"GitHub issues\" src=\"https://img.shields.io/github/issues/crewdevio/merlin\"\u003e\n   \u003c/a\u003e\n   \u003ca href=\"https://github.com/crewdevio/merlin/network\"\u003e\n     \u003cimg alt=\"GitHub forks\" src=\"https://img.shields.io/github/forks/crewdevio/merlin\"\u003e\n   \u003c/a\u003e\n   \u003ca href=\"https://github.com/crewdevio/merlin/stargazers\"\u003e\n     \u003cimg alt=\"GitHub stars\" src=\"https://img.shields.io/github/stars/crewdevio/merlin\"\u003e\n   \u003c/a\u003e\n   \u003ca href=\"https://github.com/crewdevio/merlin/blob/master/LICENSE\"\u003e\n     \u003cimg alt=\"GitHub license\" src=\"https://img.shields.io/github/license/crewdevio/merlin\"\u003e\n   \u003c/a\u003e\n   \u003ca href=\"https://deno.land\"\u003e\n     \u003cimg src=\"https://img.shields.io/badge/deno-%5E1.10.2-green?logo=deno\"/\u003e\n   \u003c/a\u003e\n   \u003ca href=\"https://nest.land/package/merlin\"\u003e\n     \u003cimg src=\"https://nest.land/badge.svg\" /\u003e\n   \u003c/a\u003e\n   \u003ca href=\"https://deno.land/x/merlin\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/available%20on-deno.land/x-blue.svg?style=flat\u0026logo=deno\"/\u003e\n   \u003c/a\u003e\n\u003c/p\u003e\n\n## Merlin\n\nMerlin is a [Jest](https://jestjs.io/en/)-inspired testing framework for deno.\n\n## Using Matchers\n\n### Common Matchers\n\n- `assertEqual(label: string, config)`\n- `assertNotEqual(label: string, config)`\n- `evalEquals(testEqual[])`\n- `stringContains(label: string, config)`\n- `arrayContains(label: string, config)`\n- `beNull(label: string, config)`\n- `beFalsy(label: string, config)`\n- `beTruthy(label: string, config)`\n\n### All Matchers\n\n- `assertEqual(label: string, config)` Compare two values and throws an error if\n  the expect and toBe are not equal\n- `assertNotEqual(label: string, config)` Compare two values and throws an error\n  if the expect and notBe are equal\n- `evalEquals(testEqual[])` evaluate multiple equality tests in an array. If the\n  data is not the same it throws an error\n- `fetchEqual(label: string, config)` evaluate if two values are equal. If the\n  request data is not the same as expected, it throws an error\n- `arrayContains(label: string, config)` evaluates that the array contains an\n  specific data. if the array does not contain the data it throws an error\n- `stringContains(label: string, config)` evaluates if a string contains an\n  specific word. if the string does not contain the word it throws an error\n- `beNull(label: string, config)` evaluates if a data is null\n- `beFalsy(label: string, config)` evaluates if a data is a falsy value\n- `beTruthy(label: string, config)` evaluates if a data is a truthy value\n- `isBigInt(label: string, config)` evaluates if a data is a bigInt value type\n- `isZero(label: string, config)` evaluates if a data is a Zero\n- `isNaN(label: string, config)` evaluates if a data is NaN value\n- `sameLength(label: string, config)` evaluates if data has a specific length\n- `assertRegExp(label: string, config)` evaluates if a regular expression match\n- `isFunction(label: string, config)` evaluates if a data is a function\n- `isSymbol(label: string, config)` evaluates if a data is a symbol\n- `isUndefined(label: string, config)` evaluates if a data is undefined\n- `isString(label: string, config)` evaluates if a data is string\n- `isNumber(label: string, config)` evaluates if a data is number\n- `isEmpty(label: string, config)` evaluates if a data is empty\n- `assertSame(label: string, config)` evaluates if two values are strictly the\n  same\n- `assertGreaterOrEqual(label: string, config)` evaluates whether the expected\n  data is greater than or equal to another\n- `assertGreater(label: string, config)` evaluates whether the expected data is\n  greater than another\n- `assertLess(label: string, config)` evaluates if the expected data is less\n  than another\n- `assertLessOrEqual(label: string, config)` evaluates if the expected data is\n  less than or equal to another\n- `assertInstanceOf(label: string, config)` evaluates that one object is an\n  instance of another\n- `assertFloat(label: string, config)` evaluates if two decimal numbers are\n  equal\n- `assertThrows(label: string, config)` expect it throws an error\n- `assertThrowsSync(label: string, config)` expect it throws an async error\n- `haveProperty(label: string, config)` expect an object to contain the\n  properties in its value\n\n#### Statics\n\n- `Merlin.Error(msg?: string)` force to throw an error\n- `Merlin.Unimplemented(msg?: string)` Use this to throw a method not\n  implemented error\n- `Merlin.Unreachable()` Use this to throw an Unreachable method error\n\n### Install Merlin\n\ninstall merlin-cli (optional)\n\n```sh\ndeno install --allow-run -n merlin https://deno.land/x/merlin/cli.ts\n```\n\n### Mirrors\n\nyou can get Merlin from different url.\n\n- from `deno.land/x`\n\n```typescript\nimport { Merlin } from \"https://deno.land/x/merlin/mod.ts\";\n```\n\n- from `github repo`\n\n```typescript\nimport { Merlin } from \"http://denopkg.com/crewdevio/merlin/mod.ts\";\n```\n\n### Basic Use\n\nsimple assertions.\n\n`example.test.ts`\n\n```typescript\nimport { Merlin } from \"https://deno.land/x/merlin/mod.ts\";\n\nconst test = new Merlin();\n\ntest.assertEqual(\"two plus two is four\", {\n  expect() {\n    return 2 + 2;\n  },\n  toBe() {\n    return 4;\n  },\n});\n```\n\nrun this test in deno.\n\n```sh\nmerlin start\n```\n\nor\n\n```sh\ndeno test\n```\n\nyou should see this output on the console.\n\n```sh\nrunning 1 tests\ntest two plus two is four ... ok (17ms)\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (18ms)\n```\n\n### Parameters\n\nall assertions have parameters that they can receive, these parameters can\nchange the behavior of the tests.\n\n- `label` add a description to the test.\n- `expect()` this function returns the data and then tests with its matchmaker.\n- `toBe()` this function returns the data that we hope is correct.\n- `notBe()` this function returns the data that we hope it is incorrect.\n- `value()` returns the data expected to be of that type.\n- `ignore (optional)` receives a boolean to ignore the test in case the value is\n  true.\n- `strict (optional)` receives a boolean, it does a strict comparison of the\n  `expect()` and `toBe()` values.\n- `message (optional)` receives a string with the message to display in case the\n  test fails.\n- `Ops (optional)` receives a boolean, closes all the operations that never end,\n  for example `Deno.open(\"file.txt\")`. by default is `true`.\n- `Resources (optional)` receives a boolean, terminates all asynchronous\n  processes that interact with the system. by default is `true`.\n- `only (optional)` receives a boolean, only tests that have `only in true` will\n  be executed, the rest will not run.\n- `Exit (optional)` receives a boolean,this is enabled by default for all tests,\n  but can be disabled by setting the Exit boolean to false in thetest definition\n\n### about resources and ops sanitizers\n\nCertain actions in Deno create resources in the resource table . These resources\nshould be closed after you are done using them.\n\nFor each test definition, the test runner checks that all resources created in\nthis test have been closed. This is to prevent resource 'leaks'. This is enabled\nby default for all tests, but can be disabled by setting the sanitizeResources\nboolean to false in the test definition.\n\nThe same is true for async operation like interacting with the filesystem. The\ntest runner checks that each operation you start in the test is completed before\nthe end of the test. This is enabled by default for all tests, but can be\ndisabled by setting the sanitizeOps boolean to false in the test definition.\n\n```typescript\nasync function writeSomething(): Promise\u003cstring\u003e {\n  const decoder = new TextDecoder(\"utf-8\");\n  Deno.createSync(\"./texts.txt\");\n  const Package = await Deno.readFileSync(\"./text.txt\");\n  await Deno.writeTextFile(\"./text.txt\", \"test\");\n  return decoder.decode(Package);\n}\n\ntest.assertEqual(\"Leak resources test\", {\n  expect: async () =\u003e await writeSomething(),\n  toBe: () =\u003e \"test\",\n  only: true,\n  Ops: false,\n  Resources: false,\n});\n```\n\n```sh\nmerlin start\n\ntest Leak resources test ... ok (5ms)\n\ntest result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n```\n\n### Multiple tests\n\n`example.test.ts`\n\n```typescript\ntest.evalEquals([\n  {\n    label: \"object assignment\",\n    expect() {\n      const data: any = { one: 1 };\n      data[\"two\"] = 2;\n\n      return data;\n    },\n    toBe() {\n      return { one: 1, two: 2 };\n    },\n  },\n  {\n    label: \"two plus two is four\",\n    expect() {\n      return 2 + 2;\n    },\n    toBe() {\n      return 4;\n    },\n  },\n]);\n```\n\noutput\n\n```sh\nmerlin start\n\nrunning 2 tests\ntest object assignment ... ok (10ms)\ntest two plus two is four ... ok (1ms)\n\ntest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (13ms)\n```\n\n### notEqual\n\n`example.test.ts`\n\n```typescript\ntest.assertNotEqual(\"two plus two not is five\", {\n  expect() {\n    return 2 + 2;\n  },\n  notBe() {\n    return 4;\n  },\n});\n```\n\noutput\n\n```sh\nmerlin start\n\nrunning 1 tests\ntest two plus two not is five ... FAILED (2ms)\n\nfailures:\n\ntwo plus two not is five\nAssertionError: actual: 4 expected: 4\n    at assertNotEquals (https://deno.land/std/testing/asserts.ts:195:5)\n    at fn (merlin.ts:105:9)\n    at async asyncOpSanitizer ($deno$/testing.ts:34:5)\n    at async Object.resourceSanitizer [as fn] ($deno$/testing.ts:68:5)\n    at async TestRunner.[Symbol.asyncIterator] ($deno$/testing.ts:276:11)\n    at async Object.runTests ($deno$/testing.ts:364:20)\n\nfailures:\n\n        two plus two not is five\n\ntest result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (2ms)\n```\n\n## stringContains\n\n`example.test.ts`\n\n```typescript\ntest.stringContains(\"hello world contains world\", {\n  Contains: () =\u003e \"world\",\n  value: () =\u003e \"Hello World\",\n});\n```\n\n```sh\nmerlin start\n\ntest hello world contains world ... ok (8ms)\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n```\n\n### fetchEqual\n\n```typescript\ntest.fetchEqual(\"fetch data\", {\n  url: \"https://jsonplaceholder.typicode.com/todos/1\",\n  type: \"json\",\n  toBe() {\n    return { userId: 1, id: 1, title: \"delectus aut autem\", completed: false };\n  },\n});\n```\n\n```sh\nmerlin start\n\ntest fetch data ... ok (1440ms)\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n```\n\n### testRegExp\n\n```typescript\ntest.assertRegExp(\"regEx match\", {\n  expect: () =\u003e \"https://google.com\",\n  toBe: () =\u003e new RegExp(\"^https?://[a-z.]+.com$\"),\n});\n```\n\n```sh\nmerlin start\n\ntest regEx match ... ok (6ms)\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (342ms)\n```\n\n### Using async code.\n\nyou can use asynchronous code by adding `async` in `expect`, `toBe` and `value`\nfunctions.\n\nexample\n\n```typescript\nconst test = new Merlin();\n\ntest.assertEqual(\"get error 404\", {\n  async expect() {\n    const response = await fetch(\"https://deno.land/std/example/examples.ts\");\n\n    const data = response.text();\n\n    return data;\n  },\n  toBe() {\n    return \"404: Not Found\";\n  },\n});\n```\n\n\u003e **Note**: all the methods of the merlin class support async function since\n\u003e they have top level await\n\n![merlin gif](https://cdn.discordapp.com/attachments/656976424778989602/735287285519745114/mer.gif)\n\n## Create benchmarks using Maven\n\nMaven is a benchmark tool for deno included in Merlin.\n\nIt's easy to use. `example`:\n\n```typescript\nimport { Maven } from \"https://deno.land/x/merlin/mod.ts\";\n\nMaven.Bench({\n  name: \"Sorting arrays\",\n  fn: () =\u003e {\n    new Array(10000).fill(Math.random()).sort();\n  },\n  steps: 1000,\n});\n\nMaven.runBench();\n```\n\nthis is the terminal output\n\n![gif](https://cdn.discordapp.com/attachments/656976424778989602/735290326645735464/ezgif-7-c060645377b2.gif)\n\n### Parameters\n\nmaven receives the following parameters.\n\n- `name: string` benchmark name\n- `fn(): void` function that contains the code\n- `steps: number` number of times to repeat the benchmark\n\nyou can see the details at the end of the benchmark using\n\n```typescript\nimport { Maven } from \"https://deno.land/x/merlin/mod.ts\";\n\nMaven.Bench({\n  name: \"Sorting arrays\",\n  fn: () =\u003e {\n    new Array(10000).fill(Math.random()).sort();\n  },\n  steps: 1000,\n});\n\nMaven.runBench().then(Maven.Result());\n```\n\nIt has a table with the detailed values\n\n```sh\n▒▒▒▒▒▒▒▒ Benchmarking finished\n\n┌───────────────────────────────────────────────────────────────────────────────────────────┐\n│    Benchmark name:  Sorting array                                                         │\n├───────────────────────┬──────────────────────────────┬────────────────────────────────────┤\n│    Total runs: 1000   │  Total time: 1099.6591 ms    │  Avg time: 1.0997 ms               │\n├───────────────────────┼────────────────────┬─────────┴───────────┬────────────────────────┤\n│    min: 0.7768 ms     │ max: 9.9867 ms     │ mean: 5.3817 ms     │ median: 0.8511 ms      │\n├───────────────────────┴────────────────────┴─────────────────────┴────────────────────────┤\n│    Thresholds:  0 ========== 70 ========== 90 ========== ∞                                │\n├───────────────────────────────┬───────────────────────────────────────────────────────────┤\n│                               │                                                           │\n│    0.7768 ms _[   965][96.5%] │========================================================   │\n│    2.6188 ms _[    33][ 3.3%] │==                                                         │\n│    4.4608 ms _[     1][ 0.1%] │=                                                          │\n│    6.3027 ms _[     0][   0%] │                                                           │\n│    8.1447 ms _[     1][ 0.1%] │=                                                          │\n│                               │                                                           │\n└───────────────────────────────┴───────────────────────────────────────────────────────────┘\n```\n\n### Contributing\n\ncontributions are welcome, create a pull request and send us your feature, first\ncheck the [CONTRIBUTING GUIDELINES](CONTRIBUTING.md).\n\n### [LICENSE MIT](https://opensource.org/licenses/MIT)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrewdevio%2Fmerlin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrewdevio%2Fmerlin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrewdevio%2Fmerlin/lists"}