{"id":36503397,"url":"https://github.com/fuzzy-ai/perjury","last_synced_at":"2026-01-12T02:26:36.942Z","repository":{"id":10673076,"uuid":"57005599","full_name":"fuzzy-ai/perjury","owner":"fuzzy-ai","description":"False vows","archived":false,"fork":false,"pushed_at":"2022-04-27T19:11:13.000Z","size":216,"stargazers_count":3,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-12T23:39:50.964Z","etag":null,"topics":["clone","framework","testing","vows","vowsjs"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fuzzy-ai.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2016-04-25T02:22:05.000Z","updated_at":"2022-08-07T16:50:46.000Z","dependencies_parsed_at":"2022-08-07T06:00:17.577Z","dependency_job_id":null,"html_url":"https://github.com/fuzzy-ai/perjury","commit_stats":null,"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/fuzzy-ai/perjury","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzzy-ai%2Fperjury","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzzy-ai%2Fperjury/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzzy-ai%2Fperjury/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzzy-ai%2Fperjury/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fuzzy-ai","download_url":"https://codeload.github.com/fuzzy-ai/perjury/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzzy-ai%2Fperjury/sbom","scorecard":{"id":415040,"data":{"date":"2025-08-11","repo":{"name":"github.com/fuzzy-ai/perjury","commit":"4bc264c8ae3e256d27d996dc3296046abc10754f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"24 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-fvqr-27wr-82fm","Warn: Project is vulnerable to: GHSA-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T23:36:35.080Z","repository_id":10673076,"created_at":"2025-08-18T23:36:35.080Z","updated_at":"2025-08-18T23:36:35.080Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28332430,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T00:36:25.062Z","status":"online","status_checked_at":"2026-01-12T02:00:08.677Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["clone","framework","testing","vows","vowsjs"],"created_at":"2026-01-12T02:26:36.865Z","updated_at":"2026-01-12T02:26:36.937Z","avatar_url":"https://github.com/fuzzy-ai.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"perjury\n=======\n\nperjury is a [vows.js](http://vowsjs.org/) work-alike library. Making false\nvows is [perjury](https://en.wikipedia.org/wiki/Perjury).\n\nThe motivation is to make the internals of the test framework clearer, so when\nyour tests are mysteriously failing, you have some idea why.\n\nLicense\n-------\n\nCopyright 2016, 2017 fuzzy.ai \u003cmailto:legal@fuzzy.ai\u003e\n\nCopyright 2017 AJ Jordan \u003cmailto:alex@strugee.net\u003e\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nExample\n-------\n\n```javascript\n\n// You should be able to do this\n\nvar vows = require('perjury');\n\n// perjury does not pollute the assert module namespace by default, but\n// this should give you the same behaviour\n\nvar assert = vows.assert;\n\n// This should look a lot like your regular tests if you're already a vows\n// user. If you're not, welcome!\n\nvows\n  .describe(\"My first vows test\")\n  .addBatch({\n    'When we open a file': {\n      topic: function() {\n        fs.open(\"/tmp/fakefile\", \"w\", this.callback);\n      },\n      'it works': function(err, fd) {\n        assert.ifError(err);\n        assert.isNumber(fd);        \n      },\n      teardown: function(fd) {\n        fs.close(fd, this.callback);\n      }\n      'and we write to the file': {\n        topic: function(fd) {\n          fs.write(fd, \"My dog has fleas\\n\", this.callback);\n        },\n        'it works': function(err, written, buffer) {\n          assert.ifError(err);\n          assert.greater(written, 0);\n          assert.isString(buffer);        \n        }\n      }\n    }\n  })\n  .run();\n\n```\n\nIntroduction\n------------\n\n`perjury` is an attempt to make a version of `vows` where I know why my tests\nfail. This has been surprisingly hard with vows.\n\nRequiring\n---------\n\nYou require the module like any other module. If you have a lot of code that\nuses the `vows` module, you should probably be able to just require the\n`perjury` module and be done with it.\n\nAssert macros\n-------------\n\n*However*, unlike `vows`, `perjury` will not pollute the namespace of the\nbuilt-in `assert` module by default. Instead, it exports an `assert` property\nwith the macros you want as properties.\n\nSo, if you use the assert macros from `vows`, you should change your code that\nlooks like this:\n\n```javascript\nvar vows = require('vows');\nvar assert = require('assert');\n```\n\nto this:\n\n```javascript\nvar vows = require('perjury');\nvar assert = vows.assert;\n```\n\nData structures\n---------------\n\nThe basic way to use vows-like tests is to build really large hierarchical\nobjects with a particular well-defined form.\n\n### Batch\n\nFor `perjury`, the core concept is the test `batch`. A batch is an object that\nconsists of the following:\n\n  * A `topic` function that generates values to be tested\n  * One or more test functions, which accept the results of the `topic` and\n    use assert macros to validate the results\n  * Zero or more sub-batches\n  * An optional `teardown` function that cleans up any values generated by the\n\nA batch can be either *synchronous* or *asynchronous*. For a synchronous batch,\nthe `topic` function just returns a value, and the test functions measure that\nvalue:\n\n```javascript\nlet batch = {\n  \"We get the answer\":  {\n    topic() {\n      return 6 * 7;\n    },\n    \"it equals 42\": (err, answer) =\u003e {\n      assert.ifError(err);\n      assert.equal(answer, 42);\n    }\n  }\n};\n```\n\nFor an asynchronous batch, the topic returns its results through the `callback`\nproperty of `this`. `perjury` knows that the callback will be used because the\nresult returned by the `topic` function is `undefined`.\n\n```javascript\nlet batch = {\n  \"When we get the answer asynchronously\":  {\n    topic() {\n      setImmediate(() =\u003e {\n        this.callback(null, 6 * 7);\n      });\n      return undefined;\n    },\n    \"it equals 42\": (err, answer) =\u003e {\n      assert.ifError(err);\n      assert.equal(answer, 42);\n    }\n  }\n};\n```\n\nAlternately, a topic can return a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\nPerjury will resolve the returned Promise and call tests with the same `(err, results)`\nformat as with other types of call.\n\n```javascript\nlet batch = {\n  \"When we get the answer\":  {\n    topic() {\n      return new Promise((resolve, reject) =\u003e {\n        fs.open(\"/tmp/testfile\", \"w\", (err, fd) =\u003e {\n          if (err) {\n            reject(err);\n          } else {\n            resolve(fd);\n          }\n        })\n      });\n    },\n    \"it equals 42\": (err, fd) =\u003e {\n      assert.ifError(err);\n      assert.isNumber(fd);\n    }\n  }\n};\n```\n\nNote that all test functions receive at least an `err` argument, and then one or\nmore arguments. Synchronous batches can only have one test argument; asynchronous\nbatches can have a lot.\n\nFor backwards compatibility, it's possible to call `this.callback` synchronously\nin your `topic`. Perjury will simply call `setImmediate` to call the callback\nlater. But that is a tricky and confusing way to write your tests, and you\nshould probably avoid it.\n\nA batch can also have sub-batches. These are just properties of the batch that are\nalso batch objects, with their own `topic`, tests, sub-batches, `teardown`, etc.\nThe argument to the topic will be the results of the parent batch, in reverse\norder up the hierarchy.\n\n```javascript\nlet batch = {\n  \"When we get the answer\":  {\n    topic() {\n      return 6 * 7;\n    },\n    \"it equals 42\": (err, answer) =\u003e {\n      assert.ifError(err);\n      assert.isNumber(answer);\n      assert.equal(answer, 42);\n    },\n    \"and we ask a couple of questions\": {\n      topic(answer) {\n        return [\"What is six times seven?\", \"How many roads must a person walk down?\"];\n      },\n      \"they look plausible\": (err, questions) =\u003e {\n        assert.ifError(err);\n        assert.isString(question[0]);\n        assert.equal(question[0][question[0].length - 1], '?');\n        assert.isString(question[1]);\n        assert.equal(question[1][question[1].length - 1], '?');\n      },\n      \"and we compare the answer and the question\": {\n        topic(questions, answer) {\n          setImmediate(() =\u003e {\n            this.callback(null, questions[0], questions[1], answer);\n          });\n          return undefined;\n        },\n        \"they match up well\": (err, question0, question1, answer) =\u003e {\n          assert.ifError(err);\n          // NB: you need to implement isAnswerTo yourself\n          assert(isAnswerTo(answer, question0));\n          assert(isAnswerTo(answer, question1));\n        }\n      }\n    }\n  }\n};\n```\n\nNote that if a batch's `topic` returns more than one value to its callback, they\nwill be provided *in order* for any sub-batches' `topic`, but hierarchically\n*in reverse order*. This may be a little confusing.\n\nNote also that if an error occurs, in either the topic or the tests, the\nsub-batches will not be run.\n\nThe `teardown` method is called after all the tests and sub-batches have been run.\nSo, the order is something like this:\n\n   - topic\n   - tests\n   - sub-batches (if there are no errors)\n   - teardown\n\nThe `teardown` gets the non-error results of the `topic` as arguments. It's useful\nfor cleaning up things that the `topic` made a mess of.\n\n```javascript\n\nbatch = {\n  'When we open a file': {\n    topic: function() {\n      fs.open(\"/tmp/fakefile\", \"w\", this.callback);\n    },\n    'it works': function(err, fd) {\n      assert.ifError(err);\n      assert.isNumber(fd);\n    },\n    teardown: function(fd) {\n      fs.close(fd, this.callback);\n    }\n  }\n};\n\n```\n\n`teardown` functions can also be synchronous or asynchronous, or they can return\na Promise. However, the results are ignored.\n\n```javascript\nlet batch = {\n  \"When we get the answer\":  {\n    topic() {\n      return new Promise((resolve, reject) =\u003e {\n        fs.open(\"/tmp/testfile\", \"w\", (err, fd) =\u003e {\n          if (err) {\n            reject(err);\n          } else {\n            resolve(fd);\n          }\n        })\n      });\n    },\n    \"it equals 42\": (err, fd) =\u003e {\n      assert.ifError(err);\n      assert.isNumber(fd);\n    },\n    teardown(fd) {\n      return new Promise((resolve, reject) =\u003e {\n        if (typeof(fd) != 'number') {\n          reject(new Error(\"File descriptor is not a number\"));\n        } else {\n          fs.close(fd, (err) =\u003e {\n            if (err) {\n              reject(err);\n            } else {\n              resolve();\n            }\n          })\n        }\n      });\n    }\n  }\n};\n```\n\nNote that the teardown will be called regardless of whether errors happened or\nnot, so it's a good idea to check the arguments to make sure they're valid.\n\nTeardowns are called as soon as the batch finishes; this is different from how\nvows.js works, but it is better.\n\nIf you're using a version of node that can handle [async/await syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function),\n(\u003e= 7.10.1), you can use async functions in your topics and teardowns, which can\nmake your aysnchronous test code about as lovely and compact as can be.\n\n```javascript\n\nconst fs = require('fs');\nconst util = require('util');\n\n// util.promisify is available in node \u003e 8.0.0\n\nconst open = util.promisify(fs.open);\nconst close = util.promisify(fs.close);\n\nlet batch = {\n  \"When we get the answer\":  {\n    topic: async function () {\n      return await open(\"/tmp/testfile\", \"w\");\n    },\n    \"it equals 42\": (err, fd) =\u003e {\n      assert.ifError(err);\n      assert.isNumber(fd);\n    },\n    teardown: async function (fd) {\n      return await close(fd);\n    }\n  }\n};\n```\n\n### Suite\n\nBatches are organized into suites. You create a suite with the `describe` method\nof `vows`.\n\n```javascript\nconst vows = require('perjury');\n\nlet suite = vows.describe('A new suite');\n```\n\nYou can then add one or more batches to the suite using the `addBatch` method.\n\n```javascript\nsuite.addBatch(batch1);\nsuite.addBatch(batch2);\nsuite.addBatch(batch3);\n```\n\nFinally, you have two options to actually run the test suite. The first is the\naptly-named `run()` method, which runs all the tests and reports the results to\n`stdout`. You can then run the script through node and you'll run all your\ntests.\n\nAlternately, you can use the `export()` method, passing the current `module` as\nan argument. This will change the `exports` property of the `module` to be the\n`run()` method of the suite. In other words, the module will now export a single\nfunction that runs the suite.\n\nThe `perjury` command-line tool can be used to run all your test modules that\nuse `export()`.\n\n```shell\n./node_modules/.bin/perjury test/*.js\n```\n\nAll the suite methods are [chainable](https://en.wikipedia.org/wiki/Method_chaining).\nThe typical way to actually use this library, then, is to require vows, use the\n`describe` method to create a suite, use `addBatch` to add one or more batches,\nand then use `export(module)` or more rarely `run()` to run the suite.\n\n```javascript\nconst fs = require('fs');\nconst vows = require('perjury');\nlet assert = vows.assert;\n\nvows.describe('Input/output tests')\n  .addBatch({\n    'When we open a file': {\n      topic: function() {\n        fs.open(\"/tmp/fakefile\", \"w\", this.callback);\n      },\n      'it works': function(err, fd) {\n        assert.ifError(err);\n        assert.isNumber(fd);\n      },\n      teardown: function(fd) {\n        fs.close(fd, this.callback);\n      }\n    }\n  })\n  .export(module);\n```\n\nCoffeeScript\n------------\n\n[CoffeeScript](http://coffeescript.org/) is a nice pre-processor for JavaScript.\nIf you write your test scripts in CoffeeScript, it's totally OK to run them with\nthe `perjury` command-line tool, as-is.\n\n```shell\n./node_modules/.bin/perjury test/*.js test/*.coffee\n```\n\n`perjury` uses the CoffeeScript package to load the test modules automatically.\n\nDebugging\n---------\n\n[Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development)\nmeans roughly that write your tests *first*, then write the implementations,\nthen keep running the tests till they work.\n\nUnfortunately, vows.js can be really hard to use for TDD, because it doesn't\ngive you a lot of information about where errors are happening. Sometimes it's\nyour test code; at other times it's the code you're trying to test. The\nstructures that vows.js and `perjury` use can be tricky to get right.\n\n`perjury` doesn't necessarily do a fantastic job at this, but it's a little\nbetter, and it's definitely a goal. `perjury` uses the\n[debug](https://www.npmjs.com/package/debug) library to spoot out debug info to\nstderr at run time. This can be very useful for looking at how the `perjury`\nmodule is running, and figuring out where errors are happening.\n\nTo use it, define the `DEBUG` environment variable when running your tests:\n\n```shell\nDEBUG=perjury:* ./node_modules/.bin/perjury mytest.js\n```\n\nWatch this space for more help in doing TDD with perjury.\n\nCompatibility\n-------------\n\nThis version is incompatible with vows and previous versions of perjury in a\nfew small ways.\n\n-   vows will check the [arity](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length) of\n    test methods and call the method different ways based on that arity. With\n    perjury, tests will always take an error argument and then zero or\n    more result arguments. This should help preserve your sanity and make you\n    write more robust tests.\n-   vows will automatically pollute the namespace of the `assert` module.\n    perjury makes you use a property instead.\n-   vows handles all teardowns at the same time, without waiting for sub-batch\n    teardowns to finish. perjury handles teardowns when the batch is finished,\n    so you can do things like deleting created files in your sub-batch\n    teardowns, and deleting their directory in your main batch teardown,\n    and things will just work right.\n-   vows and perjury 0.x treat a Promise returned from the topic just like any\n    other results. So test functions will receive the Promise as a results\n    argument. perjury 1.x will resolve the Promise and pass the results to the\n    test instead. So, if your tests expect to receive a Promise passed\n    synchronously, you should change that.\n\nassert\n------\n\nThe exposed `assert` module-ish object has a number of useful methods for doing\ntests. `perjury` avoids most of the namespace pollution problems that vows.js\nhas, so you should feel OK using these.\n\nThe module exposes all the methods of the built-in\n[assert](https://nodejs.org/api/assert.html) module. It also has the following\nutility methods. Each will do a check and if the check fails, will throw a new\n`AssertionError` with either the `message` argument as its message, or a\nstandard message for that macro.\n\n### assert.epsilon(eps, actual, expected, message)\n\nChecks that the number `actual` is within `eps` from `expected`.\n\n### assert.match(actual, expected, message)\n\nChecks that `actual` matches the regular expression `expected`. Note that\n`actual` will be coerced to a string if it is not one already.\n\n`assert.matches` is a synonym.\n\n### assert.isTrue(actual, message)\n\nChecks that `actual` is `true` (not just truthy; `true`).\n\n### assert.isFalse(actual, message)\n\nChecks that `actual` is `false` (not just falsy; `false`).\n\n### assert.isZero(actual, message)\n\nChecks that `actual` is 0.\n\n### assert.isNotZero(actual, message)\n\nChecks that `actual` is not 0.\n\n### assert.greater(actual, expected, message)\n\nChecks that `actual` is strictly greater than `expected`.\n\n### assert.lesser(actual, expected, message)\n\nChecks that `actual` is strictly lesser than `expected`.\n\n### assert.inDelta(actual, expected, delta, message)\n\nChecks that `actual` is less than `delta` away from `expected`. It's a lot\nlike `assert.epsilon()`.\n\n### assert.include(actual, expected, message)\n\nChecks that `actual` contains `expected`. `assert.includes` is a synonym.\n\n### assert.notInclude(actual, expected, message)\n\nChecks that `actual` does not contain `expected`. `assert.notIncludes` is a\nsynonym.\n\n### assert.isEmpty(actual, message)\n\nChecks that `actual` is empty (an empty array or an object with no properties).\n\n### assert.isNotEmpty(actual, message)\n\nChecks that `actual` is not empty.\n\n### assert.isArray(actual, message)\n\nChecks that `actual` is an array.\n\n### assert.isObject(actual, message)\n\nChecks that `actual` is an object.\n\n### assert.isNumber(actual, message)\n\nChecks that `actual` is a number.\n\n### assert.isBoolean(actual, message)\n\nChecks that `actual` is a boolean (`true` or `false`).\n\n### assert.isNaN(actual, message)\n\nChecks that `actual` is `NaN`.\n\n### assert.isNull(actual, message)\n\nChecks that `actual` is `null`.\n\n### assert.isNotNull(actual, message)\n\nChecks that `actual` is not `null`.\n\n### assert.isUndefined(actual, message)\n\nChecks that `actual` is `undefined`.\n\n### assert.isDefined(actual, message)\n\nChecks that `actual` is not `undefined`.\n\n### assert.isString(actual, message)\n\nChecks that `actual` is a string.\n\n### assert.isFunction(actual, message)\n\nChecks that `actual` is a function.\n\n### assert.typeOf(actual, expected, message)\n\nChecks that `actual` is of type `expected`.\n\n### assert.instanceOf(actual, expected, message)\n\nChecks that `actual` is an object and an instance of `expected`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuzzy-ai%2Fperjury","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffuzzy-ai%2Fperjury","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuzzy-ai%2Fperjury/lists"}