Docker Compose + libfaketimeでテスト用コンテナの日時を固定する

久しぶりのブログになってしまいました。
開発チームのエンジニア、片岡です。

今回は、ユニットテストにおける時間の問題についてのTipsです。

ユニットテストを行う際に手間のかかる作業として、テスト用環境の構築があります。
今はDockerがあるのでかなり手間が減りました。


ユニットテストは何度やっても同じテストができて、同じ結果になることが重要ですが、その際に問題となるのはシステム日時です。

「今日」とか「今月」などの日付・時刻によって返す結果が変わる処理があった場合にどうやって環境を設定しておけばよいか。


Docker Composeとlibfaketimeを使ってシステム日時を固定することができます。

github.com


以下、PythonのWebフレームワークであるDjangoをサンプルとして方法を紹介します。

他の言語、フレームワークでも基本は同じになります。

1. Dockerfileでlibfaketimeをインストール

Dockerfile

FROM python:3
ENV PYTHONUNBUFFERED 1

# テスト時にシステム時刻を固定化するためのlibfaketimeをインストール
WORKDIR /
RUN git clone https://github.com/wolfcw/libfaketime.git
WORKDIR /libfaketime/src
RUN make install

RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/

2. docker-compose.ymlで環境変数FAKETIMEに固定したい日時を指定

起動すると、コンテナ上の日時が固定された状態になります。

docker-compose.yml

version: '3'

services:
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: postgres
  web:
    build: .
    command: python3 manage.py runserver 0.0.0.0:8000
    environment:
# この2行で日時を設定
      - LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1
      - FAKETIME=2021-04-24 10:30:00
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

3. アプリケーション上での確認

views.py (Djangoチュートリアルより)

from django.http import HttpResponse
from datetime import datetime

def index(request):
    return HttpResponse(f"Hello, world. "
                        f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

のようにして、現在日時を表示してみると以下のようにdocker-compose.ymlで設定した日時が表示されます。(何度やっても同じ結果)

備考

後から環境変数FAKETIMEを変更することで、コンテナ的にはシステム日時を変更することができます。

# date
Sat Apr 24 10:30:00 UTC 2021
# export FAKETIME="2021-08-22 12:50:33"
# date
Sun Aug 22 12:50:33 UTC 2021

ただ、Djangoとしては再起動まで反映されません。



エンジニア募集中

TVISION INSIGHTSでは、エンジニアを大募集中です。

ビッグデータを活用した自社サービスの開発に取り組む環境があります。
Python/Django/TypeScript/Angular/Docker/AWSの経験があるエンジニアの方、是非一度下記サイトをご覧ください。

www.wantedly.com