• Stars
    star
    660
  • Rank 68,297 (Top 2 %)
  • Language
    Java
  • License
    Apache License 2.0
  • Created about 10 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Java embedded PostgreSQL component for testing

OpenTable Embedded PostgreSQL Component

Note: This library requires Java 8+.

Allows embedding PostgreSQL into Java application code, using Docker containers. Excellent for allowing you to unit test with a "real" Postgres without requiring end users to install and set up a database cluster.

Recent Changes

The release of 1.0 brings major changes to the innards of this library. Previous pre 1.x versions used an embedded tarball. This was extremely fast (a major plus), but we switched to a docker based version for these reasons:

  • Advantages:
    • multi architecture support. This has become a huge issue for us with the introduction of the Mac M1 (and Windows ARM, Linux ARM)/
    • The same container works the same way on every OS - Mac, Windows, Linux.
    • You need a tarball for every linux distribution as PG 10+ no longer ship a "universal binary" for linux. This means a lot of support and maintenance work.
    • Easy to switch docker image tag to upgrade versions - no need for a whole new pg-embedded version.
    • More maintainable and secure (you can pull docker images you trust, instead of trusting our tarballs running in your security context)
    • Trivial to do a build oneself based on the official Postgres image adding extensions, setup scripts etc. - see https://github.com/docker-library/docs/blob/master/postgres/README.md for details.
  • Admittedly, a few disadvantages
    • Slower than running a tarball (2-5x slower).
    • A few API compatibility changes and options have probably disappeared. Feel free to submit PRs.
    • Docker in Docker can be dodgy to get running. (See below for one thing we discovered)

Before filing tickets.

  1. Before filing tickets, please test your docker environment etc. If using podman or lima instead of "true docker", state so, and realize that the docker socket api provided by these apps is not 100% compatible, as we've found to our sadness. We'll be revisiting testing these in the future. We've managed to get PodMan working, albeit not 100% reliably.
  2. No further PRs or tickets will be accepted for the pre 1.0.0 release, unless community support arises for the legacy branch. Please base any PRs for pre 1.x against the legacy branch.
  3. We primarily use Macs and Ubuntu Linux at OpenTable. We'll be happy to try to help out otherwise, but other platforms, such as Windows depend primarily on community support. We simply don't have the time or hardware. Happy to merge PRs though

See "Alternatives Considered" as well if this library doesn't appear to fit your needs.

Basic Usage

In your JUnit test just add (for JUnit 5 example see Using JUnit5 below):

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();

This simply has JUnit manage an instance of EmbeddedPostgres (start, stop). You can then use this to get a DataSource with: pg.getEmbeddedPostgres().getPostgresDatabase();

Additionally, you may use the EmbeddedPostgres class directly by manually starting and stopping the instance; see EmbeddedPostgresTest for an example.

Default username/password is: postgres/postgres and the default database is 'postgres'

The port exposed on the host is random and ephemeral so always use the getDatasource, getUrl methods

Sample of Embedded Postgres direct Usage

public void testDatabaseName() throws IOException,SQLException{
        EmbeddedPostgres db=EmbeddedPostgres.builder().start();
        Datasource dataSource = db.getPostgresDatabase();
        .... use the datasource then ...
        db.close();
        }

The builder includes options to set the image, the tag, the database name, and various configuration options.

Migrators (Flyway or Liquibase)

You can easily integrate Flyway or Liquibase database schema migration:

Flyway
@Rule 
public PreparedDbRule db =
    EmbeddedPostgresRules.preparedDatabase(
        FlywayPreparer.forClasspathLocation("db/my-db-schema"));
Liquibase
@Rule
public PreparedDbRule db = 
    EmbeddedPostgresRules.preparedDatabase(
            LiquibasePreparer.forClasspathLocation("liqui/master.xml"));

This will create an independent database for every test with the given schema loaded from the classpath. Database templates are used so the time cost is relatively small, given the superior isolation truly independent databases gives you.

Postgres version

The default is to use the docker hub registry and pull a tag, hardcoded in EmbeddedPostgres. Currently, this is "13-latest", as this fits the needs of OpenTable, however you can change this easily. This is super useful, both to use a newer version of Postgres, or to build your own DockerFile with additional extensions.

You may change this either by environmental variables or by explicit builder usage

Environmental Variables

  1. If PG_FULL_IMAGE is set, then this will be used and is assumed to include the full docker image name. So for example this might be set to docker.otenv.com/postgres:mytag
  2. Otherwise, if TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX is set, this is prefixed to "postgres" (adding a slash if it doesn't exist). So for example this might be set to "docker.otenv.com/"
  3. Otherwise, the default is used as defined above.

Explicit builder

It is possible to change postgres image and tag in the builder:

    EmbeddedPostgres.builder()
        .setTag("10")
        .start();

or use custom image:

    EmbeddedPostgres.builder()
        .setImage(DockerImageName.parse("docker.otenv.com/super-postgres"))
        .start();

There are also options to set the initDB configuration parameters, or other functional params, the bind mounts, and the network.

Using JUnit5

JUnit5 does not have @Rule. So below is an example for how to create tests using JUnit5 and embedded postgres, it creates a Spring context and uses JDBI:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = DaoTestUsingJunit5.MockDBConfiguration.class)
class DaoTestUsingJunit5 {
    interface MyDao {}

