{"id":19562034,"url":"https://github.com/erikengervall/dockest","last_synced_at":"2025-04-06T18:14:30.202Z","repository":{"id":34016444,"uuid":"162917577","full_name":"erikengervall/dockest","owner":"erikengervall","description":"Docker + Jest integration testing for Node.js","archived":false,"fork":false,"pushed_at":"2024-04-11T02:06:00.000Z","size":4280,"stargazers_count":108,"open_issues_count":19,"forks_count":16,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-05-02T05:52:35.869Z","etag":null,"topics":["docker","docker-compose","hacktoberfest","integration-testing","jest","nodejs","testing","typescript"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/dockest","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/erikengervall.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-23T19:15:00.000Z","updated_at":"2024-05-13T22:37:08.482Z","dependencies_parsed_at":"2023-12-20T07:18:28.231Z","dependency_job_id":"c5241302-f956-4c50-9965-991be17084a0","html_url":"https://github.com/erikengervall/dockest","commit_stats":{"total_commits":212,"total_committers":10,"mean_commits":21.2,"dds":0.4056603773584906,"last_synced_commit":"3ff6ca47c2ed4ef01cb22dee5efec62efea1ed5f"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikengervall%2Fdockest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikengervall%2Fdockest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikengervall%2Fdockest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikengervall%2Fdockest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erikengervall","download_url":"https://codeload.github.com/erikengervall/dockest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247526763,"owners_count":20953143,"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":["docker","docker-compose","hacktoberfest","integration-testing","jest","nodejs","testing","typescript"],"created_at":"2024-11-11T05:13:14.760Z","updated_at":"2025-04-06T18:14:30.185Z","avatar_url":"https://github.com/erikengervall.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dockest\n\nDockest is an integration testing tool aimed at alleviating the process of evaluating unit tests whilst running\nmulti-container Docker applications.\n\n\u003cp align=\"center\"\u003e\n  \u003ca href='https://erikengervall.github.io/dockest/'\u003e\u003cimg alt=\"dockest logo\" width=\"300px\" src=\"https://raw.githubusercontent.com/erikengervall/dockest/master/resources/img/logo.png\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"licence\" src=\"https://github.com/erikengervall/dockest/workflows/Node.js%20CI/badge.svg\"\u003e\n\n  \u003ca href=\"https://www.npmjs.com/package/dockest\"\u003e\n    \u003cimg alt=\"npm downloads\" src=\"https://img.shields.io/npm/dm/dockest\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/erikengervall/dockest/blob/master/LICENSE\"\u003e\n    \u003cimg alt=\"licence\" src=\"https://img.shields.io/npm/l/dockest\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://app.netlify.com/sites/dockest/deploys\"\u003e\n    \u003cimg alt=\"licence\" src=\"https://api.netlify.com/api/v1/badges/36a8e5f8-42bf-402a-93a3-ba05ff7462b6/deploy-status\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://snyk.io/test/github/erikengervall/dockest\"\u003e\n    \u003cimg alt=\"snyk\" src=\"https://snyk.io/test/github/erikengervall/dockest/badge.svg\"\u003e\n  \u003c/a\u003e\n\u003cp\u003e\n\n# Table of contents\n\n- [Table of contents](#table-of-contents)\n- [Introduction](#introduction)\n- [Basic usage](#basic-usage)\n- [Development](#development)\n- [API Reference](#api-reference)\n- [Versioned Documentation](#versioned-documentation)\n- [Acknowledgements](#acknowledgements)\n- [License](#license)\n\n# Introduction\n\n## Motivation\n\nThe original motivation for Dockest, along with real world examples, can be read in this\n[blog article](https://engineering.klarna.com/node-js-integration-testing-with-ease-fab5f8d29163).\n\n\u003e Dockest was born out of frustration and with a vision to make developers’ lives slightly less miserable.\n\nDockest provides an abstraction for your Docker services’ lifecycles during integration testing, freeing developers from\nconvoluted and flaky shell scripts. Adopting Dockest is super easy regardless if you’ve got existing tests or not and\ndoesn’t necessarily require additional CI pipeline steps.\n\n## Why Dockest\n\nThe value that Dockest provides over e.g. plain docker-compose is that it figures out the connectivity and\nresponsiveness status of each individual service (either synchronously or asynchronously) and once all services are\nready the tests run.\n\n## Example use cases\n\nDockest can be used in a variety of use cases and situations, some of which can be found under\n[`packages/examples`](https://github.com/erikengervall/dockest/tree/master/packages/examples).\n\n### AWS CodeBuild\n\nWhat is AWS CodeBuild?\n\n\u003e [AWS CodeBuild is a fully managed continuous integration service that compiles source code, runs tests, and produces software packages that are ready to deploy.](https://aws.amazon.com/codebuild)\n\nCool, can I run it locally?\n\n\u003e [You can now locally test and debug your AWS CodeBuild builds using the new CodeBuild local agent.](https://hub.docker.com/r/amazon/aws-codebuild-local)\n\n### Node.js to Node.js\n\nDockest can also build and run application services as part of your integration tests.\n\n# Basic Usage\n\n## System requirements\n\nIn order to run Dockest, there's a few system requirements:\n\n- Dockest uses Jest's programmatic CLI and requires Jest **v20.0.0** or newer to work\n- [Docker](https://www.docker.com/)\n- [Docker Compose](https://docs.docker.com/compose/install/) (_\"On desktop systems like Docker Desktop for Mac and\n  Windows, Docker Compose is included as part of those desktop installs.\"_)\n\n## Install\n\n```bash\nyarn add --dev dockest\n# npm install --save-dev dockest\n```\n\n## Application code\n\n```ts\n// cache.ts\nexport const cacheKey = 'arbitraryNumberKey';\n\nexport const setCache = (redisClient: Redis, arbitraryNumber: number) =\u003e {\n  redisClient.set(cacheKey, arbitraryNumber);\n};\n```\n\n### Unit tests\n\n```ts\n// cache.spec.ts\nimport Redis from 'ioredis'; // ... or client of choice\nimport { cacheKey, setCache } from './cache';\n\nconst redisClient = new Redis({\n  host: 'localhost',\n  port: 6379, // Match with configuration in docker-compose.yml\n});\n\nit('should cache an arbitrary number', async () =\u003e {\n  const arbitraryNumber = 5;\n\n  await setCache(redisClient, arbitraryNumber);\n\n  const cachedValue = await redisClient.get(cacheKey);\n  expect(cachedValue).toEqual(arbitraryNumber);\n});\n```\n\n### Dockest integration tests\n\nTransform unit test into an integration test by creating a `docker-compose.yml` and `dockest.ts` file.\n\n\u003e **Important note for the Compose file**\n\u003e\n\u003e Dockest expects services' ports to be defined using\n\u003e [long format](https://docs.docker.com/compose/compose-file/#long-syntax-1) and works best with high versions of\n\u003e docker-compose (i.e. 3.7 or higher)\n\n```yml\n# docker-compose.yml\nversion: '3.8'\n\nservices:\n  myRedis:\n    image: redis:5.0.3-alpine\n    ports:\n      - published: 6379\n        target: 6379\n```\n\n```ts\n// dockest.ts\nimport { Dockest } from 'dockest';\n\nconst dockest = new Dockest();\n\n// Specify the services from the Compose file that should be included in the integration test\nconst dockestServices = [\n  {\n    serviceName: 'myRedis', // Must match a service in the Compose file\n  },\n];\n\ndockest.run(dockestServices);\n```\n\n### Configure scripts\n\nConfigure `package.json` to run `dockest.ts`. [`ts-node`](https://www.npmjs.com/package/ts-node) is recommended for\nTypeScript projects.\n\n```json\n{\n  \"scripts\": {\n    \"test\": \"ts-node ./dockest\"\n  },\n  \"devDependencies\": {\n    \"dockest\": \"...\",\n    \"ts-node\": \"...\"\n  }\n}\n```\n\n### Run\n\nFinally, run the tests:\n\n```sh\nyarn test\n```\n\n# Development\n\n## Publishing a new version\n\n## Prep\n\n- Decide on a version. Let's reference it as `v1.2.3`\n  - Append `-alpha.0` or `-beta.0` to create a tagged release\n- Create release branch `git checkout -b \"release-v1.2.3\"`\n- Run `yarn lerna version --no-push --no-git-tag-version`\n- Merge version updates into `main` branch\n- Create a new release at https://github.com/erikengervall/dockest/releases/new and let the CI do the rest\n\u003c!-- - Run `yarn lerna version v1.2.3` (e.g. `yarn lerna version v1.2.3-beta.0`) from project root, this will:\n  - Bump all packages configured with Lerna\n  - Create a git tag\n  - Push changes and tags (`git push --tags` to include tags) --\u003e\n\n## Contributing\n\n**Setup and testing**\n\nThis is a monorepo using [lerna](https://github.com/lerna/lerna), meaning all scripts can be run from root.\n\n`yarn prep` will executes the necessary scripts to install dependencies for all packages (including root) as well as\nbuild whatever needs building.\n\n`yarn dev:link` will link the library source to each example, making developing a smoother experience.\n\n# API Reference\n\n## DockestOpts\n\n```ts\nimport { Dockest } from 'dockest';\n\nconst { run } = new Dockest(opts);\n```\n\n### DockestOpts\n\n`DockestOpts` is optional, i.e. the dockest constructor can be called without arguments.\n\n`DockestOpts` structure:\n\n| property                               | type       | default                                                 |\n| -------------------------------------- | ---------- | ------------------------------------------------------- |\n| [composeFile](#dockestoptscomposefile) | `string`   | `docker-compose.yml`                                    |\n| [composeOpts](#dockestoptscomposeopts) | `object`   | see paragraph on [composeOpts](#dockestoptscomposeopts) |\n| [debug](#dockestoptsdebug)             | `boolean`  | `false`                                                 |\n| [dumpErrors](#dockestoptsdumperrors)   | `boolean`  | `false`                                                 |\n| [exitHandler](#dockestoptsexitHandler) | `function` | `null`                                                  |\n| [jestLib](#dockestoptsjestLib)         | `object`   | `require('jest')`                                       |\n| [jestOpts](#dockestoptsjestOpts)       | `object`   | `{}`                                                    |\n| [logLevel](#dockestoptslogLevel)       | `object`   | `logLevel.INFO`, i.e. `3`                               |\n| [runInBand](#dockestoptsrunInBand)     | `boolean`  | `true`                                                  |\n\n#### `DockestOpts.composeFile`\n\nCompose file(s) with services to use while running tests\n\n#### `DockestOpts.composeOpts`\n\n`composeOpts` structure:\n\n| property           | desription                                                                                      | type      | default |\n| ------------------ | ----------------------------------------------------------------------------------------------- | --------- | ------- |\n| alwaysRecreateDeps | Recreate dependent containers. Incompatible with `--no-recreate`                                | `boolean` | false   |\n| build              | Build images before starting containers                                                         | `boolean` | false   |\n| forceRecreate      | Recreate containers even if their configuration and image haven't changed                       | `boolean` | false   |\n| noBuild            | Don't build an image, even if it's missing                                                      | `boolean` | false   |\n| noColor            | Produce monochrome output                                                                       | `boolean` | false   |\n| noDeps             | Don't start linked services                                                                     | `boolean` | false   |\n| noRecreate         | If containers already exist, don't recreate them. Incompatible with `--force-recreate` and `-V` | `boolean` | false   |\n| quietPull          | Pull without printing progress information                                                      | `boolean` | false   |\n\nForwards options to `docker-compose up`, [Docker's docs](https://docs.docker.com/compose/reference/up/).\n\n#### `DockestOpts.debug`\n\nPauses Dockest just before executing Jest. Useful for more rapid development using Jest manually\n\n#### `DockestOpts.dumpErrors`\n\nSerializes errors and dumps them in `dockest-error.json`. Useful for debugging.\n\n#### `DockestOpts.exitHandler`\n\nCallback that will run before exit. Received one argument of type { type: string, code?: number, signal?: any, error?:\nError, reason?: any, p?: any }\n\n#### `DockestOpts.jestLib`\n\nThe Jest library itself, typically passed as `{ lib: require('jest') }`. If omitted, Dockest will attempt to require\nJest from your application's dependencies. If absent, Dockest will use it's own version.\n\n#### `DockestOpts.jestOpts`\n\nJest's CLI options, an exhaustive list of CLI-options can be found in [Jest's](https://jestjs.io/docs/en/cli.html)\ndocumentation\n\n#### `DockestOpts.logLevel`\n\nDecides how much logging will occur. Each level represents a number ranging from 0-4\n\n#### `DockestOpts.runInBand` [boolean]\n\nInitializes and runs the Runners in sequence. Disabling this could increase performance\n\n_Note: Jest runs tests in parallel per default, which is why Dockest defaults `runInBand` to `true`. This will cause\njest to run sequentially in order to avoid race conditions for I/O operations. This may lead to longer runtimes._\n\n## Run\n\n```ts\nimport { Dockest } from 'dockest';\n\nconst { run } = new Dockest();\n\nconst dockestServices = [\n  {\n    serviceName: 'service1',\n    commands: ['echo \"Hello name1 🌊\"'],\n    dependents: [\n      {\n        serviceName: 'service2',\n      },\n    ],\n    readinessCheck: () =\u003e Promise.resolve(),\n  },\n];\n\nrun(dockestServices);\n```\n\n## DockestService\n\nDockest services are meant to map to services declared in the Compose file(s)\n\n`DockestService` structure:\n\n| property                                        | type                                                | default                   |\n| ----------------------------------------------- | --------------------------------------------------- | ------------------------- |\n| **[name](#dockestservicename)**                 | `string`                                            | property is required      |\n| [commands](#dockestservicecommands)             | \u003ccode\u003e(string \u0026#124; function)[] =\u003e string[]\u003c/code\u003e | `[]`                      |\n| [dependents](#dockestservicedependents)         | `DockestService[]`                                  | `[]`                      |\n| [readinessCheck](#dockestservicereadinesscheck) | `function`                                          | `() =\u003e Promise.resolve()` |\n\n### `DockestService.name`\n\nService name that matches the corresponding service in your Compose file\n\n### `DockestService.commands`\n\nBash scripts that will run once the service is ready. E.g. database migrations.\n\nCan either be a string, or a function that generates a string. The function is fed the container id of the service.\n\n### `DockestService.dependents`\n\n`dependents` are Dockest services that are are dependent on the parent service.\n\nFor example, the following code\n\n```ts\nconst dockestServices = [\n  {\n    serviceName: 'service1',\n    dependents: [\n      {\n        serviceName: 'service2',\n      },\n    ],\n  },\n];\n```\n\nwill ensure that `service1` starts up and is fully responsive before even attempting to start `service2`.\n\n\u003e Why not rely on the Docker File service configuration options `depends_on`?\n\n[Docker's docs](https://docs.docker.com/compose/compose-file/#depends_on) explains this very neatly:\n\n```yaml\nversion: '3.8'\nservices:\n  web:\n    build: .\n    depends_on:\n      - db\n      - redis\n  redis:\n    image: redis\n  db:\n    image: postgres\n```\n\n\u003e `depends_on` does not wait for `db` and `redis` to be “ready” before starting `web` - only until they have been\n\u003e started.\n\n### `DockestService.readinessCheck`\n\nThe Dockest Service's readinessCheck function helps determining a service's readiness (or \"responsiveness\") by, for\nexample, querying a database using `select 1`. The readinessCheck function receive the corresponding Compose service\nconfiguration from the Compose file as first argument and the containerId as the second.\n\nThe readinessCheck takes a single argument in form of an object.\n\n```ts\nconst dockestServices = [\n  {\n    serviceName: 'service1',\n    readinessCheck: async ({\n      containerId,\n      defaultReadinessChecks: { postgres, redis, web },\n      dockerComposeFileService: { ports },\n      logger,\n    }) =\u003e {\n      // implement your readinessCheck...\n    },\n  },\n];\n```\n\n`readinessCheck` structure:\n\n| property                 | description                                                                                                                                                                                              |\n| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| containerId              | The Docker [container's id](https://docs.docker.com/engine/reference/run/#container-identification).                                                                                                     |\n| defaultReadinessChecks   | Dockest exposes a few default readinessChecks that developers can use. These are plug-and-play async functions that will attempt to establish responsiveness towards a service.                          |\n| dockerComposeFileService | This is an object representation of your service's information from the Compose file.                                                                                                                    |\n| logger                   | An instance, specific to this particular Dockest Service (internally known as Runner), of the internal Dockest logger. Using this logger will prettify and contextualize logs with e.g. the serviceName. |\n\n#### `defaultReadinessChecks`\n\n#### `defaultReadinessChecks.postgres`\n\nThe default readiness check for PostgreSQL is based on this [image](https://hub.docker.com/_/postgres) which expects\ncertain environment variables.\n\n```yaml\n# docker-compose.yml\nversion: '3.8'\n\nservices:\n  postgres: # (1)\n    image: postgres:9.6-alpine\n    ports:\n      - published: 5432\n        target: 5432\n    environment: # (2)\n      POSTGRES_DB: baby\n      POSTGRES_USER: dont\n      POSTGRES_PASSWORD: hurtme\n```\n\n```ts\n// dockest.ts\nimport { Dockest } from 'dockest';\n\nconst { run } = new Dockest();\n\nrun([\n  {\n    serviceName: 'postgres', // must match (1)\n    readinessCheck: async ({\n      defaultReadinessChecks: { postgres },\n      dockerComposeFileService: {\n        environment: { POSTGRES_DB, POSTGRES_USER }, // must match (2)\n      },\n    }) =\u003e postgres({ POSTGRES_DB, POSTGRES_USER }),\n  },\n]);\n```\n\n#### `defaultReadinessChecks.redis`\n\nThe default readiness check for Redis is based on this [image](https://hub.docker.com/_/postgres) which is\nplug-and-play.\n\n```yaml\n# docker-compose.yml\nversion: '3.8'\n\nservices:\n  redis: # (1)\n    image: redis:5.0.3-alpine\n    ports:\n      - published: 6379\n        target: 6379\n```\n\n```ts\n// dockest.ts\nimport { Dockest } from 'dockest';\n\nconst { run } = new Dockest();\n\nrun([\n  {\n    serviceName: 'redis', // must match (1)\n    readinessCheck: ({ defaultReadinessChecks: { redis } }) =\u003e redis(),\n  },\n]);\n```\n\n#### `defaultReadinessChecks.web` [WIP]\n\nRequires [wget](https://www.gnu.org/software/wget/). The image would most likely be a self-built web service.\n\nThe exact use case should be fleshed out.\n\n```ts\n// dockest.ts\nimport { Dockest } from 'dockest';\n\nconst { run } = new Dockest();\n\nrun([\n  {\n    serviceName: 'web', // must match (1)\n    readinessCheck: async ({ defaultReadinessChecks: { web } }) =\u003e web(),\n  },\n]);\n```\n\n## Utils\n\n### `logLevel` object\n\nHelper constant for DockestOpts\n\n```ts\nimport { logLevel } from 'dockest';\n\nconsole.log(logLevel);\n\n// {\n//   NOTHING: 0,\n//   ERROR: 1,\n//   WARN: 2,\n//   INFO: 3,\n//   DEBUG: 4\n// }\n```\n\n### `sleep` function\n\nSleeps for X **milliseconds**.\n\n```ts\nimport { sleep } from 'dockest';\n\nconst program = async () =\u003e {\n  await sleep(1337);\n};\n\nprogram();\n```\n\n### `sleepWithLog` function\n\nSleeps for X **seconds**, printing a message each second with the progress.\n\n```ts\nimport { sleepWithLog } from 'dockest';\n\nconst program = async () =\u003e {\n  await sleepWithLog(13, 'sleeping is cool');\n};\n\nprogram();\n```\n\n### `execa` function\n\nExposes the internal wrapper of the `execa` library.\n\n```ts\nimport { execa } from 'dockest';\n\nconst opts = {\n  logPrefix,\n  logStdout,\n  execaOpts,\n  runner,\n};\n\nconst program = async () =\u003e {\n  await execa(`echo \"hello :wave:\"`, opts);\n};\n\nprogram();\n```\n\n`opts` structure:\n\n| property  | description                                                                                                                                 | type      | default     |\n| --------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- |\n| logPrefix | Prefixes logs                                                                                                                               | `string`  | `'[Shell]'` |\n| logStdout | Prints `stdout` from the child process                                                                                                      | `boolean` | `false`     |\n| execaOpts | [Options](https://github.com/sindresorhus/execa/blob/df08cfb2d849adb31dc764ca3ab5f29e5b191d50/index.d.ts#L230) passed to the execa function | `object`  | `{}`        |\n| runner    | Internal representation of a DockestService. Ignore this                                                                                    | `Runner`  | -           |\n\n# Versioned Documentation\n\n- [Latest](https://github.com/erikengervall/dockest/blob/master/README.md)\n- [2.0.0](https://github.com/erikengervall/dockest/tree/94bac6f7d11588909fb42d8ce3ebbb3eccc3c49c/website/versioned_docs/version-2.0.0)\n- [1.4.0](https://github.com/erikengervall/dockest/tree/94bac6f7d11588909fb42d8ce3ebbb3eccc3c49c/website/versioned_docs/version-1.0.4)\n\n# Acknowledgements\n\nThanks to [Juan Lulkin](https://github.com/joaomilho) for the logo ❤️\n\nThanks to [Laurin Quast](https://github.com/n1ru4l) for great ideas and contributions 💙\n\n# License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikengervall%2Fdockest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferikengervall%2Fdockest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikengervall%2Fdockest/lists"}