Azure DevOpsのpipelinesを使ってSpring+MySQLのwebアプリをテストする

Azure DevOpsのpipelinesを使ってSpring + MySQLのwebアプリケーションを自動テストする方法を紹介します。

CI/CDのためのサービスは色々ありますが、Azure DevOpsはプロジェクトのバックログやダッシュボードを管理できるAzure BoardsやGitのホスティングサービスであるAzure Reposなどプロジェクト遂行に役立ツールがセットになっていて、テスト、デプロイを含め開発に関わるもろもろのことを一括して管理することができます。

逆にいうと、Azure DevOpsを使っているのならpipelinesを使わにゃ損ということになるんじゃないでしょうか。

とうことで、Spring bootのwebアプリケーションをAzure DevOpsのpipelinesで自動テストする方法をまとめようと思います。

※新人研修の資料として作成しています。なので、文章はwebアプリケーションの開発を始めた方が初めてコードの自動テスト化に挑戦する体で書きました。

一応、Azure-pipeline.yml除くコードはこちらのgithubのレポジトリありますが、springのアプリーケーションは自分がwebアプリケーションが何か知らない時に、初めてSpringを勉強したときに試しに書いたコードなので見ないでください

https://github.com/Pickerdot/github_actions_spring_mysql.git

# まず、CI（継続的インテグレーション）とは何か？
一応、CI（継続的インテグレーション）とはなんぞやという話を乗せておきます（わかるよという方は読み飛ばしてください）。

Continuous Integrationの略でCI/CDのCIです。何をintegrate（統合）するのかと言うと、それは個人が書いているコードになります。なのでCIとは、おのおのが書いているコードを小まめにまとめていこうという考えになります。

そのコードをまとめて管理するための仕組みがgitでその保存先がgitのホスティングサービスであるgithubや今回使用するAzure Reposになります（Azure pipelinesはgithubでも使用できます）。

さて、コードを集める（pushする）際に自分の書いたコードにバグがあると、チームで開発しているアプロケーションにバグが混入してしまうことになります。それを防ぐため、Azure Reposに編集したコードが送られる前にそのコードが本当に正しく動いているか自動でテストを行うわけです。

世の中でCIというと、自動テストを実行するまでの流れ、具体的にはコードの変更をコミットしてプッシュしてその変更がテストされるまでのことを言っていると指していると思います。

別に、CIツールを使って自動テストしなくても、githubを使って個人のコードをまとめて保存しておくだろうし、プログラムが完成したらテストコードを書いてテストを行いますよね・・・。

ただ、テストが一旦通っても他の箇所のバグ修正や機能の追加でバグは発生し続けます。そして、その後にテストをし直さないことが多いです。そうなると、あとでまとめてテストを実行したときには大量のバグが発生して直しようにも直せないという事態になってしまいます。

だったら、毎回リモートレポジトリにpushする際に一度テストを通しておいて、うまくいってない箇所が見つかったらその場で直してしまおうというのがCIの考え方です。

# DockerコンテナにSpringアプリケーションとMySQLを乗せてテストを実行可能に

今回は、pipelinesで自動テストを行うにあたってDockerを使います。

Dockerなんて知らん！という方もいると思いますが、今回はDocker自体の説明はあえてせずに手順だけ書いていきます。

Dockerを使わずともpipelinesは使えますが、pipelinesの設定項目はほとんどDockerとおなじで、docker composeという複数のDockerコンテナを管理できるツールを使用すれば、ローカルでも、pipelinesでも、本番環境でもコマンド一つで開発中のアプリケーションを実行できるので、ついでにDockerの設定ファイル（DockerfileとDocker0compose.yml）を書いてしまったほうがお得です。

一度、Docker上で動くように設定してしまえば、pipelinesでテストを実行するのは簡単です。他のCIツールを試すこともできます。

ということで、ここからの話はほとんどspringのアプリケーションをどうやってDockerで動かすかという内容になります。

## 前提として必要なもの

- 何かしらテストが動くSpringのwebアプリケーション
- MySQLの初期化sql
- Gitクライアント

あるといいもの

- Dockerが動く環境
※現在の内容だとDockerがないとローカルで実行できなくなるのでやっぱり必須です（対策は後日記述しようと思います）

## SpringとMySQLをDockerコンテナに載せるとためにDockerfileを書く


SpringとMySQLをそれぞれ別のコンテナに載せます。コンテナってなに？という方はSpringとMySQLを実行するための専用の入れ物（実行環境）と思ってください。

