インフラエンジニアのためのCodeBuild,CodePipelineで覚えるユニットテスト

先日インフラエンジニアのためのCodeCommitで覚えるユニットテストを公開し、ローカルでユニットテストをして、CodeCommitにプルリクエストするまでの流れをご紹介しました。

今回はCodeBuildとCodePipelineを使って、ユニットテストを自動実行する流れを紹介します。 devブランチへのpushを検知して、ユニットテストを実行します。

手動でユニットテストを実行

自動で実行する前に、手動でユニットテストを実行できるようにします。 足し算、引き算が出来るcalc.pyとテストファイル(test_calc.py)を配置し、CodeCommitにPushします。 詳しくはブログをご覧ください。

$ tree ./
./
├── calc.py
└── tests
    └── test_calc.py

calc.py

足し算関数(plus)と、引き算関数(minus)を持ちます。

def plus(x,y):
    return x + y

def minus(x,y):
    return x - y

test_calc.py

足し算関数と、引き算関数をテストします。 足し算関数に3,5を与えて、3+5=8になるかを確認します。 引き算関数には10,3を与えて、10-3=7になるかを確認します。

import unittest
import calc

class TestCalc(unittest.TestCase):
    def test_plus(self):
        self.assertEqual(calc.plus(3,5), 8)
    def test_minus(self):
        self.assertEqual(calc.minus(10,3), 7)

ユニットテストの実行

テストを実行すると2つの項目が実行され、"OK"になります。

$ python3 -m unittest tests.test_calc
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
$

CodeBuildでのユニットテスト実行

ビルドスペックファイルを配置し、CodeBuildプロジェクトを作成します。

devブランチの作成

開発用のdevブランチを作成します。

$ git checkout -b dev
Switched to a new branch 'dev'
$ git branch
* dev
  master
$

ビルドスペックファイルの配置

ビルドスペックファイル(buildspec.yml)をソースディレクトリのルートに配置します。

$ tree ./
./
├── buildspec.yml
├── calc.py
└── tests
    └── test_calc.py

1 directory, 3 files
$

ビルドスペックのサンプルはユーザーガイドに記載されています。 phases>build>commandsにユニットテストのコマンドpython3 -m unittest tests.test_calcを記載します。 artifactsでは、成果物(アーティファクト)を指定します。

version: 0.2

phases:
  build:
    commands:
      - echo Build started on `date`
      - echo Compiling the Python code...
      - python3 -m unittest tests.test_calc
  post_build:
    commands:
      - echo Build completed on `date`
artifacts:
  files:
    - calc.py

オリジンへのpush

devブランチをオリジンにpushします。

git add ./*
git commit -m "add buildspec.yml"
git push origin dev

CodeBuildプロジェクトの作成

CodeBuildプロジェクトを作成します。 プロジェクト「blog-python-unittest」を作成し、LinuxのPython 3.6.5環境を指定しました。 ビルド仕様には、「ソースコードのルートディレクトリの buildspec.yml を使用」を指定します。先ほど作成したYAMLの通りにビルドするということです。

作成したプロジェクトを選択し、「ビルドの開始」を選択します。

devブランチを選択します。

ユニットテストを実行すると、フェーズごとの状態が表示されます。 今回は全て成功しました。 ビルドログには、ユニットテストの結果が表示されます。

CodePipelineで自動実行

CodePipelineは、変更が発生した時にCloudWatchイベントを使って、自動的にビルドを実行します。 SourceステージにCodeCommit、BuildステージにCodeBuildを指定しました。

かけ算機能の追加

かけ算機能を追加し、テストを自動実行します。

calc.py

times関数を追加します。 2つの引数をかけ算して、結果を返します。 retrunのスペルが誤っていますが、そのままにしておきます。

def plus(x,y):
    return x + y

def minus(x,y):
    return x - y

def times(x,y):
    retrun x * y

test_calc.py

かけ算関数をテストします。4×8=32になるかを確認します。

import unittest
import calc

class TestCalc(unittest.TestCase):
    def test_plus(self):
        self.assertEqual(calc.plus(3,5), 8)
    def test_minus(self):
        self.assertEqual(calc.minus(10,3), 7)
    def test_times(self):
        self.assertEqual(calc.times(4,8), 32)

かけ算関数のpush

CodeCommitのdevブランチにpushします。

git add ./*
git commit -m "add times function"
git push origin dev

pushすると、ユニットテストが自動で実行されます。 returnのスペルが誤っているので、失敗しました。 詳細リンクを選択すると、CodeBuildコンソールに飛びます。

CodeBuildコンソールでは、calc.pyの8行目に文法エラーがあることがわかります。

ユニットテストのステータスコードが"1"のため、BUILDフェーズが失敗したことがわかります。「Error while executing command: python3 -m unittest tests.test_calc. Reason: exit status 1」

コードの修正とpush

「retrun」を正しく「return」に変更して、pushします。 自動でユニットテストが行われ、グリーン(成功)しました。

さいごに

CodeBuildとCodePipelineを使って、devブランチへのpushを検知し、ユニットテストを自動実行しました。 サービスやユニットテストの概要を掴むお手伝いができたのであれば、幸いです。

参考