LoginSignup
22
11

More than 1 year has passed since last update.

Firebase Local Emulator SuiteをDocker環境で構築して、アプリケーションからテストを実行してみた

Last updated at Posted at 2022-07-06

はじめに

FirebaseをローカルでテストできるFirebase Local Emulator SuiteをDocker環境で構築、アプリケーション(Go)から使用する機会がありました。
本記事では、Dockerでの構築方法及び、アプリケーション側の設定について記載していきます。
※今回は、Firestoreでの利用となります。

環境

  • macOS Monterey 12.4
  • docker desktop 4.7.0
  • Go 1.18.3

構築

プロジェクト構成は以下の前提で進めていきます。
firebase設定ファイルすでにある事を想定し、実際の環境とも併用することを考慮して、使いまわします。

root/
 ├ docker/
 │ ├ firebase/
 │ │  ├ bin/
 │ │  ├ config/
 │ └ └ Dockerfile
 ├ src/
 ├ .firebaserc
 ├ firebase.json
 ├ firestore.indexes.json
 ├ firestore.rules
 └ docker-compose.yml

エミュレータを使用するために、firebase.jsonに以下の設定を追加(ポート番号は状況に応じて変更)

firebase.json
  "emulators": {
    "auth": {
      "host": "0.0.0.0",
      "port": 9099
    },
    "functions": {
      "host": "0.0.0.0",
      "port": 5001
    },
    "firestore": {
      "host": "0.0.0.0",
      "port": 8080
    },
    "database": {
      "host": "0.0.0.0",
      "port": 9000
    },
    "hosting": {
      "host": "0.0.0.0",
      "port": 5050
    },
    "pubsub": {
      "host": "0.0.0.0",
      "port": 8085
    },
    "storage": {
      "host": "0.0.0.0",
      "port": 9199
    },
    "ui": {
      "enabled": true,
      "host": "0.0.0.0",
      "port": 4000
    }
  }

Dockerの設定

Emulatorを使用するために、

  • Node.js バージョン 8.0 以降。
  • Java JDK バージョン 11 以降。
  • Firebase CLI
    が必要なため、各種インストール。
FROM ubuntu:22.04

RUN apt update && \
apt install -y curl openjdk-17-jdk && \
curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \
apt install -y nodejs

RUN npm install -g firebase-tools

firebaseを使用するためのアプリケーション及び上記のDockerfileを指定したcomposeファイルを作成します。
ポートは同様のものを指定し、docker立ち上げ時に自動でエミュレータが起動するようにfirebase emulators:startを指定しておきます。

docker-compose.yml
version: '3.9'

services:
  app:
    build:
      context: .
      dockerfile: ./docker/app/Dockerfile
    env_file: .env
    ports:
      - "3000:8080"
    volumes:
      - ./src/:/go/src
    tty: true
  firebase:
    build:
      context: .
      dockerfile: ./docker/firebase/Dockerfile
    volumes:
      - ./.firebaserc:/opt/firebase/.firebaserc
      - ./firebase.json:/opt/firebase/firebase.json
      - ./firestore.indexes.json:/opt/firebase/firestore.indexes.json
      - ./firestore.rules:/opt/firebase/firestore.rules
      - ./docker/firebase/bin/:/root/.cache:cached
      - ./docker/firebase/config/:/root/.config:cached
    ports:
      - 9099:9099 # Firebase Authentication
      - 5001:5001 # Clound Functions
      - 8080:8080 # Cloud Firestore
      - 9000:9000 # Realtime Database
      - 5050:5050 # Firebase Hosting
      - 8085:8085 # Cloud Pub/Sub
      - 9199:9199 # Cloud Storage
      - 4000:4000 # Emulator Suite UI
    working_dir: /opt/firebase
    command: firebase emulators:start
    tty: true

初期設定

初回実行時は以下の初期設定を行って、firebaseの設定を行う。

  1. firebaseログイン
  2. firebase初期化

1.firebaseログイン

