The list of functions included in the library until now is as following:
create
: Creates a new Testcontainers instance, accepts parameters for mapped ports, environment variables and a
start commandmap-classpath-resource!
: Maps a resource from your classpath into the containers file systembind-filesystem!
: Binds a path from your local filesystem into the Docker container as a volumestart!
: Starts the containerstop!
: Stops the containercopy-file-to-container!
: Copies a file from your filesystem or classpath into the running containerexecute-command!
: Executes a command in the running container, and returns the resultThe functions accept and return a map structure, which enables us to operate them on the same data structure in a consistent way. The example shown with Java Interop above would look like this, when using the wrapped functions:
(require '[clj-test-containers.core :as tc])
(deftest db-integration-test
(testing "A simple PostgreSQL integration test"
(let [pw "db-pass"
postgres (-> (tc/create {:image-name "postgres:12.1"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" pw}}))]
(tc/start! postgres)
(let [datasource (jdbc/get-datasource {:dbtype "postgresql"
:dbname "postgres"
:user "postgres"
:password pw
:host (:host postgres)
:port (get (:mapped-ports container) 5432)})]
(is (= [{:one 1 :two 2}] (with-open [connection (jdbc/get-connection datasource)]
(jdbc/execute! connection ["SELECT 1 ONE, 2 TWO"])))))
(tc/stop! postgres))))
The execute-command
function enables us to run commands inside the container. The function accepts a container and a
vector of strings as parameters, with the first string being the command, followed by potential parameters. The function
returns a map with an :exit-code
, :stdout
and :stderr
:
(execute-command! container ["whoami"])
> {:exit-code 0
:stdout "root"}
For some test scenarios it can be helpful to mount files from your filesystem or the resource path of your application
into the container, before it is started. This could be helpful if you want to load a dumpfile into your database,
before executing the tests. You can do this with the functions map-classpath-resource!
and bind-filesystem!
:
(map-classpath-resource! container
{:resource-path "test.sql"
:container-path "/opt/test.sql"
:mode :read-only})
(bind-filesystem! {:host-path "."
:container-path "/opt"
:mode :read-only})
It is also possible to copy files into a running container instance:
(copy-file-to-container! {:path "test.sql"
:container-path "/opt/test.sql"
:type :host-path})
The above example creates a Testcontainers instance in the test function itself. If we did this for all of our
integration tests, this would spin up a docker image for every test function, and tear it down again, afterwards. If we
want to create one image for all tests in the same namespace, we can use
Clojures use-fixtures
function, which is described like this:
Wrap test runs in a fixture function to perform setup and teardown. Using a fixture-type of :each wraps every test individually, while :once wraps the whole run in a single function.
Assuming we have a function initialize-db!
in our application which sets up a JDBC connection and stores it in an
atom, a fixture for Testcontainers could look like this:
(use-fixtures :once (fn [f]
(let [{pw "apassword"
postgres (tc/start! (tc/create {:image-name "postgres:12.2"
:exposed-ports [5432]
:env-vars {"POSTGRES_PASSWORD" pw}}))}]
(my-app/initialize-db! {:dbtype "postgresql"
:dbname "postgres"
:user "postgres"
:password pw
:host (:host postgres)
:port (get (:mapped-ports postgres) 5432)}))
(f)
(tc/stop! postgres)))
This will set up the container, execute all test functions in the namespace and stop the container afterwards.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close