はじめに
FreeBSDにおける準仮想化機構jail
をWeb上で構築できるプラットフォーム「jaisting」をパブリックリリースする運びとなりました。
概要についてはこちら→https://note.mu/himrock922/n/n8e35b1d70e19
リポジトリはこちら→https://github.com/himrock922/jaisting
本格的にリリースする運びとなりましたので、そろそろテストをゴリゴリ書いていこうと思います。今回はテストをコーディングするまでに必要な環境を構築するまでの手順を執筆したいと思います。
Cirrus-CIによるFreeBSDマシンの構築
今回のプロジェクトでは、FreeBSD特有の機能をフルに利用したWebアプリケーションとなります。そのため、CIでテスト駆動するとは言え、前提としてFreeBSDをサポートしているCIサービスを利用しなければなりません。そのため、まず初めにFreeBSDをサポートしているCIサービスがそもそもあるのか調べる事が先決となりました。
CIサービスでは、今の所cirrus-ci
がFreeBSDをサポートしている模様なので、これを使いたいと思います。cirrus-ciはGitHub MarketPlaceでも提供されており、リポジトリ単位でcirrus-ciを使うかどうかフラットに決められるます。
構築手順のドキュメントはこちら→* https://cirrus-ci.org/guide/FreeBSD/
freebsd_instance:
image: freebsd-12-0-release-amd64
task:
install_script:
- pkg update
- pkg install -y python37 py36-psycopg2 py36-ucl rsync git
check_script:
- mount
script:
- pkg install -y postgresql11-server yarn
- sysrc postgresql_enable=YES
- service postgresql initdb
- service postgresql start
- sudo -u postgres createuser jaisting
- sudo -u postgres psql -c "alter user jaisting with encrypted password 'jaisting'"
- sudo -u postgres psql -c "alter role jaisting with createdb"
- python3.7 -m ensurepip
- python3.7 -m pip install Cython==0.29.5
- python3.7 -m pip install -r packages.txt
- yarn install
- ./node_modules/.bin/webpack --config webpack.config.js --mode=development
- flake8 ./**/*.py
- python3.7 manage.py test
CIで行う事は大まかに分けて二つのみです。
- コードスタイルチェック
- テスト実行
なお、テスト実行については今回のプラットフォームが、ZFSでホストOSのファイルシステムを構築する事が必須となりますので、後々ZFSによるストレージプールをCIでも構築できるようにしなければなりません(今回の開発に限らず、FreeBSDで新規に何か開発しようとするとZFSによるファイルシステム構築が必須になりかけている)。
とりあえずは、一旦現状の設定でできるところまでテストを書いていきます。
コードスタイルチェック
今回のPythonファイルのコードスタイルのチェックとして、スタイルガイドについての文書であるPEP8と、それ以外の論理的なエラーをチェックするツールであるpyflakesと循環的複雑度をチェックできるラッパーであるflake8を採用しました。
また、プロジェクト単位でチェックする設定を決めるにはsetup.cfg
に記載した設定があると自動的に読み込まれるようになります。
[flake8]
ignore = D100, D101, D102, D103, F401
max-line-length = 150
exclude = **/migrations/*, **/__init__.py
とりあえず、今回のプロジェクトではGitHubでソースコードを見る事が多いと思うので行の最大長だけギリギリスクロールが発生しない程度まで制限を緩和しておきます。
テスト実行
実際にテストを書いていきましょう。初めは単純にログイン認証が必要な画面でログインしていない場合にアクセスすると、ログイン画面にリダイレクトするか検証するテストを書いて実行します。
% python3.7 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
----------------------------------------------------------------------
Ran 0 tests in 0.000s
test.pyに書いたテストケースが実行されていませんね。調べてみると、どうもメソッド名にtest
のプレフィックスをつけないと実行されない模様です(これ、実はあんまり納得してないです。メソッド名に制約をつけてしまうと汎用性なくなるんじゃないかなと、ちょっと考えすぎな気もしますが)。
- 修正前
class JailsViewTest(TestCase):
def login_require_jails(self):
response = self.client.get('/jails/')
self.assertRedirects(response, '/accounts/login/?next=/jails/')
- 修正後
% python3.7 manage.py test
class JailsViewTest(TestCase):
def test_login_require_jails(self):
response = self.client.get('/jails/')
self.assertRedirects(response, '/accounts/login/?next=/jails/')
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.081s
OK
Destroying test database for alias 'default'...
from django.test import TestCase, Client
from django.contrib.auth import get_user_model
class TemplateTest(TestCase):
def test_login_require_jails(self):
response = self.client.get('/jails/')
self.assertRedirects(response, '/accounts/login/?next=/jails/')
def test_login_require_jail_new(self):
response = self.client.get('/jails/new')
self.assertRedirects(response, '/accounts/login/?next=/jails/new')
def test_access_jails(self):
User = get_user_model()
self.client = Client()
self.client.force_login(User.objects.create_user('user'))
response = self.client.get('/jails/')
self.assertTemplateUsed(response, 'jails/index.html')
self.client.logout()
def test_access_jail_new(self):
User = get_user_model()
self.client = Client()
self.client.force_login(User.objects.create_user('user'))
response = self.client.get('/jails/new')
self.assertTemplateUsed(response, 'jails/new.html')
self.client.logout()
from django.test import TestCase
class TemplateTest(TestCase):
def test_login_require_networks_new(self):
response = self.client.get('/networks/new')
self.assertRedirects(response, '/accounts/login/?next=/networks/new')
まとめ
VMでCIテスト駆動やるの結構辛いです。今回のcirrus-ciのプランではパブリックリポジトリの場合、無料で行えるため文句言えないですが、、、
むしろ、今回のプラットフォームでCI用のコンテナ作れると嬉しいかもしれませんね。それをするにはやはりスケーリング機能も重要になってきますが。
まだまだ、やる事いっぱいあるので、今回はこの辺で