docker compose run --rm firebase firebase login --no-localhost
Docker内で、firebase login --no-localhostを実行してログインする。
(実際のfirebaseプロジェクトが作成済であることが前提、権限があるアカウントにてログイン)

2.firebase初期化

docker compose run --rm firebase firebase init
Docker内で、firebase initを実行して初期化する。
対話形式で、設定を聞かれるので、Emulatorを選択、port番号は設定済みで、最後にエミュレータをダウンロードするか聞かれるので、インストール。
binフォルダ配下にエミュレータがインストールされる

起動

初期設定後、docker compose up等でコンテナ起動するとlocalhost:4000でエミュレータ画面にアクセスできるようになります。
localhost_4000_ (1).png

アプリケーションから接続

エミュレータが起動したので、簡単なテストを実装しました。

Firebase Admin SDKを利用していますので、詳細はそちらをご覧いただければと思います。

ポイントとしては以下2点

  • エミュレータにデータクリアの機能が備わっているため、テストごとTearDownを行うことで、クリーンな状態で各種テストが実行が可能
  • 環境変数FIRESTORE_EMULATOR_HOSTの設定有無によって、エミュレータ接続するかどうかが決まるため、プロダクションコードに大きな変更を加えることなく、テスト可能
sample.go
import (
	"context"
	"fmt"
	"os"

	"cloud.google.com/go/firestore"
	"google.golang.org/api/option"
)

func getFirestore(ctx context.Context) (*firestore.Client, error) {
	sa := option.WithCredentialsFile(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"))
	projectId := os.Getenv("PROJECT_ID")
	client, err := firestore.NewClient(ctx, projectId, sa)
	if err != nil {
		fmt.Println(err)
	}
	return client, err
}

func GetDocument(c, docId string) (*firestore.DocumentSnapshot, error) {
	ctx := context.Background()
	client, err := getFirestore(ctx)

	// コレクション検索
	snapshot, err := client.Collection(c).Doc(docId).Get(ctx)
	if err != nil {
		fmt.Println(err)
	}
	return snapshot, err
}

func UpsertDocument(c, docId string, data interface{}) error {
	ctx := context.Background()
	client, err := getFirestore(ctx)
	if err != nil {
		return err
	}

	_, err = client.Collection(c).Doc(docId).Set(ctx, data)
	if err != nil {
		return err
	}

	return nil
}

sample_test.go
import (
	"fmt"
	"net/http"
	"os"
	"testing"

	"github.com/mobility-api/middleware"
	"github.com/mobility-api/model"
	"github.com/stretchr/testify/suite"
)

type Companies struct {
	suite.Suite
}

func (suite *Companies) SetupTest() {
	//初期データ投入
	for i, v := range []string{"sample1", "sample2", "sample3"} {
		i++
		middleware.UpsertDocument("companies", v, map[string]interface{}{"name": fmt.Sprintf("name%d", i)})
	}
}

func (suite *Companies) TearDownTest() {
	//全データ削除
	url := fmt.Sprintf("http://%s/emulator/v1/projects/%s/databases/(default)/documents", os.Getenv("FIRESTORE_EMULATOR_HOST"), os.Getenv("PROJECT_ID"))
	req, _ := http.NewRequest(http.MethodDelete, url, nil)
	client := http.Client{}
	client.Do(req)
}

func TestCompanies(t *testing.T) {
	os.Setenv("FIRESTORE_EMULATOR_HOST", "localhost:8080")
	os.Setenv("PROJECT_ID", "sample-project")
	suite.Run(t, &Companies{})
}

func (suite *Companies) TestCompanies_GetCompany() {
	docID := "sample1"
	actual, err := middleware.GetDocument("companies", docID)
	suite.Nil(err)
	suite.Equal(&model.Company{
		DocId: docID,
		Name:  "name1",
	}, actual)
}

まとめ

Firebaseエミュレータを使用して、起動・アプリケーションからのテストまで作成しました。
Firestore以外にも各種エミュレータを使用できるため、ローカルでの動作確認には、非常に役に立つのではないかと思いました。

22
11
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
22
11