### まずは、MySQLを動かすコンテナを作成

空のフォルダ上でmysqlというフォルダを作成します。そして、その中にsqlというファルダとDockerfileというファイル（拡張子はいりません）、my.cnfというファイルを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412418/10cf1d68-41c4-0e90-2e17-1f39eb8d1587.png)

こんな感じ。

そして、sqlのフォルダにはdbの初期化のためのsqlを入れておきます。

Dockerfileの内容は

```dockerfile
# 使用するコンテナのイメージ
FROM mysql:5.7

#MySQL設定ファイルをイメージ内にコピー
ADD ./my.cnf /etc/mysql/conf.d/my.cnf
```

コンテナの設定を記載するDockerfileはこんな感じでめちゃめちゃシンプルです。

2行目で`mysql:5.7`というイメージを指定していますが、このコンテナのイメージ（コンテナの種類）にすでにmysqlを動かすために必要なあれこれが詰められているので、こちらで新しく指定するものは後述するmy.cnfをコンテナ内のmysqlの設定フォルダにコピーしてくださいという命令だけでOKになります。

で、そのmy.cnfの内容は

```conf
[mysqld]
# 一応文字コードの指定
character-set-server=utf8
# windowsで開発していた方は1をMac、Linuxで開発していた方は0を指定
lower_case_table_names = 1

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8
```

こちらの設定もシンプルで、文字コードをの指定と大文字小文字の区別についての設定だけです（一応書いているだけでもしかすると不要かもしれないです）。

一つ気を付けてほしいのはWindowsでmysqlを動かしていて、sqlの大文字小文字の区別をつけずにクエリを書いていた人は5行目の`lower_case_table_names = 1`が必須になります。

逆にそれ以外の環境の方は`lower_case_table_names = 0`にするか記述を消してください。

コンテナ内でMySQLはLinuxで動いていて、Linuxだと大文字小文字の区別をつけるのがデフォルトなのでsqlが読めなくなります。

ここは、Windowsでwebアプリを開発していてLinuxのサーバーにデプロイする人が一番引っかかりやすいポイントかなと思います。

これで、MySQLをコンテナで動かす準備ができました。

### 次にSpringアプリケーションを動かすコンテナを作る

まずはmysqlというフォルダを作成したフォルダにspringというフォルダを作成します。

そしたらspringアプリケーションの大本のフォルダ（srcやgbuild.radleなどがある）の中身をすべて移します。

さらに、そのフォルダにDockerfileとrun.shというファイルを追加してください。

Dockerfileには下のように記述

```dockerfile
# Javaの実行環境Java8を使っている人はFROM openjdk:8に
FROM openjdk:11

# コンテナ内の操作フォルダを指定
WORKDIR /app

# コンテナ内にソースコードをコピー
COPY . . 

# 起動スクリプトを実行
ENTRYPOINT ["/bin/bash", "run.sh"]  
```

イメージはJavaのバージョンがJava11の人はopenjdk:11を、Java8の人はopenjdk:8を使用してください。

テストコードの実行はこれから記述するrun.shに実行コマンドを記述して実行します。

run.shの内容はというと、

```shell
chmod +x gradlew
./gradlew wrap
./gradlew test
```
こんなんです。

1行目と2行目はgradlewを実行する際にエラーを防ぐために入れてあります（一度でもローカルでgradlewを使用してwebアプリケーションを立ち上げていれば不要です）。

3行目の`./gradlew test`でテストが実行されます。

最後にもう一点変更点があります。

Spring側のmysqlのパスをlocalhostからmysqlに変更します。通常はapplication.propertiesで一括で変更できるようになっていますが、個別に変えてある場合はそこもいじってください。

```
spring.datasource.url=jdbc:mysql://mysql:3306/spring
```

これでSpringを立ち上げるコンテナの設定も完了です。

## docker composeで2つのコンテナを立ち上げる

docker-compose.ymlに二つのコンテナを立ち上げるための設定をかけば、MySQLとSpringアプリケーションを同時に起動してテストを実行することができます。

docker-compose.ymlの内容は

