Trac Plugin Developement with Jenkins
- 1. 闯别苍办颈苍蝉と一绪に罢谤补肠プラグイン开発
-UT/IT自動化のコツ-
2011年5月20日
株式会社NTTデータ
Copyright ? 2011 NTT DATA CORPORATION 技術開発本部 ALMソリューションセンタ
- 2. アジェンダ
1. 自己绍介
2. Trac Plugin開発 with Jenkins
1. 開発環境説明
2. 環境準備(ジョブ作成など)
3. コーディング
4. 単体テスト
5. 結合テスト
3. 最後に
Copyright ? 2011 NTT DATA CORPORATION 1
- 3. 自己绍介
名前:和田 貴久
Twitter ID:@wadatka
所属:NTTデータ
何ができる?:
入社以来、Python道を攻略中です。
それ以前は、データマイニングの研究や.netでの開発をやっていました。
今の仕事:
社内向けの開発支援ツールの開発
各プロジェクトへの開発支援ツールの導入?普及活動
Copyright ? 2011 NTT DATA CORPORATION 2
- 5. Trac Plugin開発 with Jenkins
■ 弊社で行ってきたSDワークベンチ(開発支援ツール)の開発で、Jenkinsをどのように活
用してきたか紹介します。
ポイント
? 品質を担保するため、一定以上のテスト密度が必要。
? バグ修正時の再テストの量が膨大
? リリース毎のテストの量が膨大
?Jenkinsで自動化して効率的に!
ポイント
? ゼロ機能リリースを実践!(ZFR:Zero-Feature Release)
?開発の最初に機能はないがリリースできる状態を用意
?以降の開発の進捗がわかりやすくなる!
Copyright ? 2011 NTT DATA CORPORATION 4
- 6. Trac Plugin開発(開発環境)
Tracプラグインの開発は、下記の開発環境とサーバ構成で行いました。
開発者端末
? 実行結果(メール、IRC、???)
? レポート
? コミット
? バグチケット ? チェックアウト/アップデート
単体テストジョブ
CIサーバ Seleniumジョブ
? チェックアウト/
アップデート
? Java
? Python
? ポーリング
デプロイジョブ
ポイント
デプロイサーバ(Slave)
? CIサーバやデプロイサーバは、ジョブ
の実行で負荷がかかるので、 ? Java
Trac?Subversionと分けて構築しました! ? Python
? Trac
Copyright ? 2011 NTT DATA CORPORATION 5
- 7. Trac Plugin開発(開発準備)
「何を作るか」「どのように作るか」は、決まっているものとします。
Jenkinsとコラボレーションするための準備をします。
? 準備は、次の2ステップで行います。
1. Subversionへの雛型フォルダ?ファイルのインポート
? プラグイン本体コード
コードの中身はなくてもOK!
? 単体テストコード
まずは、フォルダと空ファイルだけでもインポート
? Seleniumコード
2. Jenkinsのジョブの作成
? 単体テストジョブ
インポートしたコードのチェックアウトと実行を行うジョブを作成
? デプロイジョブ
結果は空だが、いつでも実行できるジョブを用意
? Seleniumジョブ
ポイント
? CIやるならコーディング前からジョブを作るべし!
? ZFR を実践!!!
Copyright ? 2011 NTT DATA CORPORATION 6
- 9. Trac Plugin開発(単体テストジョブ)
? 流れ:チェックアウト?単体テストコード実行?結果の変換
? Jenkinsプラグイン:Cobertura Plguin、violations
? その他のツール:ant、python-coverage、pylint、nose
CIサーバー(Masterマシン)
リポジトリ
ワークスペース 単体テストジョブ
Ant ?チェックアウト
チェックアウト
HelloWorldPlugin ?Ant実行 Antで定義した処理
? ワークスペースの初期化
?結果保存 ? プラグインのインストール
Result
? 単体テストコードの実行
? テスト結果の集計
チェックアウト ? カバレッジの集計
? Pylintでの解析
? 集計?解析結果の変換
ポイント
? 出てきた結果は視覚化したい!!!
?各種プラグインで読み込むために変換スクリプト
を実装。
Copyright ? 2011 NTT DATA CORPORATION 8
- 10. Trac Plugin開発(単体テストジョブでの利用ファイル)
単体テストジョブで実行したAntファイル
<?xml version="1.0"?> (続き)
<project name="pythonUnitTest" default="report">
<property name="project.name" value="pythonUnitTest"/> <!-- report -->
<target name="report" depends="test"> レポート変換
<!-- Properties for all source file folders --> <!-- report of nose -->
<property name="src.dir" value="plugins"/> <exec dir="${basedir}" executable="python.exe">
<arg line="converter.py test ${src.dir}?nosetest.xml ${result.dir}?nosetest.xml"/>
<property name="result.dir" value="result"/> </exec>
<property name="result.test" value="${result.dir}/nosetest.xml"/>
<property name="result.pylint" value="${result.dir}/pylint.txt.raw"/> <!-- report of coverage(xml) -->
<property name="result.coverage" value="${result.dir}/coverage.xml"/> <exec dir="${src.dir}" executable="coverage.exe">
<property name="result.coverage.dir" value="${result.dir}/coverage"/> <arg line="xml --omit=*__init__.py,*setup.py,*?*?tests?*.py"/>
</exec>
<!-- init -->
<target name="init">
初期化 <exec dir="${basedir}" executable="python.exe">
<delete dir="${result.dir}"/> <arg line="converter.py test ${src.dir}?coverage.xml ${src.dir}?coverage2.xml"/>
<mkdir dir="${result.dir}"/> </exec>
<mkdir dir="${result.coverage.dir}"/>
<exec dir="${src.dir}" executable="coverage.exe"> <!-- report of coverage(html) -->
<arg line="-e"/> <exec dir="${src.dir}" executable="coverage.exe">
</exec> <arg line="-b -i -d ..?${result.coverage.dir} --omit=*__init__.py,*setup.py,*?*?tests?*.py"/>
</target> </exec>
<!-- test --> <!-- report of pylint -->
<target name="test" depends="init"> テスト <exec dir="${src.dir}" executable="pylint.bat" output="${result.pylint}">
<exec dir="${src.dir}" executable="nosetests.exe"> <arg line="-f parseable -i y . --output-format=parseable --
<arg line="-i .*?.*?tests?*.py --with-xunit --xunit-file=nosetest.xml --with-coverage"/> ignore=decoder.py,report_editor.py,setup.py,tests"/>
</exec> </exec>
</target> <exec dir="${basedir}" executable="python.exe">
<arg line="converter.py pylint ${result.dir}?pylint.txt.raw ${result.dir}?pylint.txt"/>
</exec>
</target>
(続く???) </project>
Copyright ? 2011 NTT DATA CORPORATION 9
- 11. Trac Plugin開発(単体テストジョブでの利用ファイル)
単体テストジョブで実行したAntファイル
# -*- coding: utf-8 -*- (続き)
import codecs
import os argvs = sys.argv
import re argc = len(argvs)
import sys
if (argc != 4):
global _workspace_path print 'Usage: python %s mode input_file output_file' % argvs[0]
_workspace_path = os.path.dirname(os.path.abspath(__file__))
if argvs[1] == 'test':
print _workspace_path convert_character_code(argvs[2], argvs[3])
elif argvs[1] == 'pylint':
def convert_character_code(input_file, output_file): convert_escape_character(argvs[2], argvs[3])
input = codecs.open(input_file, 'r', 'shift_jis')
output = codecs.open(output_file, 'w', 'utf-8')
for line in input: 単体テスト結果、
output.write(line)
カバレッジの変換
output.close()
input.close()
print output_file + ' is generated from ' + input_file
def convert_escape_character(input_file, output_file):
input = open(input_file, 'r')
output = open(output_file, 'w',)
for line in input:
if '__init__.py' in line: Pylintの結果の変換
continue
else:
output.write(re.sub('????', '/', line))
output.close()
input.close()
print output_file + ‘ is generated from ’ + input_file (続く???)
Copyright ? 2011 NTT DATA CORPORATION 10
- 14. Trac Plugin開発(デプロイジョブ)
? 流れ:チェックアウト?プラグインインストール?Trac再起動
? Jenkinsプラグイン: -
? その他のツール: -
CIサーバー(Masterマシン) ポイント
リポジトリ
? デプロイサーバーに直
デプロイジョブ
?チェックアウト
接チェックアウトしてい
?Windowsバッチの実行 た。
?テストのリビジョンと
デプロイのリビジョンが
チェックアウト 異なることが。。。
デプロイサーバー
(Slaveマシン)
? デプロイサーバーには、事前に
ワークスペース
Trac+Subversionをインストール
HelloWorldPlugin が必要
? プラグインのインストールは、
「python setup.py install」コマンド
で実行
Copyright ? 2011 NTT DATA CORPORATION 13
- 15. Trac Plugin開発(Seleniumジョブ)
? 流れ:チェックアウト?Seleniumコード実行?結果出力
? Jenkinsプラグイン:HTML Publisher Plugin(結果表示に利用)
? その他のツール:firefox、Selenium、HTMLTestRunner.py
CIサーバー(Masterマシン)
リポジトリ
ワークスペース Seleniumジョブ
Selenium ?チェックアウト
?Selenium実行
チェックアウト ?結果保存
Seleniumテスト ポイント
? SeleniumジョブのSeleniumコードの実行は、
デプロイサーバー バッチとPythonのコードで実装しました。
(Slaveマシン) ? 下記のプラグインを使うとよりスマートに
実行できます。
? Selenium Plugin
? Selenium AES Plugin
? Seleniumhq Plugin
? SeleniumRC Plugin
? seleniumhtmlreport Plugin
Copyright ? 2011 NTT DATA CORPORATION 14
- 17. Trac Plugin開発
ジョブの準備ができたので、実際に開発例を紹介します。
1. コーディング
プラグイン本体のコードを実装
テストコードの実装
2. 単体テスト
コーディング時に実装しなかったテストケースを実装(異常系など)
3. 結合テスト
Seleniumコードを実装
■ 今回実装するコードは、管理画面に「HelloWorldPlugin」のメニューを表示し、リンクをク
リックしたときに画面上に「Hello World」と表示します。
Copyright ? 2011 NTT DATA CORPORATION 16
- 18. Trac Plugin開発(コーディング 1/3)
Tracの管理画面を表示するメソッドを実装します。
「helloworld.html」ページに変数「helloworld」に値「Hello World」を格納して渡します。
プラグインのコード(web_ui.py) テストコード(test_web_ui.py)
def render_admin_panel(self, req, cat, page, path_info): def test_render_admin_panel(self):
req.perm.require('TRAC_ADMIN') pass
data = {}
return 'helloworld.html', data
def render_admin_panel(self, req, cat, page, path_info): def test_render_admin_panel(self):
req.perm.require('TRAC_ADMIN') cat = 'helloworldplugin'
data = {'helloworld': "Hello World"} page = 'helloworld'
return 'helloworld.html', data path_info = ''
self.req.perm = PermissionCache(self.env, 'admin')
helloworld_admin = HelloWorldAdminPage(self.env)
templdate, data = helloworld_admin.render_admin_panel( self.req, cat, page, path_info )
assert templdate == 'helloworld.html'
assert data == {'helloworld': ''}
画面上の変更箇所
Copyright ? 2011 NTT DATA CORPORATION 17
- 20. Trac Plugin開発(コーディング 3/3)
エラーの発生しているテストコードを修正
テストコード(test_web_ui.py)
def test_render_admin_panel(self): def test_render_admin_panel(self):
cat = 'helloworldplugin' cat = 'helloworldplugin'
??? ???
assert data == {'helloworld': ''} assert data == {'helloworld': ‘Hello World'}
変数「helloworld」の期待値として「Hello World」を設定します。
ALL OK!!
Copyright ? 2011 NTT DATA CORPORATION 19
- 21. Trac Plugin開発(単体テスト 1/2)
異常系操作を行ったときのテストを実装します。
Tracの管理画面は、管理者権限を持つユーザだけアクセス可能。
管理者権限がないユーザでアクセスしたときのテストを実装。
テストコード(test_web_ui.py)
def test_render_admin_panel_error(self):
cat = 'helloworldplugin'
page = 'helloworld'
path_info = ''
self.req.perm = PermissionCache(self.env, ‘wada')
try:
helloworld_admin = HelloWorldAdminPage(self.env)
templdate, data = helloworld_admin.render_admin_panel( self.req, cat, page, self.path_info )
except PermissionError:
pass
else:
self.fail()
期待した権限を持つユーザでないときに発生するエラー
Copyright ? 2011 NTT DATA CORPORATION 20
- 23. Trac Plugin開発(結合テスト 1/2)
画面からのオペレーションテストをSeleniumで実装します。
テストは2つ行います。
? Tracの管理画面にアクセスした際に、メニューに「HelloWorldPlugin」と「HelloWorld」が表示されているか。
def test_func_01(self):
sel = self.selenium
num, cap_num = init(1)
sel.open("http://%s:%s@%s/trac/SampleProject/login" % (user, password, server))
sel.open("http://%s/trac/SampleProject/admin" % server) Trac管理画面へアクセス
self.failUnless(sel.is_text_present("HelloWorldPlugin")) メニューの確認
self.failUnless(sel.is_text_present(“HelloWorld"))
cap_num = capture(sel, num, cap_num, test_id, evidence) 画面キャプチャの取得
? リンク「helloworld」をクリックしたとき、画面に「Hello World」と表示されるか。
def test_func_02(self):
sel = self.selenium
num, cap_num = init(2)
sel.open("http://%s:%s@%s/trac/SampleProject/login" % (user, password, server))
sel.open("http://%s/trac/SampleProject/admin" % server)
Trac管理画面へアクセス
sel.click(u"link=HelloWorld") 実装したページへのリンク
self.failUnless(sel.is_text_present("Hello World")) 「Hello World」の表示確認
cap_num = capture(sel, num, cap_num, test_id, evidence) 画面キャプチャの取得
Copyright ? 2011 NTT DATA CORPORATION 22
- 25. 最後に
まとめ
? コーディングをする前やコーディングの早い段階でジョブを作成したので、
様々なメリットが得られました!!!(ZFRを実践!)
? リファクタリング時のデグレーションの早期発見
? 実装量、テスト実施量の見える化でモチベーションアップ
? 単体テスト~結合テストまでの自動化で再テスト時の作業の効率化が
できた!!!
? バグ修正時の再テストの効率化
? リリース毎のテスト作業の削減
Copyright ? 2011 NTT DATA CORPORATION 24
- 26. 今后取り组みたいこと
? 初期の雛型コードと雛型ジョブを一発で作成したい。
? Jenkinsからバグトラッカーへの連携を設定したい。
? XFDをつけたい。
? Selenium関連のプラグインを効果的に使いたい。
Copyright ? 2011 NTT DATA CORPORATION 25
- 27. おまけ
? 現在、Jenkinsに関する本書いています!!
? 今年度中に出版します。
? ぜひ書店で見かけた際には、買ってください!
? こんな内容が書かれる予定です。
1. 継続的インテグレーションについて
継続的インテグレーションについて
2. Jenkinsについて
Jenkinsについて
3. Jenkinsインストールと
インストールと基本設定
Jenkinsインストールと基本設定
4. Jenkinsの基本的な
Jenkinsの基本的な使い方
5. Jenkinsを ってWeb開発をしてみよう
Jenkinsを使ってWeb開発をしてみよう
Web開発
6. 高度な
高度な使い方
Copyright ? 2011 NTT DATA CORPORATION 26