    @Inject
    MyDao myDao;

    @Test
    void someTest() {
        // ....
    }

    @Import(DaoTestUsingJunit5.GlobalMockDBConfiguration.class)
    @Configuration
    static class MockDBConfiguration {
        @Bean
        public MyDao dao(Jdbi jdbi) {
            return jdbi.onDemand(MyDao.class);
        }
    }

    /**
     * This class is here as inner class for brevity,
     * but it's better to have only one for all tests.
     */
    @Configuration
    public static class GlobalMockDBConfiguration {
        @Bean("jdbiUser")
        @Primary
        Jdbi jdbi() throws SQLException {
            DatabasePreparer db = FlywayPreparer.forClasspathLocation("db/migration");

            Jdbi jdbi = Jdbi.create(PreparedDbProvider.forPreparer(db).createDataSource())
                    .installPlugin(new PostgresPlugin())
                    .installPlugin(new SqlObjectPlugin())
                    .setTransactionHandler(new SerializableTransactionRunner());

            return configureJdbi(jdbi);
        }

        static Jdbi configureJdbi(Jdbi jdbi) {
            // possible actions:
            // - register immutables
            // - set up mappers, etc
            return jdbi;
        }
    }
}

Yes, Junit4 is a compile time dependency

This is because TestContainers has a long outstanding bug to remove this -testcontainers/testcontainers-java#970 If you exclude Junit4, you get nasty NoClassDefFound errors.

If you only use Junit5 in your classpath, and bringing in Junit4 bothers you (it does us, sigh), then you can do the following:

  • add maven exclusions to the testcontainers modules you declare dependencies on to strip out junit:junit. This by itself would still lead to NoClassDefFound errors.
  • add a dependency on io.quarkus:quarkus-junit4-mock , which imports empty interfaces of the required classes. This is a hack and a cheat, but what can you do?

We initially excluded junit4 ourselves, which led to confusing breakages for junit5 users...

Some new options and some lost from Pre 1.0

  • You can't wire to a local postgres, since that concept doesn't make sense here. So that's gone.
  • You can add bind mounts and a Network (between two containers), since those are docker concepts, and can be very useful.
  • By the way, TestContainers does support ~/.docker/config.json for setting authenticated access to Docker, but we've not tested it.

Docker in Docker, authentication notes

We've been able to get this working in our CICD pipeline with the following

TESTCONTAINERS_HOST_OVERRIDE=localhost
TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX=dockerhub.otenv.com/

The first parameter corrects for testcontainers getting confused whether to address the hosting container or the "container inside the container". The second parameter (which outside OpenTable would point to your private Docker Registry) avoids much of the Docker Rate Limiting issues.

See testcontainers/testcontainers-java#4596 for more information

Alternatives considered

We updated this library primarily for convenience of current users to allow them to make a reasonably smooth transition to a Docker based test approach.

  • Why not just use Testcontainers directly?

You can, and it should work well for you. The builders, the api compatibility, the wrapping around Flyway - that's the added value. But certainly there's no real reason you can't use TestContainers directly - they have their own Junit4 and Junit5 Rules/Extensions.

  • Why not use a maven plugin approach like fabric8-docker-maven?

Honestly I suspect this is a better approach in that it doesn't try to maintain its own version of the Docker API, and runs outside the tests, reducing issues like forking and threading conflicts. However, it would have been too major an overhaul for our users.

  • "I really prefer the old embedded postgres approach. It's faster."

    We recommend those who prefer the embedded tarball use https://github.com/zonkyio/embedded-postgres which was forked a couple of years ago from the embedded branch and is kept reasonably up to date.

    Another alternative is Flapdoodle's embedded postgres, but that is deprecated in favor of testcontainers too.

    Both libraries suffer from many of the cons that bedeviled upkeep of this library for years, but they are certainly viable options for many.


Copyright (C) 2017-2022 OpenTable, Inc

More Repositories

1

accept-language-parser

It parses the accept-language header from an HTTP request
JavaScript
151
star
2

hobknob

Hobknob is a feature toggle front-end built on top of etcd. It allows users to create and modify feature toggles, which can then be accessed in your applications.
JavaScript
96
star
3

design-tokens

A place where OpenTable engineers and designers openly work together
JavaScript
80
star
4

wiremock-body-transformer

Wiremock Body Transformer Extension
Java
66
star
5

graphql-query-generator

Generates queries from the GraphQL endpoint via schema introspection.
JavaScript
48
star
6

star-printing

Star print queue and persistence for iOS and Mac
Objective-C
39
star
7

spur-ioc

Dependency Injection library for Node.js
JavaScript
26
star
8

HLClock

Hybrid Logical Clocks for iOS
Swift
22
star
9

mercury

Mercury is a bot for handling in-code static translations.
JavaScript
18
star
10

image-optimizer

Smart image optimization
JavaScript
15
star
11