```
version: "3"
services:
  mysql:    # MySQLを起動するコンテナ
    build: ./mysql/    #Dockerfileからビルドすることを示す
    volumes:
      - ./mysql/sql:/docker-entrypoint-initdb.d  #初期データをマウントする場所
    environment:
      - MYSQL_ROOT_PASSWORD=*****   #コンテナ内のMySQLを起動する際のrootのパスワードを設定
      - MYSQL_USER=****   # springが利用するユーザー名
      - MYSQL_PASSWORD=****   # そのユーザーのパスワード
    ports: 
      - "3306:3306"   #portの設定
  spring:    # Springアプリケーションを起動するコンテナ
    build: ./spring/
    ports:
      - "8080:8080"   #portの設定
    depends_on:
      - mysql
```

となります。portの部分はローカルに反映されるportを表していますが、コンテナどおしはサービス名で通信をするので、Pipelinesを実行するには不要です。

ただ、これを設定しておくことで`docker-compose.yml up --build`もしくは`docker compose.yml up --build`コンテナをローカルで起動することができます。

6行目の`- ./mysql/sql:/docker-entrypoint-initdb.d`は初期化sqlをdocker-entrypoint-initdb.dというディレクトリコピーして起動時に読み込ませるための記述です。

注意するところは、

- MYSQL_ROOT_PASSWORD
- MYSQL_USER
- MYSQL_PASSWORD

Sringpのmysqlに接続できるようにユーザーの設定を書くところです。

設定項目の詳細はこちらにまとまっていました。

https://qiita.com/nanakenashi/items/180941699dc7ba9d0922

# Azure reposにコードをアップロードし、パイプラインを設定する

いよいよ本題です。

Azure DevOpsのプロジェクトを作成し、Azure reposにソースコードをpushした後に、pipelinesでテストします。

下のリンクからAzure DevOpsのプロジェクトを作成します（無料です）。

プロジェクトを作成するとプロジェクトのダッシュボードが表示されます。Reposを選択するとリポジトリのURLが表示されます。

このリポジトリにコードをアップロードする方法ですが、お使いのgitのクライアントソフトウェアによって違うので調べてください。

Sorcetreeでの方法は次のサイトにまとまってました。

https://qiita.com/tajima440/items/336a730c3c7d29429b39

そしたら、ついにパイプラインの設定です。[Pipelines]を選択すると下の画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412418/71c56d71-5b34-7004-be74-30bedf0c0658.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412418/918cb68a-cbb3-6e93-7b8d-3cfa52194adf.png)

[Create Pipeline]を選んで、次の画面で[Azure Repos Git]を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412418/434cce47-5c20-c83a-7d3a-ba360e643c7f.png)

CIするレポジトリを選んで

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412418/3a4e9429-92fe-bdcd-89a0-275733877f7a.png)

とりあえず、Dockerを選びます（全部書き換えるのでどれを選んでも大丈夫です）。Dockerfileを選択するように言われますが、無視して[Validate and configure]を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412418/2c706631-cb28-61b6-bbd2-92dc2ca56d1c.png)

すると、pipelineの設定ファイルになるazure-pipelines.ymlの編集画面が出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412418/0893d1e4-d803-3005-a132-3253792b7451.png)

ここにしたの内容をコピペしてください。

```yaml
trigger:
- master

pool:
  vmImage: ubuntu-latest

steps:
- script: docker-compose up --build --abort-on-container-exit # springのコンテナが停止したらmysqlのコンテナも自動停止させてworkflow停止
  displayName: 'docker build'
```

docker-compose upをするだけの設定です。オプションが付いているのはSpringのテストが実行されたのちに自動でmysqlのコンテナを立ち下げるための設定になります。

`trigger:`ではテストが実行されるブランチを指定します。とりあえずmasterだけにしておきました。

最後に、[Sava and Run]を実行して実行後に、このようにstatusがsaccessになっていたら成功です（当然ですがテストが通らないと失敗します）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412418/3d2a3513-ca85-247c-c452-a2628f3c17bd.png)

# 最後に

以上がAzure DevOpsのPipelinesを使ったCIの手順です。

最後に書いてから気づいたのですが、この手順をふむとSpringのapplication.propertiesの設定を書き開けるため、Dockerを使わないとローカルでアプリケーションを実行できなくなります。

対策としては、環境変数でmysqlのパスを渡すかDockerを利用するかのどちらかですが、ですかその方法に関する記事もいずれ書いておこうと思います（それかちょうど良さそうなページのリンクを載せるか）。

# 参考にさせていただいたサイト

https://qiita.com/sayama0402/items/e863ffb597ce87bf2e2f

https://qiita.com/shonansurvivors/items/cb8902acfe5c3a1b3ca0

https://docs.github.com/ja