{"id":13623202,"url":"https://github.com/taskworld/test-bed","last_synced_at":"2025-04-15T14:32:24.125Z","repository":{"id":48253587,"uuid":"59647828","full_name":"taskworld/test-bed","owner":"taskworld","description":":factory: Development test runner for webpack to improve TDD experience. Runs only specs affected by code change. Serve test files from memory. For large project with hundreds of test files.","archived":true,"fork":false,"pushed_at":"2021-08-04T08:41:24.000Z","size":624,"stargazers_count":33,"open_issues_count":15,"forks_count":5,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-12T12:34:13.878Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/taskworld.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-05-25T09:00:22.000Z","updated_at":"2024-02-09T09:11:23.000Z","dependencies_parsed_at":"2022-09-13T23:02:44.840Z","dependency_job_id":null,"html_url":"https://github.com/taskworld/test-bed","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taskworld%2Ftest-bed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taskworld%2Ftest-bed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taskworld%2Ftest-bed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taskworld%2Ftest-bed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/taskworld","download_url":"https://codeload.github.com/taskworld/test-bed/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223677486,"owners_count":17184482,"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":[],"created_at":"2024-08-01T21:01:29.106Z","updated_at":"2024-11-08T11:30:27.747Z","avatar_url":"https://github.com/taskworld.png","language":"JavaScript","readme":"\n# test-bed\n\n__test-bed__ is a testing tool that integrates with webpack to provide a better test-driven development experience.\nIt only executes run test files that are affected by the code change.\n\nThis project is only meant to improve the developer experience.\nIt is not for running tests inside a CI.\nFor that purpose, you should use something like Karma.\n(We use both: test-bed for TDD, Karma for CI)\n\n\n## Overview\n\nOnce test-bed is set up, you can run it by invoking `./node_modules/.bin/test-bed`.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/W4LOJEZ.png\" width=\"777\" /\u003e\u003c/p\u003e\n\nIt binds a web server on port 9011 and shows you the test result.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/KKR2v0o.png\" width=\"836\" /\u003e\u003c/p\u003e\n\nPowered by `webpack-dev-middleware`, your bundle files are served from memory. No disk writes!\n\ntest-bed integrates closely with webpack. Because of this, it can track dependencies between modules, and will re-execute only the changed files.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/CN5OfY1.png\" width=\"836\" /\u003e\u003c/p\u003e\n\nPowered by `webpack-hot-middleware`, an overlay will be displayed when there is a bundler error.\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/3vFd6TM.png\" width=\"836\" /\u003e\u003c/p\u003e\n\nIf you’ve set up code coverage instrumentation (e.g. using [babel-plugin-\\_\\_coverage\\_\\_](https://github.com/dtinth/babel-plugin-__coverage__)), then `test-bed` will generate a coverage report file (`lcov.info`) so that you can [integrate coverage measurement into your text editor](https://atom.io/packages/atom-lcov)!\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/9kOSk6m.png\" width=\"849\" /\u003e\u003c/p\u003e\n\n\n\n## Why?\n\nAt Taskworld, our front-end, as well as our test suite, is growing quickly. Now we have hundreds of test files…\n\nWe’ve been using Karma with webpack, and there are some pain points:\n\n- Karma does not load eval’d source maps.\n\n- Karma’s reporter on console is harder to read, when compared to Mocha’s HTML reporter.\n\n- When running tests for the first time, Karma will always run the whole suite. It takes quite a long time for Taskworld’s codebase (which is quite big). You need to open a new terminal tab and invoke `karma run -- --grep=MyModule` later to limit the scope of the test. And it doesn’t survive restarts.\n\nFor running in CI servers, we use Karma which works perfectly fine!\n\n\n\n## How to use it?\n\n1. Install test-bed. (Note: Please use Node 6.)\n\n    ```\n    npm install --save-dev test-bed\n    ```\n\n2. Create a `webpack.config.test-bed.js` file with your webpack configuration.\n\n    - `entry` should be set to the test entry file. For example, `./test-entry.js`.\n\n3. Create a test entry file, which sets up the testing environment and sends the test context to TestBed:\n\n    ```js\n    // ./test-entry.js\n    // This example assumes you are using Mocha test framework,\n    // but test-bed should work with any browser-based test framework,\n    // as long as it exposes the necessary hooks.\n\n    // 1. Set up your test environment. (e.g. mocha, power-assert, chai)\n    //    Let’s use an adapter for mocha.\n    var TestBedMocha = require('test-bed/adapters/mocha')\n    TestBedMocha.setup({ ui: 'bdd' }) // this makes `describe`, `it` available.\n\n    // 2. Set up your test environment.\n    global.chai = require('chai')\n    global.expect = global.chai.expect\n\n    // 3. Run test-bed, sending the webpack context.\n    TestBedMocha.run({\n      // Specify the test context: https://webpack.github.io/docs/context.html\n      context: require.context(\n        './src',        // ← Look for test files inside `src` directory.\n        true,           // ← Recurse into subdirectories.\n        /\\.spec\\.js$/   // ← Only consider files ending in `.spec.js`.\n      )\n    })\n    ```\n\n4. Run `./node_modules/.bin/test-bed` and go to `http://localhost:9011/`\n\n## Webpack configuration options\n\nYou can change options of the webpack middleware by adding a `webpackMiddleware` entry to `webpack.config.test-bed.js`.\nThe following code will restore the default webpack output and enable polling:\n```\n// webpack.config.test-bed.js\nmodule.exports = {\n  entry: ./test-entry.js\n  ... // other webpack options\n  webpackMiddleware: {\n    quiet: false,\n    watchOptions: {\n      aggregateTimeout: 300,\n      poll: true,\n      ignore: /node_modules/\n    }\n  }\n}\n```\n\nFurthermore, you can configure test-bed by adding a `testBed` entry to your `webpack.config.test-bed.js`:\n```\n// webpack.config.test-bed.js\nmodule.exports = {\n  ... // other webpack options\n  testBed: {\n    openBrowser: true\n  }\n}\n```\n\nAvailable options are:\n- `port: \u003cportNumber\u003e`: Change the port test-bed should use. Using a value of `0` tells test-bed to find a free port\n  (default is `port: 9011`)\n\n- `openBrowser: \u003ctrue/false\u003e`: Determine if test-bed should automatically try to open your systems default browser\n  (default is `openBrowser: false`)\n\n- `configureExpressApp: \u003cfunction(app, express)\u003e`: Change the server configuration. The following code will make all\n  files in `test/resources` available under `localhost:9011/base/resources` and log all requests:\n  ```\n  configureExpressApp: function (app, express) {\n                         app.use('/base/resources', express.static('test/resources'))\n                         app.use(function (req, res, next) {\n                           console.log('Request received:', req.url)\n                           next()\n                         })\n                       }\n  ```\n\n\n## Command line options\n\n- `--help`: display available command line options\n- `-b true`, `--browser true`: automatically open test-bed in your systems default browser (can be `true` or `false`,\n  overrides setting in `webpack.config.test-bed.js`)\n- `-c myconfig.js`, `--config myconfig.js`: Use the webpack configuration given in `myconfig.js` instead of\n  `webpack.config.test-bed.js`. Allows you to e.g. use different test contexts with subsets of test.\n- `-p 9876`, `--port 9876`: Use a different port, e.g. `9876`, instead of the default of `9011`. Also overrides any port\n  specified in `webpack.config.test-bed.js`. A value of `0` will tell test-bed to find a free port.\n\n## Appendix: How it works...\n\n- First, test-bed fires up webpack-dev-middleware, which puts webpack in watch mode using memory file system.\n\n  webpack also builds a module graph.\n  Each module has a unique “ID” number (which can be accessed from client code).\n\n  \u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/WBbVQ8F.png\" width=\"700\" /\u003e\u003c/p\u003e\n\n  Notice the “context module.” [It is created when you use `require.context()`](https://webpack.github.io/docs/context.html#require-context). This allows you to require files in bulk. Note that there is a dashed line from test-bed runtime to the context module, because the test entry sent the context module to the runtime via `TestBed.run({ context: ... })`.\n\n  It also contains other useful information, such as the list of modules names inside this context and the corresponding “module IDs,” summarized in a table below.\n\n  \u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/kgni6zU.png\" width=\"700\" /\u003e\u003c/p\u003e\n\n- Now let’s consider what happens when I changed a module.\n\n  \u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/Gdi5LHc.png\" width=\"700\" /\u003e\u003c/p\u003e\n\n  - I edited `add.js`.\n\n  - webpack picks up the change and rebuilds the bundle. Only modules that are changed needs to be “rebuilt,” while the rest comes from cache.\n\n  - Once the bundle is rebuilt, webpack announces a [“stats” object](https://webpack.github.io/docs/node.js-api.html#stats), which contains the build stats, including which modules are rebuilt and which are not.\n\n- test-bed server picks up the stats object and walks the dependency graph to obtain the “affected modules.”\n\n  \u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/mJPTpRu.png\" width=\"700\" /\u003e\u003c/p\u003e\n\n- test-bed server sends the affected modules to the runtime in the client.\n\n  - The client saves the information and reload the page, thus gives us a pristine test environment, as well as access to the new bundle.\n\n  \u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/wqHgm3m.png\" width=\"700\" /\u003e\u003c/p\u003e\n\n- The runtime looks at the context module, and figures out which files to run. Finally, it requires just the affected test files, and starts the test.\n\n  \u003cp align=\"center\"\u003e\u003cimg src=\"http://i.imgur.com/JoPVabE.png\" width=\"700\" /\u003e\u003c/p\u003e\n\n\n## Appendix: Client API\n\ntest-bed comes with an adapter for Mocha.\nBut if you want to integrate test-bed with other test frameworks, you can use the client API directly.\n\n```js\nconst TestBed = require('test-bed')\n```\n\n\n### TestBed.run(options)\n\nThis function makes test-bed start running... Here are the options:\n\n- `context` Webpack context module that contains the test files. Required.\n\n- `runTests ()` A function that will be called when the tests files finish loading. This function should start running tests, and return a Promise that resolves when the test finished. Required.\n\n- `wrapRequire (key, doRequire)` A function that will be called when test-bed wants to require a test file. This function must be synchronous and call `doRequire()` once. Optional, defaults to `(key, doRequire) =\u003e { doRequire() }`.\n\n\n### Coverage measurement functions\n\ntest-bed supports code coverage measurement. However, by default, when not all test files are run, the result code code coverage can be inaccurate (until you run all tests again).\n\nTo make code coverage more accurate when running subset of tests, test-bed can record test coverage for each test separately. This is handled automatically in mocha adapter.\n\nWhen using test-bed API directly, you should call these methods to obtain more accurate coverage data:\n\n- #### TestBed.fileStarted(key)\n\n  This function should be called when the test framework is going to run the tests in a test file. Can be called at most once per test file.\n\n- #### TestBed.testStarted(testName)\n\n  This function should be called when the test framework is going to run a test.\n\n- #### TestBed.testEnded()\n\n  This function should be called when the test framework finished running a test.\n\n- #### TestBed.fileEnded()\n\n  This function should be called when the test framework finished executing tests inside a test file.\n\nSee mocha adapter source code for example.\n\n\n## Appendix: How we tripled our test speed with this one weird trick.\n\nAs our application grows, we notice that our test starts running slower and slower.\n__We found out that in our React component tests, we mounted the component but didn’t unmount it!__\n\nThis causes hundreds of components that connects to a several legacy global stores to re-render itself whenever the store triggers. This slows the store’s unit tests drastically — by about ~0.5 seconds per test.\n\nThe solution? We monkey-patched ReactDOM so that we can keep track of all the mounted component instances, then we unmount them all before starting the next test. This also forces us to keep all tests isolated.\n\n```js\n// spec-helper.js\nimport ride from 'ride'\n\nconst cleanupPreviouslyMountedComponent = (() =\u003e {\n  let _mountedContainers = [ ]\n\n  ride(ReactDOM, 'render', (render) =\u003e function (element, node) {\n    const component = render.apply(this, arguments)\n    _mountedContainers.push(node)\n    return component\n  })\n\n  return () =\u003e {\n    const containersToCleanUp = _mountedContainers\n    if (!containersToCleanUp.length) return\n    for (const container of containersToCleanUp) {\n      try {\n        ReactDOM.unmountComponentAtNode(container)\n      } catch (e) {\n        console.error('[spec-helpers] Cannot unmount component:', e)\n      }\n    }\n    _mountedContainers = [ ]\n  }\n})()\n\nbeforeEach(function () {\n  cleanupPreviouslyMountedComponent()\n})\n```\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaskworld%2Ftest-bed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftaskworld%2Ftest-bed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaskworld%2Ftest-bed/lists"}