GitLab CIでpythonのunittestを自動化

# はじめに
GitLab CIをちょっと使えるようになったので、python使ってunittest試してみようと思った。
少し試行錯誤した結果、うまくいった。

# コードとテスト
簡単なコードとそれのテストを書く。
確認のため、テストでfailするのも書いといた。

```python:calc.py
def add(x, y):
    return x + y
```

```python:tests/test_calc.py
import unittest
import calc

class TestCalc(unittest.TestCase):

    def test_add_three(self):
        self.assertEqual(calc.add(5,3), 8)

    def test_add_three_fail(self):
        self.assertEqual(calc.add(5,3), 7)
```

```bash
python -m unittest tests/test_calc.py
```

# GitLab CIの設定とか

## 試行1
えいやでやってみた。
alpine使うと、python入ってないらしい。
alpineは軽量らしいから使ってみたけど、駄目やった。

```yaml:.gitlab-ci.yml
image: alpine

stages:
  - test

test:
  stage: test
  script: python -m unittest tests/test_calc.py
```

```
unning with gitlab-runner 11.1.0 (081978aa)
  on docker-auto-scale e11ae361
Using Docker executor with image ruby:2.5 ...
Pulling docker image ruby:2.5 ...
Using docker image sha256:1d8640b852eb145393da754ec38a4153edcf473a249448b7a271cea5f06016fa for ruby:2.5 ...
Running on runner-e11ae361-project-7757792-concurrent-0 via runner-e11ae361-srm-1533394697-2f948c1c...
Cloning repository...
Cloning into '/builds/iton/pyunittest'...
Checking out 50b90f99 as master...
Skipping Git submodules setup
$ python -m unittest tests/test_calc.py
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
    main(module=None)
  File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
    self.module)
  File "/usr/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
    module = __import__('.'.join(parts_copy))
ImportError: Import by filename is not supported.
ERROR: Job failed: exit code 1
```

## 試行2
image消して試してみた。
python3的なコード書いてたのに、pythonで実行したのでエラーが出た。
手元の環境とテストの環境が違うことによる典型的なエラーだと思う。

```yaml:.gitlab-ci.yml
stages:
  - test

test:
  stage: test
  script: python -m unittest tests/test_calc.py
```

```
unning with gitlab-runner 11.1.0 (081978aa)
  on docker-auto-scale e11ae361
Using Docker executor with image ruby:2.5 ...
Pulling docker image ruby:2.5 ...
Using docker image sha256:1d8640b852eb145393da754ec38a4153edcf473a249448b7a271cea5f06016fa for ruby:2.5 ...
Running on runner-e11ae361-project-7757792-concurrent-0 via runner-e11ae361-srm-1533394697-2f948c1c...
Cloning repository...
Cloning into '/builds/iton/pyunittest'...
Checking out 50b90f99 as master...
Skipping Git submodules setup
$ python -m unittest tests/test_calc.py
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
    main(module=None)
  File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
    self.module)
  File "/usr/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/usr/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
    module = __import__('.'.join(parts_copy))
ImportError: Import by filename is not supported.
ERROR: Job failed: exit code 1
```

## 試行3
python3で試してみた。やっとうまくいった。

```yaml:.gitlab-ci.yml
stages:
  - test

test:
  stage: test
  script: python3 -m unittest tests/test_calc.py
```


```
Running with gitlab-runner 11.1.0 (081978aa)
  on docker-auto-scale 0277ea0f
Using Docker executor with image ruby:2.5 ...
Pulling docker image ruby:2.5 ...
Using docker image sha256:1d8640b852eb145393da754ec38a4153edcf473a249448b7a271cea5f06016fa for ruby:2.5 ...
Running on runner-0277ea0f-project-7757792-concurrent-0 via runner-0277ea0f-srm-1533394736-2b371039...
Cloning repository...
Cloning into '/builds/iton/pyunittest'...
Checking out 6950c363 as master...
Skipping Git submodules setup
$ python3 -m unittest tests/test_calc.py
.F
======================================================================
FAIL: test_add_three_fail (tests.test_calc.TestCalc)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builds/iton/pyunittest/tests/test_calc.py", line 10, in test_add_three_fail
    self.assertEqual(calc.add(5,3), 7)
AssertionError: 8 != 7

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)
ERROR: Job failed: exit code 1
```


# おわりに
すごくシンプルなことしかやっていないけど、学ぶものはあった。
テスト環境と手元の環境の違いを気にしなあかん。
これくらいのテストやったら手元で試したほうが速い気がするけど、
勝手にテストしてくれるのって気持ちいい。
手元でやるとテスト結果残す工夫もいるけど、
勝手にログ取ってくれるのも楽ちんなんかも。

# 参考
[Pythonでunittestを実行する（初心者用） - Qiita](https://qiita.com/takus69/items/cde279266b46daf9972d)
[27.4. unittest --- ユニットテストフレームワーク — Python 3.7.0 ドキュメント](https://docs.python.org/ja/dev/library/unittest.html)