Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

dockest

Package Overview
Dependencies
Maintainers
1
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dockest

Dockest is an integration testing tool aimed at alleviating the process of evaluating unit tests whilst running multi-container Docker applications.

  • 3.1.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
1.9K
increased by28.7%
Maintainers
1
Weekly downloads
 
Created
Source

Dockest

Dockest is an integration testing tool aimed at alleviating the process of evaluating unit tests whilst running multi-container Docker applications.

dockest logo



licence npm downloads licence licence snyk

Table of contents

Introduction

Motivation

The original motivation for Dockest, along with real world examples, can be read in this blog article.

Dockest was born out of frustration and with a vision to make developers’ lives slightly less miserable.

Dockest provides an abstraction for your Docker services’ lifecycles during integration testing, freeing developers from convoluted and flaky shell scripts. Adopting Dockest is super easy regardless if you’ve got existing tests or not and doesn’t necessarily require additional CI pipeline steps.

Why Dockest

The value that Dockest provides over e.g. plain docker-compose is that it figures out the connectivity and responsiveness status of each individual service (either synchronously or asynchronously) and once all services are ready the tests run.

Example use cases

Dockest can be used in a variety of use cases and situations, some of which can be found under packages/examples.

AWS CodeBuild

What is AWS CodeBuild?

AWS CodeBuild is a fully managed continuous integration service that compiles source code, runs tests, and produces software packages that are ready to deploy.

Cool, can I run it locally?

You can now locally test and debug your AWS CodeBuild builds using the new CodeBuild local agent.

Node.js to Node.js

Dockest can also build and run application services as part of your integration tests.

Basic Usage

System requirements

In order to run Dockest, there's a few system requirements:

  • Dockest uses Jest's programmatic CLI and requires Jest v20.0.0 or newer to work
  • Docker
  • Docker Compose ("On desktop systems like Docker Desktop for Mac and Windows, Docker Compose is included as part of those desktop installs.")

Install

yarn add --dev dockest
# npm install --save-dev dockest

Application code

// cache.ts
export const cacheKey = 'arbitraryNumberKey';

export const setCache = (redisClient: Redis, arbitraryNumber: number) => {
  redisClient.set(cacheKey, arbitraryNumber);
};

Unit tests

// cache.spec.ts
import Redis from 'ioredis'; // ... or client of choice
import { cacheKey, setCache } from './cache';

const redisClient = new Redis({
  host: 'localhost',
  port: 6379, // Match with configuration in docker-compose.yml
});

it('should cache an arbitrary number', async () => {
  const arbitraryNumber = 5;

  await setCache(redisClient, arbitraryNumber);

  const cachedValue = await redisClient.get(cacheKey);
  expect(cachedValue).toEqual(arbitraryNumber);
});

Dockest integration tests

Transform unit test into an integration test by creating a docker-compose.yml and dockest.ts file.

Important note for the Compose file

Dockest expects services' ports to be defined using long format and works best with high versions of docker-compose (i.e. 3.7 or higher)

# docker-compose.yml
version: '3.8'

services:
  myRedis:
    image: redis:5.0.3-alpine
    ports:
      - published: 6379
        target: 6379
// dockest.ts
import { Dockest } from 'dockest';

const dockest = new Dockest();

// Specify the services from the Compose file that should be included in the integration test
const dockestServices = [
  {
    serviceName: 'myRedis', // Must match a service in the Compose file
  },
];

dockest.run(dockestServices);

Configure scripts

Configure package.json to run dockest.ts. ts-node is recommended for TypeScript projects.

{
  "scripts": {
    "test": "ts-node ./dockest"
  },
  "devDependencies": {
    "dockest": "...",
    "ts-node": "..."
  }
}

Run

Finally, run the tests:

yarn test

Development

Publishing a new version

Prep

  • Decide on a version. Let's reference it as <VERSION>
    • e.g. v1.0.0, append -alpha.0 or -beta.0 to inform the CI to publish the package to npm as such (i.e. not as "latest")
  • Create release branch git checkout -b "release-v<VERSION>"
  • Make sure CHANGELOG.md contains the changes for the upcoming version

