0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

pytest-dockerでデータベースをfixtureする

Posted at

「データベースをDockerでfixtureするpytest環境例」として次を用意した。

こちらの記事はMySQLだったがPostgreSQLでも確認してみた。

なお、データベース接続インターフェースのライブラリーとして、

を利用したが、他のものを利用する場合もほぼ同じだと思う。

conftest.py の fixture例

pytest-dockerのサンプルでhttpbinの起動待ちしているところ、

def is_responsive(url):
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return True
    except ConnectionError:
        return False


@pytest.fixture(scope="session")
def http_service(docker_ip, docker_services):
    """Ensure that HTTP service is up and responsive."""

    # `port_for` takes a container port and returns the corresponding host port
    port = docker_services.port_for("httpbin", 80)
    url = "http://{}:{}".format(docker_ip, port)
    docker_services.wait_until_responsive(
        timeout=30.0, pause=0.1, check=lambda: is_responsive(url)
    )
    return url

これのデータベース版。アプリケーション側ではSQLAlchemy等のORMを使う場合でも直接接続ライブラリーでconnectしてみた。ORM経由だとSQLAlchemyのcreate_engine()のタイミングなのかどうなのかよくわからないし。ドライバーライブラリー経由で接続して起動確認してるってことを明示する感じで。

MySQL container の場合

MySQLコンテナーが立ち上がりconnect()に対してExceptionがでなくなるまで待つ

def is_mysqld_ready(docker_ip):
    try:
        pymysql.connect(
            host=docker_ip,
            user=os.getenv('MYSQL_USER', ''),
            password=os.getenv('MYSQL_PASSWORD', ''),
            db=os.getenv('MYSQL_DATABASE', '')
        )
        return True
    except:
        return False

@pytest.fixture(scope="session")
def database_service(docker_ip, docker_services):
    docker_services.wait_until_responsive(
        timeout=30.0, pause=0.1, check=lambda: is_mysqld_ready(docker_ip)
    )
    return

PostgreSQL container の場合

一緒ですね。

def is_postgresql_ready(docker_ip):
    try:
        psycopg2.connect(
            "postgresql://{user}:{password}@{host}/{db}".format(
                user=os.getenv('POSTGRES_USER', ''),
                password=os.getenv('POSTGRES_PASSWORD', ''),
                host=docker_ip,
                db=os.getenv('POSTGRES_DB', '')
            )
        )
        return True
    except:
        return False
@pytest.fixture(scope="session")
def database_service(docker_ip, docker_services):
    docker_services.wait_until_responsive(
        timeout=30.0, pause=0.1, check=lambda: is_postgresql_ready(docker_ip)
    )
    return

docker-compose.yml

# MySQLの場合
version: "3"

services:
  database:
    image: mysql:5.7
    ports:
      - 3306:3306
    volumes:
      - ./initdb.d:/docker-entrypoint-initdb.d
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_DATABASE=${MYSQL_DATABASE}
# PostgreSQLの場合
version: "3"

services:
  database:
    image: postgres:13
    ports:
      - 5432:5432
    volumes:
      - ./initdb.d:/docker-entrypoint-initdb.d
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}

スキーマとテストデータの準備

あとはdocker-composeでヴォリュームマウントされ初期化実行される./initdb.d:/docker-entrypoint-initdb.d./initdb.dフォルダにSQLや初期化シェルスクリプトなどを放り込んでおけばテスト可能に。

Qiitaの記事だと、Docker で MySQL 起動時にデータの初期化を行う など。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?