falcor-postman

A graphical interactive in-browser IDE to validate Falcor queries.
JavaScript
14
star
12

hapi-shutdown

handle SIGTERM and register triggers to be run before the server stops
JavaScript
8
star
13

mesos_stats

Mesos -> Graphite
Python
7
star
14

logstash-redis

Uber simple Logstash logging using Redis
JavaScript
6
star
15

eslint-config-opentable

Extends eslint-config-airbnb-base with OpenTable's internal preferences.
JavaScript
6
star
16

hobknob-client-nodejs

A client to get feature toggle values by convention from an etcd store
JavaScript
5
star
17

hapi-accept-language

plugin to wrap accept-language-parser
JavaScript
4
star
18

otj-kafka

Kafka integration library
Java
4
star
19

ot-wpt

Initiates a run on a given webpagetest instance and logs to hipchat, statsd, and logstash.
JavaScript
4
star
20

grunt-ot-webpagetest

Grunt module to run against WebPageTest API and fire events into hipchat
JavaScript
4
star
21

menu-code-test-react

JavaScript
4
star
22

otj-logging

OpenTable Java logging configuration component
Java
4
star
23

optimizely-toggle-chrome

This extension disables Optimizely for the tab you are browsing, via a switch.
JavaScript
4
star
24

otj-jvm

JVM inspection and debugging utilities
Java
4
star
25

otj-pausedetector

Java JVM pause detector library
Java
3
star
26

spur-web

Common node modules and express middleware that are designed to be the boilerplate of a node web app. Extends the 'spur-common' NPM module.
JavaScript
3
star
27

spur-mongoosemanager

Mongoose common extensions
CoffeeScript
3
star
28

otj-core

OpenTable core Java types library
Java
3
star
29

spur-config

Configuration framework to help with large amount of configurations.
JavaScript
3
star
30

ot-oc-template-react-email

βš›οΈ πŸ‘‰ βœ‰οΈ Module for handling React to write emails with OC
JavaScript
3
star
31

spur-mockserver

HTTP mock server library
JavaScript
3
star
32

menu-code-test

3
star
33

spur-errors

Common error builder utility for Node.js. Contains common error types, and stack trace tracking to support more detailed error messages.
JavaScript
3
star
34

hobknob-client-net

A .net client library for Hobknob
C#
3
star
35

go-singularity

Singularity API client
Go
3
star
36

uk-tech-blog

Technology and software engineering blog for the OpenTable London office
JavaScript
3
star
37

hvm

Host file manager
JavaScript
2
star
38

grunt-mount

grunt task to mount a network share
JavaScript
2
star
39

homebrew-public

Public OpenTable brew.sh formulae `brew tap opentable/public`
Ruby
2
star
40

grunt-gherkin-report

A grunt task that reads a folder of Cucumber/SpecFlow feature files and generates an html report
JavaScript
2
star
41

spur-common

Common node library that is implemented through the use of spur-ioc and bluebird promises.
JavaScript
2
star
42

i18n-transform

apply i18n transforms to a json object
JavaScript
2
star
43

grunt-git-changelog-generator

Generate markdown changelog from git log messages and tags
JavaScript
2
star
44

optimizely-toggle-firefox

This extension disables Optimizely for the tab you are browsing, via a switch.
JavaScript
2
star
45

hapi-service-status

service-status endpoint for hapi.js
JavaScript
2
star
46

hapi-heartbeat

JavaScript
2
star
47

designplayground

A place where designers are free to play with git.
1
star
48

i18n-merge

Merge together objects according to their languages
JavaScript
1
star
49

injecorator

A simple decorator based dependecy injection framework for nodejs es6 projects
JavaScript
1
star
50

otj-scopes

OpenTable thread scopes component
Java
1
star
51

ot-availability-phrases

nodejs code for generating availability-phrases
JavaScript
1
star
52

hapi-hobknob-client

A Hobknob client plugin for Hapi
JavaScript
1
star
53

AndroidCodingChallenge

Kotlin
1
star
54

grunt-xbuild

xbuild runner for grunt
JavaScript
1
star
55

decorouter

Simple decorator based es6 class method routing for express nodejs
JavaScript
1
star
56

sensucronic

wrapper script to integrate cronjobs with sensu client input socket
Ruby
1
star
57

otj-jackson

OpenTable Jackson DI integration
Java
1
star
58

otkit-ios

OTKit for iOS: frameworks and apps to exercise them
Swift
1
star
59

hapi-mongodb-init

plugin for initialising shared mongodb connections in a hapi app
JavaScript
1
star
60

hapi-version-prereq

detects version info in the url/headers and populates the prereq object
JavaScript
1
star
61

hapi-methods

helper for registering cached server methods
JavaScript
1
star
62

grunt-nagios

Nagios grunt module to interact with cmd.cgi interface
JavaScript
1
star
63

otj-server

OpenTable Java server template component
Java
1
star
64

hapi-domainculture

parse query params OR header for fetching the domain and culture
JavaScript
1
star
65

grunt-get-last-pinned

Get the last-pinned build number from a teamcity build
JavaScript
1
star