Creating a new version

  • Be in release branch
  • Make sure all changes are pushed to remote
  • Run yarn lerna version <VERSION> (e.g. yarn lerna version v3.0.0-beta.0) from project root, this will:
    • Bump all packages configured with Lerna
    • Create a git tag
    • Push changes and tags (git push --tags to include tags)

From here, the pipeline will publish the library's new version to npm

Clean up

  • Merge release branch into master
  • Delete release branch

Contributing

Setup and testing

This is a monorepo using lerna, meaning all scripts can be run from root.

yarn prep will executes the necessary scripts to install dependencies for all packages (including root) as well as build whatever needs building.

yarn dev:link will link the library source to each example, making developing a smoother experience.

API Reference

DockestOpts

import { Dockest } from 'dockest';

const { run } = new Dockest(opts);

DockestOpts

DockestOpts is optional, i.e. the dockest constructor can be called without arguments.

DockestOpts structure:

propertytypedefault
composeFilestringdocker-compose.yml
composeOptsobjectsee paragraph on composeOpts
debugbooleanfalse
dumpErrorsbooleanfalse
exitHandlerfunctionnull
jestLibobjectrequire('jest')
jestOptsobject{}
logLevelobjectlogLevel.INFO, i.e. 3
runInBandbooleantrue
DockestOpts.composeFile

Compose file(s) with services to use while running tests

DockestOpts.composeOpts

composeOpts structure:

propertydesriptiontypedefault
alwaysRecreateDepsRecreate dependent containers. Incompatible with --no-recreatebooleanfalse
buildBuild images before starting containersbooleanfalse
forceRecreateRecreate containers even if their configuration and image haven't changedbooleanfalse
noBuildDon't build an image, even if it's missingbooleanfalse
noColorProduce monochrome outputbooleanfalse
noDepsDon't start linked servicesbooleanfalse
noRecreateIf containers already exist, don't recreate them. Incompatible with --force-recreate and -Vbooleanfalse
quietPullPull without printing progress informationbooleanfalse

Forwards options to docker-compose up, Docker's docs.

DockestOpts.debug

Pauses Dockest just before executing Jest. Useful for more rapid development using Jest manually

DockestOpts.dumpErrors

Serializes errors and dumps them in dockest-error.json. Useful for debugging.

DockestOpts.exitHandler

Callback that will run before exit. Received one argument of type { type: string, code?: number, signal?: any, error?: Error, reason?: any, p?: any }

DockestOpts.jestLib

The Jest library itself, typically passed as { lib: require('jest') }. If omitted, Dockest will attempt to require Jest from your application's dependencies. If absent, Dockest will use it's own version.

DockestOpts.jestOpts

Jest's CLI options, an exhaustive list of CLI-options can be found in Jest's documentation

DockestOpts.logLevel

Decides how much logging will occur. Each level represents a number ranging from 0-4

DockestOpts.runInBand [boolean]

Initializes and runs the Runners in sequence. Disabling this could increase performance

Note: Jest runs tests in parallel per default, which is why Dockest defaults runInBand to true. This will cause jest to run sequentially in order to avoid race conditions for I/O operations. This may lead to longer runtimes.

Run

import { Dockest } from 'dockest';

const { run } = new Dockest();

const dockestServices = [
  {
    serviceName: 'service1',
    commands: ['echo "Hello name1 🌊"'],
    dependents: [
      {
        serviceName: 'service2',
      },
    ],
    readinessCheck: () => Promise.resolve(),
  },
];

run(dockestServices);

DockestService

Dockest services are meant to map to services declared in the Compose file(s)

DockestService structure:

propertytypedefault
namestringproperty is required
commands(string | function)[] => string[][]
dependentsDockestService[][]
readinessCheckfunction() => Promise.resolve()

DockestService.name

Service name that matches the corresponding service in your Compose file

DockestService.commands

Bash scripts that will run once the service is ready. E.g. database migrations.

Can either be a string, or a function that generates a string. The function is fed the container id of the service.

DockestService.dependents

dependents are Dockest services that are are dependent on the parent service.

For example, the following code

const dockestServices = [
  {
    serviceName: 'service1',
    dependents: [
      {
        serviceName: 'service2',
      },
    ],
  },
];

will ensure that service1 starts up and is fully responsive before even attempting to start service2.

