{"id":16092896,"url":"https://github.com/xaviervia/washington","last_synced_at":"2025-03-17T17:31:10.701Z","repository":{"id":17878240,"uuid":"20819293","full_name":"xaviervia/washington","owner":"xaviervia","description":"A pure, functional† unit testing tool with a dependency-free test suite API","archived":false,"fork":false,"pushed_at":"2023-03-02T05:24:10.000Z","size":3153,"stargazers_count":13,"open_issues_count":11,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T00:00:10.638Z","etag":null,"topics":["functional","javascript","testing"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xaviervia.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-06-13T22:11:44.000Z","updated_at":"2020-09-11T03:15:09.000Z","dependencies_parsed_at":"2024-06-19T00:19:38.067Z","dependency_job_id":"8ea19262-9bba-40bf-86de-d71cb55da3c7","html_url":"https://github.com/xaviervia/washington","commit_stats":{"total_commits":221,"total_committers":5,"mean_commits":44.2,"dds":"0.38009049773755654","last_synced_commit":"ca1f73676c7b2af0444d17a5fe26eba8a6cc132a"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviervia%2Fwashington","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviervia%2Fwashington/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviervia%2Fwashington/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviervia%2Fwashington/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xaviervia","download_url":"https://codeload.github.com/xaviervia/washington/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243872380,"owners_count":20361465,"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":["functional","javascript","testing"],"created_at":"2024-10-09T16:44:24.696Z","updated_at":"2025-03-17T17:31:10.360Z","avatar_url":"https://github.com/xaviervia.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Washington\n\n[![https://travis-ci.org/xaviervia/washington.svg?branch=master](https://travis-ci.org/xaviervia/washington.svg?branch=master)](https://travis-ci.org/xaviervia/washington/builds) [![npm version](https://img.shields.io/npm/v/washington.svg?maxAge=1000)](https://www.npmjs.com/package/washington)\n\nA [pure, functional†](#why-do-you-say-this-is-functional) unit testing tool with a dependency-free test suite API.\n\n## Installation\n\n```\nnpm install washington --dev\n```\n\nWashington provides a CLI, so you can install it globally to get the `washington` command:\n\n```\nnpm install -g washington\n```\n\n## Cheat sheet\n\n#### From the command line:\n\n```javascript\n// tests.js\nconst add = (x, y) =\u003e x + y\n\nmodule.exports = [\n  {\n    description: 'returns 2 when adding 1 and 1',\n    test: check =\u003e check(add(1, 1)),\n    shouldEqual: 2\n  },\n  {\n    description: 'returns 4 when adding 2 and 2',\n    test: check =\u003e check(add(2, 2)),\n    shouldEqual: 4\n  }\n]\n```\n\n```\n\u003e washington test.js\n```\n\nThe `washington` command exits the process with an exit code equal to the number of failing examples. This takes advantage of the fact that any non-zero exit code means that the command failed.\n\n#### Programmatically:\n\n```javascript\nconst washington = require('washington')\n\nconst add = (x, y) =\u003e x + y\n\nwashington([\n  {\n    description: 'returns 2 when adding 1 and 1',\n    test: check =\u003e check(add(1, 1)),\n    shouldEqual: 2\n  }\n])\n```\n\nBy default the `washington` function also exits the process with an exit code equal to the number of failing examples.\n\n#### Asynchronous examples work out of the box:\n\n```javascript\nconst addLater = (x, y, callback) =\u003e {\n  setTimeout(() =\u003e callback(x + y))\n}\n\nmodule.exports = [\n  {\n    description: 'will eventually add 1 and 1 and pass 2 to the callback',\n    test: check =\u003e addLater(1, 1, result =\u003e check(result)),\n    shouldEqual: 2\n  }\n]\n```\n\n#### You can compare complex object/array structures, no problem:\n\n```javascript\nmodule.exports = [\n  {\n    description: 'has the expected data structure',\n    test: check =\u003e check({ a: [1, '2', false] }),\n    shouldEqual: { a: [1, '2', false] }\n  }\n]\n```\n\nThis is because assertions are done with [`assert.deepEqual`](https://nodejs.org/api/assert.html#assert_assert_deepequal_actual_expected_message).\n\n#### There is a shorthand for synchronous examples; just return the value that you want to compare:\n\n```javascript\nconst add = (x, y) =\u003e x + y\n\nmodule.exports = [\n  {\n    description: 'returns 2 synchronously when adding 1 and 1',\n    test: () =\u003e add(1, 1),\n    shouldEqual: 2\n  }\n]\n```\n\n#### Examples without a test scenario are considered pending. Washington is your unit test to-do list:\n\n```javascript\nmodule.exports = [\n  {\n    description: 'is so test, many unit'\n  },\n  {\n    description: 'buys milk'\n  }\n]\n```\n\n#### To make it work in the browser, just replace the output formatter:\n\n```javascript\nimport washington from 'washington'\nimport washingtonFormatterBrowser from 'washington.formatter.browser'\n\nconst add = (x, y) =\u003e x + y\n\nconst suiteTask = washington(\n  [\n    {\n      description: 'returns 3 when adding 1 and 2',\n      test: check =\u003e check(add(1, 2)),\n      shouldEqual: 3\n    }\n  ],\n  { safe: true }\n)\n\nsuiteTask\n  .chain(washingtonFormatterBrowser(console.log))\n  .run()\n```\n\nThere is no [Karma](https://karma-runner.github.io/) adapter yet. Make an issue or pull request if you want one.\n\n#### DSL\n\nIf you get bored of writing the arrays of examples by hand, Washington comes bundled with a neat DSL to write them in a more compact way.\n\n* `example(description, test, shouldEqual)`: builds and returns the example object.\n* `suite(prefix, ...examples)`: prepends the prefix to the description of every example that you pass it to it, and returns the array with the modified examples.\n\n```js\nimport { example, suite } from 'washington'\n\nexport default suite(\n  'addition',\n\n  example(\n    '1 and 2 make 3',\n    () =\u003e 1 + 2,\n    3\n  ),\n\n  example(\n    '2 and 4 make 6',\n    () =\u003e 2 + 4,\n    6\n  ),\n\n  example('pending example')\n)\n```\n\nYou might wonder, \"Why don’t you open the README with this example? It looks like a cleaner API than writing the objects manually, and more similar to other tools\".\n\nWell, the thing is that the DSL functions of most JS testing libraries behave in a very different way than Washington, doing things behind the scenes, and by showing examples that looked like those of other libraries, it might seem that Washington works the same way. But half of the point of Washington is that the test suite is a simple array of plain objects, so that you can manipulate it in any way you want. Showing that instead helps hammer the point home.\n\nThe DSL functions are extremely lean. There is no magic at all in them. Take a look:\n\n```js\n// packages/washington.dsl/index.js\nconst suite = (name, ...suite) =\u003e\n  suite.map(x =\u003e ({...x, description: `${name}: ${x.description}`}))\n\nconst example = (description, test, shouldEqual) =\u003e\n  ({ description, test, shouldEqual })\n```\n\n### Other formatters\n\n#### Output [TAP](https://testanything.org/) instead of the default colors:\n\n```javascript\nconst washington = require('washington')\nconst washingtonFormatterTAP = require('washington.formatter.tap')\n\nconst add = (x, y) =\u003e x + y\n\nconst suiteTask = washington(\n  [\n    {\n      description: 'returns 2 when adding 1 and 1',\n      test: check =\u003e check(add(1, 1)),\n      shouldEqual: 2\n    },\n    {\n      description: 'returns 4 when adding 2 and 2',\n      test: check =\u003e check(add(2, 2)),\n      shouldEqual: 4\n    }    \n  ],\n  {safe: true}\n)\n\nsuiteTask\n  .chain(washingtonFormatterTAP(console.log))\n  .run()\n```\n\n#### Access the test results programmatically\n\nMaybe you want to use this tool for something else other than simple unit testing? Displaying results interactively in a REPL, in the browser, or sending them over a network…\n\nI always thought that a library like this might be useful for exercises such as [Ruby Koans](https://github.com/edgecase/ruby_koans).\n\nCheck the [suite result object structure](#suite-result) for details on the object structure that Washington puts inside the Task.\n\n```javascript\nconst washington = require('washington')\n\nconst add = (x, y) =\u003e x + y\n\nconst suiteTask = washington(\n  [\n    {\n      description: 'returns 2 when adding 1 and 1',\n      test: check =\u003e check(add(1, 1)),\n      shouldEqual: 2\n    },\n    {\n      description: 'returns 4 when adding 2 and 2',\n      test: check =\u003e check(add(2, 2)),\n      shouldEqual: 4\n    },\n    {\n      description: 'gets chocolate as well'\n    }\n  ],\n  {safe: true} // This prevents the default output formatter from running\n)\n\nsuiteTask\n  .map(result =\u003e {\n    console.log('object structure result', result)\n  })\n  .run()\n```\n\n\u003e `map` is used in this example because most users need not be familiar with `chain` and Folktale Tasks. If you are, I suggest that you use that instead.\n\n## API\n\nThe `washington` function takes two arguments. It returns a `Task` when running `safe`. When the `safe` option `false` or not set, it will quit the process before returning.\n\n```javascript\nconst suiteTask = washington(\n  testSuite, // explained in the previous examples\n  {\n    // When set to be `safe`, washington will not actually run anything.\n    // Instead, it will return a Folktale Task that you can consume\n    // to add your own output formatter or manipulate the result in any\n    // other way\n    safe: true\n  }\n)\n\nsuiteTask\n  .map(suiteResult =\u003e {\n    console.log(suiteResult)\n\n    return suiteResult\n  })\n\nsuiteTask\n  .chain(suiteResult =\u003e Task.of(suiteResult))\n```\n\nThe `suiteTask` is a [Folktale Task](https://github.com/origamitower/folktale/tree/master/src/data/task). In a nutshell, that means that you can `map` or `chain` over it to get access to the results. These operations are defined in the [Fantasy Land specification](https://github.com/fantasyland/fantasy-land).\n\nIf you are not familiar with this, think of `map` and `chain` as the `then` of a Promise. In the [\"Why do you say this is functional\"](#why-do-you-say-this-is-functional) section there are links to great tutorials if you want to learn more.\n\n### Suite Result\n\nThe `suiteResult` structure looks like this:\n\n```javascript\n[\n  {\n    description: 'some examples that is successful',\n    test: () =\u003e 1 + 1,\n    shouldEqual: 2,\n    result: {\n      type: 'success'\n    }\n  },\n  {\n    description: 'something pending',\n    result: {\n      type: 'pending'\n    }\n  },\n  {\n    description: 'an error',\n    test: check =\u003e check(1 + 1),\n    shouldEqual: 3,\n    result: {\n      type: 'failure',\n      message: 'the error message',\n      stack: [\n        'a list of',\n        'lines',\n        'from the stack trace'\n      ],\n      original: new Error // Original error object\n    }\n  }\n]\n```\n\n## A simple setup for a project using Washington as a test tool\n\nWashington is a library and a CLI, not a testing framework. This means that it does not enforce any file structure for testing and does not do any discovery of files in your project. So, how do you set it up to use it in yours?\n\nSay you have a project with the structure:\n\n```\nexample-project/\n  src/\n    addition.js\n    addition.test.js\n  package.json\n```\n\n…and `addition.js` looks like:\n\n```javascript\n// src/addition.js\nfunction addition (x, y) {\n  return x + y\n}\n\nmodule.exports = addition\n```\n\nThen you can write the tests in the `addition.test.js` as follows:\n\n```javascript\n// src/addition.test.js\nconst addition = require('./addition')\n\nmodule.exports = [\n  {\n    description: 'returns 2 when adding 1 and 1',\n    test: check =\u003e check(addition(1, 1)),\n    expectedValue: 2\n  },\n  {\n    description: 'returns 4 when adding 1 and 3',\n    test: check =\u003e check(addition(1, 3)),\n    expectedValue: 4\n  }\n]\n```\n\n…and in the `package.json` set the test script to use Washington with that file as input:\n\n```json\n  …\n  \"scripts\": {\n    \"test\": \"washington src/addition.test.js\"\n  }\n  …\n```\n\nThe output of this will be: // TODO image\n\n### Working with multiple test files\n\nOK, so what if I have another file in my project, that I want to test? Let’s add multiplication:\n\n```diff\nexample-project/\n  src/\n    addition.js\n    addition.test.js\n+    multiplication.js\n+    multiplication.test.js\n  package.json\n```\n\n```javascript\n// src/multiplication.js\nfunction multiplication (x, y) {\n  return x * y\n}\n\nmodule.exports = multiplication\n```\n\n```javascript\n// src/multiplication.test.js\nconst multiplication = require('./multiplication')\n\nmodule.exports = [\n  {\n    description: 'returns 1 when multiplying 1 by 1',\n    test: check =\u003e check(multiplication(1, 1)),\n    shouldEqual: 1\n  },\n  {\n    description: 'returns 6 when multiplying 2 by 3',\n    test: check =\u003e check(multiplication(2, 3)),\n    shouldEqual: 6\n  }\n]\n```\n\nWell, the exported values of these two test files (`addition.test.js` and `multiplication.test.js`) are just arrays. There is a very simple solution here. Let’s create and `src/index.test.js`:\n\n```diff\nexample-project/\n  src/\n    addition.js\n    addition.test.js\n    multiplication.js\n    multiplication.test.js\n+    index.test.js\n  package.json\n```\n\n```javascript\n// src/index.test.js\nconst additionTest = require('./addition.test')\nconst multiplicationTest = require('./multiplication.test')\n\nmodule.exports = additionTest.concat(multiplicationTest)\n```\n\n…and in the `package.json`:\n\n```json\n  …\n  \"scripts\": {\n    \"test\": \"washington src/index.test\"\n  }\n  …\n```\n\nRemember, there is nothing fancy going on here, your test are just data, so you can feel free to manipulate them that way. You can make a script that grabs all `.test.js` file by a glob pattern and concat them all together if you feel so inclined. Washington’s only concern is that you pass in an array of example objects.\n\n#### But what about namespaces?\n\nWashington is an opinionated tool regarding namespaces. Washington thinks you don’t need them.\n\nThere are two reasons:\n\n1. You can namespace you example descriptions. `'Addition: 1 and 1 give 2'`.\n2. If you need a deeply nested structure of tests, there’s probably room for simplification in the app/library.\n\nBut sure some of you disagree! Well, if you for some reason you really like Washington’s approach and would like to use it, but you also really want to get a nested structure of tests, remember: examples are just plain JavaScript objects. You are more than welcome to add a `namespace` or `breadcrumbs` property to them and use it in a custom formatter to organize the output of the test suite.\n\n## Why do you say this is functional?\n\n\u003e † As much as it can be in JavaScript\n\nWashington is built on principles inspired or directly taken from the Fantasy Land community. Furthermore, the test suite is just a regular array of simple objects, there is no hidden magic or state anywhere. You can easily write your own lib that consumes the Washington example format. I believe this makes Washington fairly future proof—time will tell.\n\nWashington is also friendly to a functional programming approach by providing a nice out-of-the-box experience for testing pure functions. Because the test cases are just plain objects, it’s easy to imagine automating test scenario generation. Washington could be easily combined with [jsverify](https://github.com/jsverify/jsverify) for this purpose.\n\n\u003e Shoutout to [DrBoolean](https://  egghead.io/instructors/brian-lonsdorf) who should take credit of most of my [education in functional JavaScript](https://www.youtube.com/watch?v=h_tkIpwbsxY)\n\n## Why \"Washington\"?\n\n- George Washington gave us a good example\n- We all know that he can’t lie\n\n## Collaborating\n\nTests for Washington are written in Washington. I really believe in [dogfooding](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) and [ain’t afraid of self reference](https://xkcd.com/917/).\n\nThis library is transpiler free. I ❤️ Babel but it’s not necessary for this.\n\nThe project is a [multi-package repo managed with Lerna](https://lernajs.io/). To get started, clone and then run:\n\n```\n\u003e npm install\n\u003e npm run bootstrap\n```\n\nThis installs lerna locally and then run `lerna bootstrap` that will set up all necessary symlinks between the packages.\n\n## License\n\nCopyright 2014 Fernando Vía Canel\n\nBSD 2 Clause license\n\n[See LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxaviervia%2Fwashington","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxaviervia%2Fwashington","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxaviervia%2Fwashington/lists"}