See LICENSE.
Copyright (c) 2018 - 2023 Cristian Greco and other authors.
See CONTRIBUTING.md for how to contribute to this repo.
See contributors for all contributors.
Join our Slack workspace | Testcontainers OSS | Testcontainers Cloud
Testcontainers is a NodeJS library that supports tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
Home Page: https://testcontainers.com
License: MIT License
See LICENSE.
Copyright (c) 2018 - 2023 Cristian Greco and other authors.
See CONTRIBUTING.md for how to contribute to this repo.
See contributors for all contributors.
Join our Slack workspace | Testcontainers OSS | Testcontainers Cloud
Hi Folks,
i tried the example, "Running commands inside running container", from the README.md and i tumbled over the following issue.
TypeError: container.exec is not a function at somefile.js:17:54 at <anonymous> at process._tickCallback (internal/process/next_tick.js:189:7)
It is not part of the TestContainer interface so the GenericContainer is obviously not able to do this. Please adjust the README cause i'm now really sad cause i can not work around the getContainerIpAddress issue ;)
Cheers
Christian
I would like to expose for example MySQL container ports, how can I define the container host ports? There is a function like in the Java package called .withFixedExposedPort(3306, 3306)
where I can define the host and the container ports?
Can you help me to solve this problem?? im runing version 2.0 .0 in a windows machine, yeah sad =/ but true. Here is the code:
const containers = {}
describe(`Integration Testing ${__filename}`, () => {
before(async done => {
containers.mysqlContainer = await new GenericContainer('mysql')
.withEnv('MYSQL_ROOT_PASSWORD', 'test')
.withExposedPorts(3306)
await containers.mysqlContainer.start()
process.env.DB_HOST_TEST = `mysql://test:test@localhost:${containers.mysqlContainer.getMappedPort(3306)}`
done()
})
...
The container start, i can see it running docker ps but when i run npm test, it fails.
the error code:
Integration Testing
testcontainers INFO: Using Docker defaults +0ms
testcontainers INFO: Creating container for image: mysql:latest +66ms
Redis conectado
testcontainers INFO: Starting container with ID: 8e5214f4cabf114262aeb7a23811f01d0ab6caf04bb6448ae52021b4e2a8105d +173ms
testcontainers DEBUG: Starting container health checks +1s
testcontainers DEBUG: Waiting for host port :54979 +4ms
testcontainers DEBUG: Waiting for internal port :3306 +5ms
And stops here, nothing responds. Any idea?
Thanks in advance.
Any way/plans to support user-defined networks? From what I see testcontainers-java
has such functionality.
Using the following code I am unable to connect to the spawned Docker. Is there something I'm doing wrong?
const { createPool } = require('mysql2/promise')
const { GenericContainer, Wait } = require('testcontainers')
const mysqlContainer = async () => {
const container = await new GenericContainer('mysql', '5.7')
.withExposedPorts(3306)
.withEnv('MYSQL_ALLOW_EMPTY_PASSWORD', '1')
.withEnv('MYSQL_DATABASE', 'testdb')
.withWaitStrategy(Wait.forLogMessage('mysqld: ready for connections.'))
.start()
console.log('Container started')
return container
}
(async () => {
const container = await mysqlContainer()
try {
const connection = await createPool({ host: 'localhost', user: 'root', password: '', port: container.getMappedPort(3306) })
console.log('Connected to database')
console.log(await (connection.query('SELECT count(*) FROM information_schema.columns')))
} catch (e) {
console.error(e, e.stack)
}
await container.stop()
})()
It would be helpful to be able to specify a tmpfs
setting, the same way you can with testcontainers-java:
testcontainers/testcontainers-java#673
My use case is that I'd like to point a test database's data volume at a tmpfs in order to make our unit tests run quicker.
Thanks.
It would be nice to have the ability to pull images from private registries the same way as java
lib does.
https://github.com/testcontainers/testcontainers-java/blob/master/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java
~/.docker/config.json
credHelpers
and credsStore
In my application, I have to separate different test environments into different Dockerfile,
and name each of them in different Dockerfile names. (e.g. Dockerfile-a, Dockerfile-b, ...)
Can I achieve this?
Since I didn't find any fileName support from the GenericContainer.fromDockerfile
api.
Thanks a lot!
Hi! I love this library, thanks for making it available!
I'm having an issue on one of my two machines but I can't figure out the root cause. I have been using the library for several months now and it was working however a few days ago, my test container wouldn't start on one of my machines. On another machine it still works well. I tried comparing the Docker versions but there doesn't seem to be an obvious difference. (I'm a relative docker n00b though.)
What's happening is that the start()
method is throwing an error and not starting.
I'm not sure exactly what would be helpful for you to know so I'll put some relevant information below. I don't really think it's your library but I'm wondering what other dependencies I should look at to see what the issue is.
Thanks so much!
Here is a readout of my terminal output.
env DEBUG=testcontainers yarn app:corona test JobService.test.js
yarn run v1.15.2
$ yarn workspace corona-api-poc test JobService.test.js
$ jest JobService.test.js
Determining test suites to run...
# TEST CONTAINERS DEBUG OUTPUT
testcontainers INFO: Using default Docker configuration +0ms
testcontainers INFO: Creating container for image: postgres:9.6 +21ms
testcontainers INFO: Starting container with ID: 7b5d63d4f6cdd192a164b0a223c36cdb694a2d2b2a97fc94035978119df9bab5 +129ms
testcontainers DEBUG: Starting container health checks +533ms
# ERROR HAPPENS BECAUSE THE START() METHOD FAILED
{"level":"error"}
TypeError: container.getMappedPort is not a function
at startTestDB (/Users/mitchconquer/Projects/corona/packages/corona-app/app/test/testDatabase/container.js:26:97)
at process._tickCallback (internal/process/next_tick.js:68:7)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed.
Exit code: 1
Command: /Users/mitchconquer/.nvm/versions/node/v10.15.0/bin/node
Arguments: /usr/local/Cellar/yarn/1.15.2/libexec/lib/cli.js test JobService.test.js
Directory: /Users/mitchconquer/Projects/corona/packages/corona-app
Output:
info Visit https://yarnpkg.com/en/docs/cli/workspace for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
I have traced this back to the testcontainer
library's generic-container.ts
file's waitForContainer
method. I have run the debug logs for testcontainer
and it seems that the container health checks are starting but not completing.
I also do not see the logs from the wait strategy's waitUntilReady
method so I believe the error is being thrown on the withStartupTimeout
method or the early in the waitUntilReady
method.
testcontainers
Version
From yarn.lock
testcontainers@^1.1.19:
version "1.1.19"
resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-1.1.19.tgz#0062682548b99c2ea93af07c71a86264510870e9"
integrity sha512-Aa/X92Gv0EVNRuYzxkK3AEc5hnMAgiTBhB/MpouuFTjc1Dh675Xw4KQtysy5+5S85NnQjC8RlNwshqaWC6/ikA==
dependencies:
byline "^5.0.0"
debug "^4.1.1"
dockerode "^2.5.8"
get-port "^4.1.0"
node-duration "^1.0.2"
stream-to-array "^2.3.0"
tar-fs "^2.0.0"
Docker Version
# docker version
Client: Docker Engine - Community
Version: 19.03.2
API version: 1.40
Go version: go1.12.8
Git commit: 6a30dfc
Built: Thu Aug 29 05:26:49 2019
OS/Arch: darwin/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.2
API version: 1.40 (minimum version 1.12)
Go version: go1.12.8
Git commit: 6a30dfc
Built: Thu Aug 29 05:32:21 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.2.6
GitCommit: 894b81a4b802e4eb2a91d1ce216b8817763c29fb
runc:
Version: 1.0.0-rc8
GitCommit: 425e105d5a03fabd737a126ad93d62a9eeede87f
docker-init:
Version: 0.18.0
GitCommit: fec3683
package.json
{
"name": "corona",
"version": "0.1.0",
"private": true,
"main": "src/index.js",
"engines": {
"yarn": "0.27.5 || >=1.0.2",
"node": "^10.0.0"
},
"dependencies": {
"@google-cloud/debug-agent": "^3.2.0",
"@google-cloud/logging-winston": "^0.11.1",
"@google-cloud/pubsub": "^0.28.1",
"@google-cloud/storage": "^2.5.0",
"@google-cloud/trace-agent": "^3.6.1",
"@material-ui/core": "^4.0.0-beta.0",
"@material-ui/icons": "^3.0.2",
"app-module-path": "^2.2.0",
"async": "^2.6.2",
"body-parser": "^1.19.0",
"chalk": "^2.4.2",
"classnames": "^2.2.6",
"connect-busboy": "^0.0.2",
"continuation-local-storage": "^3.2.1",
"csv-stringify": "^5.3.0",
"dotenv": "^8.0.0",
"express": "^4.16.4",
"express-winston": "^3.1.0",
"fast-crc32c": "^1.0.4",
"fast-csv": "^3.1.0",
"inquirer": "^6.3.1",
"jsrsasign": "^8.0.12",
"multer": "^1.4.1",
"nconf": "^0.10.0",
"node-fetch": "^2.5.0",
"node-sequelize-stream": "^1.0.9",
"pg": "^7.11.0",
"pg-hstore": "^2.3.2",
"react": "^16.8",
"react-dom": "^16.9",
"react-dropzone": "^10.1.4",
"react-router-dom": "^4.1.2",
"react-scripts": "^3.0.0",
"recharts": "^1.5.0",
"request": "^2.88.0",
"sendgrid": "^5.2.3",
"sequelize": "^5.8.6",
"sequelize-mock": "^0.10.2",
"short-uuid": "^3.1.1",
"styled-components": "^4.2.0",
"through2": "^3.0.1",
"unzipper": "^0.10.1",
"winston": "^3.2.1"
},
"scripts": {
"start": "node --max-old-space-size=8192 ./app/server.js",
"dev": "nodemon ./app/server.js",
"deploy": "gcloud app deploy app.yaml",
"logs": "gcloud app logs tail -s default",
"precommit": "react-scripts precommit",
"add:mapping": "node ./app/scripts/add-mapping",
"add:account": "node ./app/scripts/add-account",
"logs:worker": "gcloud app logs tail -s worker",
"test": "jest",
"test:silent": "SILENT=true jest",
"test:coverage": "jest --watchAll=false --coverage",
"test:add-rev-usd": "node ./app/scripts/add-rev.js",
"sequelize": "./node_modules/.bin/sequelize"
},
"proxy": "http://localhost:8080",
"browserslist": {
"production": [">0.2%", "not dead", "not op_mini all"],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"nodemonConfig": {
"ignore": ["test/*", "docs/*"],
"delay": "2500"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.7.0",
"dockerode": "^2.5.8",
"faker": "^4.1.0",
"http-proxy": "^1.17.0",
"jest-serial-runner": "^1.1.0",
"nodemon": "^1.19.0",
"npm-run-all": "^4.1.5",
"sequelize-cli": "^5.5.0",
"superagent": "^5.1.0",
"supertest": "^4.0.2",
"testcontainers": "^1.1.19"
}
}
container.js
Code that invokes testcontainer
, failing in the startTestDB()
method, line 31 because the container has not started. Says getMappedPort
is not a function.
'use strict';
const { getAppPath } = require('../helpers');
require('app-module-path').addPath(getAppPath());
const { GenericContainer, Wait } = require('testcontainers');
const chalk = require('chalk');
const { logger } = require('../../utils/logging');
const testSqlPort = 5432;
let container;
async function startTestDB() {
container = await new GenericContainer('postgres', '9.6')
.withEnv('POSTGRES_PASSWORD', 'password')
.withExposedPorts(testSqlPort)
.withWaitStrategy(
Wait.forLogMessage(
'PostgreSQL init process complete; ready for start up.'
)
)
.start()
.catch(err => logger.error(err));
// eslint-disable-next-line no-restricted-syntax
console.log(
chalk.green(
'\nTest database container created with port',
chalk.yellow(container.getMappedPort(testSqlPort))
)
);
return container;
}
function stopTestDB() {
if (!container) {
throw new Error('Test database container not found');
}
return container
.stop()
.then(() =>
// eslint-disable-next-line no-restricted-syntax
console.log(chalk.green('Test database container closed successfully'))
)
.catch(err =>
// eslint-disable-next-line no-restricted-syntax
console.log(chalk.red('Error closing test database container'), err)
);
}
module.exports = { startTestDB, stopTestDB, container };
i'm trying to build an image with
const buildContext = path.resolve(__dirname, '../../');
try {
const container = await GenericContainer.fromDockerfile(buildContext)
.build();
}
catch (e) {
console.log(e);
}
and that just fails after ~40 seconds w/o giving any reasonable error message:
testcontainers INFO: Using Docker defaults +0ms
testcontainers INFO: Building image '4e0f7a06234ada9ac05edc4a278b7a09:1fb761b7a169152a983a78ee352eeafb' with context '/Users/me/project' +1ms
Error: Failed to build image
at GenericContainerBuilder.<anonymous> (/Users/me/node_modules/testcontainers/dist/generic-container.js:44:23)
at Generator.next (<anonymous>)
at fulfilled (/Users/me/node_modules/testcontainers/dist/generic-container.js:4:58)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
i can see that the building starts, but cannot get logs from the 'build' container
Error response from daemon: configured logging driver does not support reading
running node v14.3.0 on macOS 10.13.6 and docker desktop 2.1.0.4 (39773) engine 19.03.4
building the container manually works fine.
is there a way to get logs from the 'build' container?
Hello from the Rust universe! :)
Thanks for creating a node
port of testcontainers!
Context: Some containers take quite a while to start up and open ports before they are actually ready (bitcoin for example).
Possible solution: One way that works well for us in testcontainers-rs is to wait for a particular log message to be emitted:
It would be nice if testcontainers-node
would support that aswell :)
Happy to send PR if you outline how you would like the library to be extended.
Hello,
thanks for this useful library.
I am trying to extend (subclass) GenericContainer
but the current typing for the withXxx
method gets in the way. To be able to extend the fluent API, these methods should return the actual type of Container class, not the base TestContainer
.
In other words, simply return a this type
from the methods e.g.
export interface TestContainer {
withEnv(key: EnvKey, value: EnvValue): this;
withCmd(cmd: Command[]): this;
// ...
}
Docker supports defining a separate HEALTHCHECK command that can be run internally to check whether the container is "healthy".
testcontainers-node
mentions doing its own "health checks" at
testcontainers-node/src/generic-container.ts
Line 180 in 878e2ef
However, this seems to refer only to checking that ports are listening. In some cases such as starting a postgres container this is not enough. The port may be opening but the DB isn't ready to receive queries, resulting in the error: "error: the database system is starting up". Note: this is not 100% verified.
testcontainers-java
has another wait strategy called Wait.forHealthcheck
https://github.com/testcontainers/testcontainers-java/blob/f113979ad5207464490cc4f19f488bb27795802c/core/src/main/java/org/testcontainers/containers/wait/strategy/Wait.java#L69 which basically keeps checking the .State.Health.Status
of the container until its true.
From my understanding of a quick check of the source code of testcontainers-node
this does not seem to be supported. It is possible that healthchecks are implicitly included somewhere along the line, but based on the Java version needing an explicit healthcheck wait strategy I assume a similar strategy would be needed for the Node version.
So what we would like is something like:
new GenericContainer('postgres').withWaitStrategy(Wait.healthcheck()).start()
One issue that seems to come up here is that you can currently only have a single wait strategy. It might be the case that you want to wait for a combination of conditions, but let's keep that out of scope for this issue.
Hi @cristianrgreco !
We noticed that you've created this project - it's very nice that you've decided to bring the Testcontainers concept to Node 😄
A NodeJS implementation was something that I've wanted to do at some point but didn't get around to yet. As you've made a start, would it be possible for us to work together to develop, maintain and publicise this?
Perhaps we could discuss here or on the Testcontainers Slack team?
Thanks!
Richard
It would be nice if the node version of test containers will be supporting the .withImagePullPolicy()
feature.
When using TypeScript with this project, we need StartedGenericContainer
to be exported in order to define a container in a top level scope properly, and use the container without assigning it to any
.
For example:
export let container: StartedGenericContainer;
export const start = async () => {
container = await new GenericContainer('postgres', '11.2-alpine')
.withExposedPorts(5432)
.start();
};
This is currently not possible, and let container: any;
must be used.
Hi Im trying to get this to work with couchdb. However I keep getting an error. Here is my code
container = await new GenericContainer("couchdb")
.withExposedPorts(5984)
.start();
After running I get this error
Error: Port :4369 not bound after 10000ms
at retryStrategy.retryUntil (node_modules/testcontainers/dist/wait-strategy.js:59:23)
at IntervalRetryStrategy.<anonymous> (node_modules/testcontainers/dist/retry-strategy.js:35:28)
at Generator.next (<anonymous>)
at fulfilled (node_modules/testcontainers/dist/retry-strategy.js:4:58)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
Not entirely sure where port 4369
is coming from. Perhaps I misunderstand the purpose of withExposedPorts
.
I also noticed that it does seem to start the container correctly when I do a docker ps
I can see the started container
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4722698ad61 couchdb:latest "tini -- /docker-ent…" 13 seconds ago Up 12 seconds 4369/tcp, 9100/tcp, 0.0.0.0:35121->5984/tcp epic_raman
UPDATE
I also tried with a simple mysql container, And I get more or less the same error as above. Code below
container = await new GenericContainer("mysql")
.withEnv("MYSQL_ROOT_PASSWORD", "my-secret-pw")
.withExposedPorts(3306)
.start();
And error
Error: Port :3306 not bound after 10000ms
at retryStrategy.retryUntil (node_modules/testcontainers/dist/wait-strategy.js:59:23)
at IntervalRetryStrategy.<anonymous> (node_modules/testcontainers/dist/retry-strategy.js:35:28)
at Generator.next (<anonymous>)
at fulfilled (node_modules/testcontainers/dist/retry-strategy.js:4:58)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
The only container that seems to work for me is the redis one in the main example on the readme
I see there's an inspect method buried in the depths of private variables. Why not expose it? Also would be great to have access to the full response rather than very limited sub-set.
I was looking for a way to retrieve an internal ip of the container on the default bridge network. Might be useful, especially without support for user-defined networks.
I have been using testcontainers-java for the past couple of year and I love it. Hence I tried to give it a shot to node version (2.7.0) of it.
I have a fairly simple test setup:
jest.setTimeout(600000)
let pgContainer: any
beforeAll(async () => {
pgContainer = await new GenericContainer('postgres')
.withEnv('POSTGRES_USER', 'test')
.withEnv('POSTGRES_PASSWORD', 'test')
.withEnv('POSTGRES_DB', 'postgres')
//added for testing purposes
//.withStartupTimeout(new Duration(200, TemporalUnit.SECONDS))
.withExposedPorts(5432)
.start()
expect(pgContainer).toBeTruthy()
console.log(pgContainer.boundPorts)
})
afterAll(async () => {
await pgContainer.stop()
})
describe('verify it works', function() { ... }
which works perfectly on my local machine:
2020-04-06T13:12:54.372Z testcontainers INFO: Using Docker defaults
2020-04-06T13:12:54.452Z testcontainers INFO: Creating container for image: postgres:latest
2020-04-06T13:12:54.586Z testcontainers INFO: Starting container with ID: 6647ae7575ff97de3be4444b704bec49edb68ffc74275f6b5be7b8e7e1ff7f79
2020-04-06T13:12:55.238Z testcontainers DEBUG: Waiting for container to be ready
2020-04-06T13:12:55.238Z testcontainers DEBUG: Waiting for host port 63608
2020-04-06T13:12:55.240Z testcontainers DEBUG: Waiting for internal port 5432
2020-04-06T13:12:57.996Z testcontainers DEBUG: Container is ready
But when I try to run it on my company's Jenkins slave (which sadly I have very limited access to)
it fails to start
$ DEBUG=testcontainers yarn test:unit
$ jest --config test/config.unit.json
2020-04-06T13:02:22.935Z testcontainers INFO: Using Docker in Docker method
2020-04-06T13:02:22.961Z testcontainers INFO: Pulling image: postgres:latest
2020-04-06T13:02:29.102Z testcontainers INFO: Creating container for image: postgres:latest
2020-04-06T13:02:30.761Z testcontainers INFO: Starting container with ID: 8735ecbb25cd26c084db2e009e6553fed518b2446e3d9a9a5edef1f9aec1b14c
2020-04-06T13:02:31.566Z testcontainers DEBUG: Waiting for container to be ready
2020-04-06T13:02:31.567Z testcontainers DEBUG: Waiting for host port 44063
2020-04-06T13:02:31.568Z testcontainers DEBUG: Waiting for internal port 5432
FAIL src/middleware/test/index.spec.ts
● Test suite failed to run
TypeError: Cannot read property 'stop' of undefined
26 |
27 | afterAll(async () => {
> 28 | await pgContainer.stop()
| ^
29 | })
30 |
31 | describe('verify it works', function() {
at ../src/middleware/clientAuth/index.spec.ts:28:21
at ../src/middleware/clientAuth/index.spec.ts:8:71
at Object.<anonymous>.__awaiter (../src/middleware/clientAuth/index.spec.ts:4:12)
at Object.<anonymous>.afterAll (../src/middleware/clientAuth/index.spec.ts:27:21)
Jenkins pipeline that is responsible to orchestrate is something like this (I use NodeJS plugin with Node version of 10.19)
stage("test") {
nodejs(nodeJSInstallationName: 'node_10') {
sh 'yarn && CI=1 yarn test'
}
}
It looks like the similar issues that have been fixed in this repository but only thing that I can't make sense of is, this is pretty much the same setup I have in my Java project (which uses a Java plugin on Jenkins instead of a NodeJS one).
Is there any tip you could give me about further debugging this?
Thanks
Hello,
First of all, thanks for the great library! 👍
I'm having a problem when running my test using jest - it happens only intermittently on my machine, but seems to happen all the time in CircleCI.
This is my test code:
let pgContainer: StartedTestContainer;
let pgPort: number;
beforeAll(async () => {
pgContainer = await new GenericContainer("postgres", "11.5")
.withEnv("POSTGRES_USER", "postgres")
.withEnv("POSTGRES_PASSWORD", "postgres")
.withExposedPorts(5432)
.start();
pgPort = pgContainer.getMappedPort(5432);
});
afterAll(async () => {
await pgContainer.stop();
});
This is the error I get from jest:
Test suite failed to run
TypeError: Cannot read property 'stop' of undefined
20 |
21 | afterAll(async () => {
> 22 | await pgContainer.stop();
| ^
23 | });
24 |
25 | describe("Database Integration", () => {
at services/notifications/src/app.spec.ts:22:21
at services/notifications/src/app.spec.ts:8:71
at Object.<anonymous>.__awaiter (services/notifications/src/app.spec.ts:4:12)
at Object.<anonymous> (services/notifications/src/app.spec.ts:21:21)
Thanks!
Similar to Java testcontainers it would be great to be able to mount files/volumes. (i.e. https://www.testcontainers.org/features/files/)
Thanks 😃
What if container already exists, with a specific name and everything - will this start it or attempt something else?
I recently ran into an issue where a container failed to start, and the problem was tricky to track down because testcontainers
does not expose the error. Even when running with DEBUG=testcontainers
, the output does not include errors at startup. In the end I had to manually start the container with docker run <image>
in order to find the error.
We're starting the container with the following code (simplified):
try {
logger.info('Starting container');
const container = await new GenericContainer('image-name')
.withCmd(['node', 'test-script'])
.withExposedPorts(3000)
.withWaitStrategy(Wait.forLogMessage('Ready on'))
.start();
/* ... stuff ... */
} catch (err) {
logger.error('Failed to do stuff', err);
process.exit(2);
}
The output I get from this is unhelpful:
$ DEBUG=testcontainers node do-stuff.js
[INFO] - Starting container...
testcontainers INFO: Using Docker defaults +0ms
testcontainers INFO: Creating container for image: image-name:latest +48ms
testcontainers INFO: Starting container with ID: <hash> +53ms
testcontainers DEBUG: Waiting for container to be ready +292ms
testcontainers DEBUG: Waiting for log message "Ready on" +1ms
[ERROR] - Failed to do stuff
When I start the container manually with docker run image-name
I get the actual error message (in my case, a simple syntax error in test-script.js
).
Ideally the error would be exposed in the catch block in the code above. Currently err
is undefined
.
Hi, Would be nice to be able to run commands when creating a container. Something such as:
new GenericContainer("redis")
.withCommand("redis-server --port 7777")
Is it already possible?
If not, I'd be glad to help with this or any other enhancement that might be in the roadmap.
Thanks!
Hello,
is there any way to use with a set of services, using a docker-compose.yml ? or is there any plan to add this functionality ?
thanks for testcontainers-node !
When running an official image (such as postgres) you're stuck with the VOLUME
s that it declares. This has the unfortunate effect of having your tests accumulate anonymous volumes, as can be seen with docker volume ls
. It would be really helpful if there was an option to remove any associated volumes when a container is stopped (which would actually be handled by passing the option to the underlying call to dockerode's remove()
):
https://docs.docker.com/engine/api/v1.37/#operation/ContainerDelete
It's pretty nasty, but you can set the underlying container's default options for remove (but obviously it would be better not to break the abstraction):
(pgContainer as any).container.container.defaultOptions.remove.v = true;
It would be helpful to be able to pass arguments to to container.stop()
which are propagated to the underlying dockerode container.stop()
. This would allow specifying the timeout, t
, that the container will wait before it is killed:
https://docs.docker.com/engine/api/v1.37/#operation/ContainerStop
When dealing with an ephemeral container for a test, you often don't care about it shutting down cleanly, since you'll be throwing it away. In order to achieve this in my tests I have to currently do a hacky (and brittle) workaround of setting the underlying container's default options for stop:
(testContainer as any).container.container.defaultOptions.stop.t = 1;
await testContainer.stop();
It would be much nicer to instead just call the top-level stop with arguments:
await testContainer.stop({ t: 1 });
Adding a .restart()
method on the StartedTestContainer interface would be really useful, and possibly more efficient/faster than having to .stop()
and .start()
in between tests.
e.g. Writing integration tests for a MySQL database where each test invocation is expected to have an empty database.
If GenericContainer is started with a command and command fails (exits with the error), shouldn't the whole thing throw and exception?
Doesn't seem to be a case.
I'm facing some troubles when trying to connect to Postgres container. In my integration tests, I'm using jest and supertest to run...
This is the error message
findAll() >> Error: Error: connect ECONNREFUSED 127.0.0.1:5432
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1083:14)
And here is my test class:
const { GenericContainer } = require('testcontainers');
const request = require('supertest');
const { Promise } = require('bluebird')
const cmd = require('node-cmd');
const app = require('../../app');
describe('User routes', () => {
jest.setTimeout(45000);
let container = '';
beforeAll(async () => {
container = await new GenericContainer('postgres', 'alpine')
.withEnv('POSTGRES_USER', 'bidu')
.withEnv('POSTGRES_PASSWORD', 'test')
.withExposedPorts(5432)
.start();
process.env.DATABASE_URL = `postgres://bidu:test@localhost:${container.getMappedPort(
5432
)}/bidu`;
const getAsync = Promise.promisify(cmd.get, { multiArgs: true, context: cmd })
await getAsync('npm run migrate up').then(data => {
console.log('cmd data', data)
}).catch(err => {
console.log('cmd err', err)
});
});
afterAll(async () => {
await container.stop();
});
test('should return 200 on get all users', async (done) => {
request(app)
.get('/v1.0/users/all')
.end((err, res) => {
console.log(res);
expect(res).not.toBeNull();
expect(res.statusCode).toBe(200);
done();
});
});
});
Could you give more examples of how to connect with Postgres or something like that?
this exists in the java implementation
Would be great to have remove
method exposed separately from stop
. Some containers stop automatically (like wrappers for executables) and then there's no way to remove them.
Hi great library thanks for it.
Does it support to run a kafka server?
I tried a basic generic container but probably I don't understand how to do it correctly.
Thanks in advance for you answer.
Hey,
Just a quick note asking if building images from a local Dockerfile was on your roadmap? From what I can see, that is not currently supported. To be clear, what I mean is creating a GenericContainer from a Dockerfile that you keep within the repository itself.
I can't get amazon/dynamodb-local image work inside Gitlab CI. Note that it works locally without any problems. I haven't tried if this works in testcontainers-java, cc @rnorth
I'm using CI settings from docs.
My .gitlab-ci.yml
:
image: node:12
stages:
- prepare
- test
# DinD service is required for Testcontainers
services:
- docker:dind
variables:
DOCKER_HOST: 'tcp://docker:2375'
DOCKER_DRIVER: overlay2
install_packages_run_linters:
stage: prepare
script:
- yarn install
- yarn lint
artifacts:
expire_in: 1 hour
paths:
- ./node_modules
test:unit:
stage: test
script:
- DEBUG=testcontainers yarn test --runInBand --coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
dependencies:
- install_packages_run_linters
I'm setting up container like this:
const startedContainer = new GenericContainer("amazon/dynamodb-local").withExposedPorts(8000).start()
Relevant CI output:
�[0KRunning with gitlab-runner 11.11.0-rc2 (7f58b1ec)
�[0;m�[0K on docker-auto-scale 0277ea0f
�[0;msection_start:1557296045:prepare_executor
�[0K�[0KUsing Docker executor with image node:12 ...
�[0;m�[0KStarting service docker:dind ...
�[0;m�[0KPulling docker image docker:dind ...
�[0;m�[0KUsing docker image sha256:6204caf6c5927058f10fab0e4661b5f7a37d9a986a89b28a176a9958a0a5b7d3 for docker:dind ...
�[0;m�[0KWaiting for services to be up and running...
�[0;m�[0KPulling docker image node:12 ...
�[0;m�[0KUsing docker image sha256:c77f0d290562eefd2c9e5481cc1707f40a43d2dd5f7719384bd3b8f05699ffd7 for node:12 ...
�[0;msection_end:1557296093:prepare_executor
�[0Ksection_start:1557296093:prepare_script
�[0KRunning on runner-0277ea0f-project-12215658-concurrent-0 via runner-0277ea0f-srm-1557295996-5afcffc7...
section_end:1557296098:prepare_script
�[0Ksection_start:1557296098:get_sources
�[0KInitialized empty Git repository in /builds/Meemaw/meshwatch-backend-core/.git/
�[32;1mFetching changes...�[0;m
�[32;1mCreated fresh repository.�[0;m
From https://gitlab.com/Meemaw/meshwatch-backend-core
* [new branch] master -> origin/master
�[32;1mChecking out fb1e6645 as master...�[0;m
�[32;1mSkipping Git submodules setup�[0;m
section_end:1557296100:get_sources
�[0Ksection_start:1557296100:restore_cache
�[0Ksection_end:1557296101:restore_cache
�[0Ksection_start:1557296101:download_artifacts
�[0K�[32;1mDownloading artifacts for install_packages_run_linters (208940828)...�[0;m
Downloading artifacts from coordinator... ok �[0;m id�[0;m=208940828 responseStatus�[0;m=200 OK token�[0;m=oi_pU5bU
section_end:1557296112:download_artifacts
�[0Ksection_start:1557296112:build_script
�[0K�[32;1m$ DEBUG=testcontainers yarn test --runInBand --coverage�[0;m
yarn run v1.15.2
warning package.json: No license field
$ tsdx test --verbose --runInBand --coverage
2019-05-08T06:15:26.975Z testcontainers Pulling image: amazon/dynamodb-local:latest
2019-05-08T06:15:44.045Z testcontainers Creating container for image: amazon/dynamodb-local:latest
2019-05-08T06:15:44.875Z testcontainers Starting container with ID: 32d5a7fed1d39799e6093fa46c52e8901156043e3b1bdf2bedd937d486133ff9
2019-05-08T06:15:46.372Z testcontainers Waiting for host port :39113
2019-05-08T06:15:46.376Z testcontainers Waiting for internal port :8000
FAIL test/monitoring-service.spec.ts (72.17s)
MonitoringService
with DynamoStorage
bookmarkMonitor
✕ should_bookmarkMonitor_when_monitorIdPassed (14ms)
✕ should_return404_when_bookmarkMonitorWithRandomId
deleteMonitor
✕ should_deleteMonitor__when_newlyCreatedMonitorParamsPassed (1ms)
✕ should_return404_when_deleteMonitorWithRandomId
getMonitorsByScheduler
✕ should_getEmptyCollection_when_searchByRandomScheduler (1ms)
✕ should_getMonitors_when_searchByTestScheduler
getMonitors
✕ should_getEmptyCollection_when_searchByRandomUserId
✕ should_getTestMonitors_when_searchByTestUserId (1ms)
createMonitor
✕ should_createNewMonitor_when_createMonitorPayloadPassed
✕ should_returnBoomWithFieldError_when_invalidCreateMonitorPayloadPassed
✕ should_returnBoomWithFieldErrors_when_emptyObjectPassed (1ms)
updateMonitor
✕ should_updateMonitor_when_updateMonitorPayloadIsPassed
✕ should_returnBoomWithFieldError_when_createMonitorPayloadPassed (1ms)
� MonitoringService › with DynamoStorage › bookmarkMonitor › should_bookmarkMonitor_when_monitorIdPassed
Port :39113 not bound after 40000ms
```
Hello,
Would it make sense to implement a wrapper around dockerode.remove()
?
I want to clean up containers after tests have finished running. Instead of installing dockerode separately in my application, this could be a nice enhancement to the library. I think it's a quite straightforward feature, but just to provide an example, I'd like to be able to do something like this.
afterAll(() => {
pgContainer.stop({ timeout: new Duration(10, TemporalUnit.SECONDS) })
pgContainer.remove()
})
I'll be happy to create a PR if that gets approval from maintainers... :)
Hello!
I am trying to connect two containers but without any success. The only way I know to achieve this is using networks.
I've tried to add the feature myself but I am struggling a bit with typescript. Can I make this possible without networks?
I have this workaround using dockerode
const opts = {
"limit": 1,
"filters": `{"name": ["${mysql.getName()}"]}`
};
const container = (await dockerode.listContainers(opts))[0];
const mysqlContainer = dockerode.getContainer(container.Id);
const ins = await mysqlContainer.inspect();
console.log(`My working IP! ${ins.NetworkSettings.IPAddress}`);
Regards!
I always run into this error when running npm run test
(HTTP code 400) bad parameter - Client sent an HTTP request to an HTTPS server.
at node_modules/docker-modem/lib/modem.js:257:17
at getCause (node_modules/docker-modem/lib/modem.js:287:7)
at Modem.Object.<anonymous>.Modem.buildPayload (node_modules/docker-modem/lib/modem.js:256:5)
at IncomingMessage.<anonymous> (node_modules/docker-modem/lib/modem.js:232:14)
I totally have no idea why.
a few info that may help
node: v10.16.1
Docker version 18.09.3, build 774a1f4eee
-- using docker toolbox instead of desktop
cheers!
I have a similar issue like #7
I am starting two docker containers in my tests (postgres and rabbitMQ). Then I construct the connection strings using container.getContainerIpAddress()
and container.getMappedPort()
. Everything works locally. However, in Gitlab CI I get the following error.
Error: connect ECONNREFUSED 127.0.0.1:44937
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1056:14)
When checking the value of getContainerIpAddress()
it always returns localhost
even when run in the Gitlab CI docker runner. While the Java counter part of testcontainers returns a real IP address like 172.17.0.1
in Gitlab CI.
Can you adjust your code to behave like your Java counter part?
I'm not sure if it's something I'm doing wrong, but I'm finding that stopping containers is really slow.
My code for this is:
close: async () => {
logger.info('Stopping test database');
await postgres.stop();
logger.info('Stopped test database');
},
And the output from this is:
[2019-09-12 08:25:48.820 +0000] INFO (nuworlds.database.wrapper/51357 on xxxxxxxx.local): Stopping test database
2019-09-12T08:25:59.120Z modem Received:
2019-09-12T08:25:59.121Z modem Sending: {
path: '/containers/ecebe4e47716f5a4bd67f355de63d7cb5eccfa8cf34e5926b8c6c07a3db95a0c?v=true',
method: 'DELETE',
headers: {},
key: undefined,
cert: undefined,
ca: undefined,
socketPath: '/var/run/docker.sock'
}
2019-09-12T08:25:59.180Z modem Received:
[2019-09-12 08:25:59.181 +0000] INFO (nuworlds.database.wrapper/51357 on xxxxxxxx.local): Stopped test database
Note the 11 seconds between my log statement and the actual call to delete the container. Given that this entire test ran in 15 seconds, this is a huge percentage of it.
Cheers
After installing testcontainers
my typescript project no longer compiles. This is because testcontainers
s types depend on @types/dockerode
, however these are only a dev dependency of testcontainers
and are thus not installed for consumers.
The fix is to convert @types/dockerode
into a regular dependency.
See this thread for further explanation
How does one exec complex commands on a container? Something like seq 5 | xargs -I INDEX echo "hi"
for example? I tried all kind of things, but it fails, obviously command gets mingled on its way to the container. Escaping quotes or pipe characters doesn't seem to have any effect. Is this even possible?
Hi,
I recently opened apocas/docker-modem#112 because I kept getting seemingly innocuous stack traces in my tests logs:
console.error node_modules/jest-jasmine2/build/jasmine/Env.js:289
Unhandled error
console.error node_modules/jest-jasmine2/build/jasmine/Env.js:290
Error: (HTTP code 500) server error - a disk usage operation is already running
at /home/jean/dev/startups/yupwego/src/yupwego/node_modules/docker-modem/lib/modem.js:257:17
at getCause (/home/jean/dev/startups/yupwego/src/yupwego/node_modules/docker-modem/lib/modem.js:287:7)
at Modem.Object.<anonymous>.Modem.buildPayload (/home/jean/dev/startups/yupwego/src/yupwego/node_modules/docker-modem/lib/modem.js:256:5)
at IncomingMessage.<anonymous> (/home/jean/dev/startups/yupwego/src/yupwego/node_modules/docker-modem/lib/modem.js:232:14)
at IncomingMessage.emit (events.js:187:15)
at endReadableNT (_stream_readable.js:1094:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
@ehossack recently commented that he started observing these messages after upgrading from testcontainers 2.0.0 to testcontainers 2.1.0 and suggests it may be related to the changes in #42
It seems testcontainers is attempting to run multiple disk free checks. It's a bit surprising because I can reproduce it with a single jest test which starts the container in a beforeAll
and stops it in an afterAll
When I'm using TestContainers with just an image name and nothing else, everything works fantastic. However, if I use a versioned image name then for some reason the container now no longer works.
For example, this works great:
const postgres = await new GenericContainer('postgres')
.withExposedPorts(5432)
.start();
But this doesn't:
const postgres = await new GenericContainer('postgres:11.5-alpine')
.withExposedPorts(5432)
.start();
On the second, I get the following output:
(HTTP code 400) unexpected - invalid reference format
at node_modules/docker-modem/lib/modem.js:257:17
at getCause (node_modules/docker-modem/lib/modem.js:287:7)
at Modem.Object.<anonymous>.Modem.buildPayload (node_modules/docker-modem/lib/modem.js:256:5)
at IncomingMessage.<anonymous> (node_modules/docker-modem/lib/modem.js:232:14)
Cheers
Hi,
I am currently facing the issue, that my freshly built container could not be found to start it for the tests.
const appPort: number = 3000
let appImage: GenericContainer = new GenericContainer(image)
if (!image) {
appImage = await GenericContainer.fromDockerfile(path.resolve(__dirname, '..'))
}
const bully: StartedTestContainer = await appImage.withExposedPorts(appPort).start()
This fails with:
pull access denied for 5668e0a8f7a4fb86f08d814c41dc4402, repository does not exist or may require 'docker login'
The uuid 5668e0a8f7a4fb86f08d814c41dc4402
is the generated one.
Does somebody else had those issues before?
Is there a solution for it?
When running versions higher than 1.3.1 in AWS CodeBuild I get an error saying "Error: Port :39195 not bound after 60000ms".
This is the full output running with "DEBUG=testcontainers":
2020-02-03T12:35:46.072Z testcontainers INFO: Using Docker in Docker method
2020-02-03T12:35:46.106Z testcontainers INFO: Pulling image: postgres:11.5
2020-02-03T12:35:58.817Z testcontainers INFO: Creating container for image: postgres:11.5
2020-02-03T12:36:00.850Z testcontainers INFO: Starting container with ID: 25734549ff033c92c0fb07ce168387f69d6d6e823f86e88467097dc1bf3f7707
2020-02-03T12:36:01.504Z testcontainers DEBUG: Starting container health checks
2020-02-03T12:36:01.505Z testcontainers DEBUG: Waiting for host port :39195
2020-02-03T12:36:01.508Z testcontainers DEBUG: Waiting for internal port :5432
Error: Port :39195 not bound after 60000ms
at retryStrategy.retryUntil (/codebuild/output/src627916599/src/github.com/Pepins/pepinsx/lambda-js/node_modules/testcontainers/dist/wait-strategy.js:59:23)
at IntervalRetryStrategy.<anonymous> (/codebuild/output/src627916599/src/github.com/Pepins/pepinsx/lambda-js/node_modules/testcontainers/dist/retry-strategy.js:35:28)
at Generator.next (<anonymous>)
at fulfilled (/codebuild/output/src627916599/src/github.com/Pepins/pepinsx/lambda-js/node_modules/testcontainers/dist/retry-strategy.js:4:58)
at process._tickCallback (internal/process/next_tick.js:68:7)
My code looks like this:
dbContainer = await new GenericContainer('postgres', '11.5')
.withEnv('POSTGRES_PASSWORD', 'xxxx')
.withEnv('POSTGRES_USER', 'yyyy')
.withExposedPorts(5432)
.start();
I'm using the aws/codebuild/standard:1.0
image in AWS CodeBuild.
Hi. I'm running two test containers during my integration tests using Jest. One is based on postgres and the other is for a migrations app based on a local Dockerfile. They both seem to be running correctly, but I would like to see their output (STDOUT) and it is not clear to me from the documentation how I can do this.
DEBUG=testcontainers
only outputs docker image build and container creation.
withDefaultLogDriver
has no effect.
Hi there, thanks for the great node lib!
I'm using it to run some tests with jest and getting the message:
Test suite failed to run
TypeError: pgContainer.stop is not a function
My code:
/* eslint-disable no-undef */
const { GenericContainer } = require('testcontainers');
const pgContainer = new GenericContainer('postgres').withEnv('POSTGRES_USER', 'test').withEnv('POSTGRES_PASSWORD', 'test').withExposedPorts(5432);
beforeAll(async () => {
await pgContainer.start();
});
afterAll(async () => {
await pgContainer.stop();
});
describe('Controller' tests, () => {
it('should create something', () => {
expect(1).toEqual(1);
});
});
Any ideas why is this happening?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.