Why not rely on the Docker File service configuration options depends_on?

Docker's docs explains this very neatly:

version: '3.8'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

depends_on does not wait for db and redis to be “ready” before starting web - only until they have been started.

DockestService.readinessCheck

The Dockest Service's readinessCheck function helps determining a service's readiness (or "responsiveness") by, for example, querying a database using select 1. The readinessCheck function receive the corresponding Compose service configuration from the Compose file as first argument and the containerId as the second.

The readinessCheck takes a single argument in form of an object.

const dockestServices = [
  {
    serviceName: 'service1',
    readinessCheck: async ({
      containerId,
      defaultReadinessChecks: { postgres, redis, web },
      dockerComposeFileService: { ports },
      logger,
    }) => {
      // implement your readinessCheck...
    },
  },
];

readinessCheck structure:

propertydescription
containerIdThe Docker container's id.
defaultReadinessChecksDockest 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.
dockerComposeFileServiceThis is an object representation of your service's information from the Compose file.
loggerAn 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.
defaultReadinessChecks
defaultReadinessChecks.postgres

The default readiness check for PostgreSQL is based on this image which expects certain environment variables.

# docker-compose.yml
version: '3.8'

services:
  postgres: # (1)
    image: postgres:9.6-alpine
    ports:
      - published: 5432
        target: 5432
    environment: # (2)
      POSTGRES_DB: baby
      POSTGRES_USER: dont
      POSTGRES_PASSWORD: hurtme
// dockest.ts
import { Dockest } from 'dockest';

const { run } = new Dockest();

run([
  {
    serviceName: 'postgres', // must match (1)
    readinessCheck: async ({
      defaultReadinessChecks: { postgres },
      dockerComposeFileService: {
        environment: { POSTGRES_DB, POSTGRES_USER }, // must match (2)
      },
    }) => postgres({ POSTGRES_DB, POSTGRES_USER }),
  },
]);
defaultReadinessChecks.redis

The default readiness check for Redis is based on this image which is plug-and-play.

# docker-compose.yml
version: '3.8'

services:
  redis: # (1)
    image: redis:5.0.3-alpine
    ports:
      - published: 6379
        target: 6379
// dockest.ts
import { Dockest } from 'dockest';

const { run } = new Dockest();

run([
  {
    serviceName: 'redis', // must match (1)
    readinessCheck: ({ defaultReadinessChecks: { redis } }) => redis(),
  },
]);
defaultReadinessChecks.web [WIP]

Requires wget. The image would most likely be a self-built web service.

The exact use case should be fleshed out.

// dockest.ts
import { Dockest } from 'dockest';

const { run } = new Dockest();

run([
  {
    serviceName: 'web', // must match (1)
    readinessCheck: async ({ defaultReadinessChecks: { web } }) => web(),
  },
]);

Utils

logLevel object

Helper constant for DockestOpts

import { logLevel } from 'dockest';

console.log(logLevel);

// {
//   NOTHING: 0,
//   ERROR: 1,
//   WARN: 2,
//   INFO: 3,
//   DEBUG: 4
// }

sleep function

Sleeps for X milliseconds.

import { sleep } from 'dockest';

const program = async () => {
  await sleep(1337);
};

program();

sleepWithLog function

Sleeps for X seconds, printing a message each second with the progress.

import { sleepWithLog } from 'dockest';

const program = async () => {
  await sleepWithLog(13, 'sleeping is cool');
};

program();

execa function

Exposes the internal wrapper of the execa library.

import { execa } from 'dockest';

const opts = {
  logPrefix,
  logStdout,
  execaOpts,
  runner,
};

const program = async () => {
  await execa(`echo "hello :wave:"`, opts);
};

program();

opts structure:

propertydescriptiontypedefault
logPrefixPrefixes logsstring'[Shell]'
logStdoutPrints stdout from the child processbooleanfalse
execaOptsOptions passed to the execa functionobject{}
runnerInternal representation of a DockestService. Ignore thisRunner-

Versioned Documentation

Acknowledgements

Thanks to Juan Lulkin for the logo ❤️

Thanks to Laurin Quast for great ideas and contributions 💙

License

MIT

Keywords

FAQs

Package last updated on 23 Oct 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc