狠狠撸

狠狠撸Share a Scribd company logo
闯别苍办颈苍蝉と一绪に罢谤补肠プラグイン开発
                                                  -UT/IT自動化のコツ-




                                                          2011年5月20日
                                                        株式会社NTTデータ
Copyright ? 2011 NTT DATA CORPORATION          技術開発本部 ALMソリューションセンタ
アジェンダ

                   1. 自己绍介
                   2. Trac Plugin開発 with Jenkins
                              1. 開発環境説明
                              2. 環境準備(ジョブ作成など)
                              3. コーディング
                              4. 単体テスト
                              5. 結合テスト

                   3. 最後に




Copyright ? 2011 NTT DATA CORPORATION              1
自己绍介

    名前:和田 貴久
    Twitter ID:@wadatka
    所属:NTTデータ

    何ができる?:
              入社以来、Python道を攻略中です。
              それ以前は、データマイニングの研究や.netでの開発をやっていました。

    今の仕事:
     社内向けの開発支援ツールの開発
     各プロジェクトへの開発支援ツールの導入?普及活動


Copyright ? 2011 NTT DATA CORPORATION               2
社内向けの開発支援ツールの開発

SDワークベンチというツールの開発を行っています。




■ SDワークベンチとは、Trac、Subversion、JenkinsなどOSSのツールを組み合わせオール
  インワンパッケージです。
■ ツール、利用方法、開発方法の3点セットのベストプラクティスを普及活動しています。
■ ツールの開発を行う中でTracプラグインの開発を行ってきました。
   ? NTTデータならではの機能拡充
                   ?       チケットの情報公開範囲の指定など
       ? Trac?プラグインのバグフィックス

                                         ? 開発?バグフィックスの中で、Jenkinsを活用してデプロイ?単体テス
                                           ト?結合テストを自動化しました。

                                         オールインワンパッケージのインストーラの生成では、Jenkinsを活用
                                         して日次ビルド?テストを行ってきました。
 Copyright ? 2011 NTT DATA CORPORATION                                           3
Trac Plugin開発 with Jenkins

■ 弊社で行ってきたSDワークベンチ(開発支援ツール)の開発で、Jenkinsをどのように活
  用してきたか紹介します。

          ポイント
                                ? 品質を担保するため、一定以上のテスト密度が必要。
                                   ? バグ修正時の再テストの量が膨大
                                   ? リリース毎のテストの量が膨大
                                  ?Jenkinsで自動化して効率的に!

          ポイント
                               ? ゼロ機能リリースを実践!(ZFR:Zero-Feature Release)
                                 ?開発の最初に機能はないがリリースできる状態を用意
                                 ?以降の開発の進捗がわかりやすくなる!



Copyright ? 2011 NTT DATA CORPORATION                                     4
Trac Plugin開発(開発環境)

Tracプラグインの開発は、下記の開発環境とサーバ構成で行いました。
                        開発者端末
                                                    ? 実行結果(メール、IRC、???)
                                                    ? レポート

                                        ? コミット
         ? バグチケット                       ? チェックアウト/アップデート
                                                                               単体テストジョブ
                                                                      CIサーバ         Seleniumジョブ
                                                      ? チェックアウト/
                                                       アップデート
                                                                                    ? Java
                                                                                    ? Python
                                                       ? ポーリング

                                                                          デプロイジョブ
      ポイント
                                                                      デプロイサーバ(Slave)
           ? CIサーバやデプロイサーバは、ジョブ
             の実行で負荷がかかるので、                                                          ? Java
             Trac?Subversionと分けて構築しました!                                             ? Python
                                                                                    ? Trac


Copyright ? 2011 NTT DATA CORPORATION                                                             5
Trac Plugin開発(開発準備)

  「何を作るか」「どのように作るか」は、決まっているものとします。
  Jenkinsとコラボレーションするための準備をします。
? 準備は、次の2ステップで行います。
   1. Subversionへの雛型フォルダ?ファイルのインポート
                ? プラグイン本体コード
                                            コードの中身はなくてもOK!
                ? 単体テストコード
                                            まずは、フォルダと空ファイルだけでもインポート
                ? Seleniumコード
      2. Jenkinsのジョブの作成
                ?           単体テストジョブ
                                            インポートしたコードのチェックアウトと実行を行うジョブを作成
                ?           デプロイジョブ
                                            結果は空だが、いつでも実行できるジョブを用意
                ?           Seleniumジョブ

              ポイント
                                   ? CIやるならコーディング前からジョブを作るべし!
                                   ? ZFR を実践!!!

Copyright ? 2011 NTT DATA CORPORATION                                        6
Trac Plugin開発(SCMに雛型インポート)

Subversionに雛型フォルダ?ファイルをインポートします。
                                           プラグイン本体コード
         リポジトリ


                                                         単体テストコード




                                           Seleniumコード




           ポイント
                                ? プラグインのソースコードやテストコード、Seleniumコードは、
                                  同じフォルダ構成にするべし!
                                ? フォルダの構成?ジョブのコピーで再利用が簡単に!!!
 Copyright ? 2011 NTT DATA CORPORATION                                7
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
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
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
Trac Plugin開発(単体テストジョブ実行結果実例1)

単体テストジョブ実行結果




Copyright ? 2011 NTT DATA CORPORATION                11
Trac Plugin開発(単体テストジョブ実行結果実例2)

コードチェッカー(Pylint)とコード?カバレッジレポート




      ポイント
              ? Pythonだけど、Javaと同じように失敗個所、問題個所を簡単に確認できて、
                早めの対応が可能に!

 Copyright ? 2011 NTT DATA CORPORATION                    12
Trac Plugin開発(デプロイジョブ)

? 流れ:チェックアウト?プラグインインストール?Trac再起動
? Jenkinsプラグイン: -
? その他のツール: -

                                                    CIサーバー(Masterマシン)   ポイント
     リポジトリ
                                                                         ? デプロイサーバーに直
                                                    デプロイジョブ
                                                  ?チェックアウト
                                                                           接チェックアウトしてい
                                                  ?Windowsバッチの実行           た。
                                                                           ?テストのリビジョンと
                                                                           デプロイのリビジョンが
                                        チェックアウト                            異なることが。。。
                                                    デプロイサーバー
                                                    (Slaveマシン)

                                                                        ? デプロイサーバーには、事前に
                                                   ワークスペース
                                                                          Trac+Subversionをインストール
                                                  HelloWorldPlugin        が必要
                                                                        ? プラグインのインストールは、
                                                                          「python setup.py install」コマンド
                                                                          で実行

Copyright ? 2011 NTT DATA CORPORATION                                                                     13
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
Trac Plugin開発(Seleniumジョブ実行結果)

ジョブのトップページからSeleniumコードの実行結果を表示できます。




Copyright ? 2011 NTT DATA CORPORATION                15
Trac Plugin開発

ジョブの準備ができたので、実際に開発例を紹介します。
1. コーディング
                プラグイン本体のコードを実装
                テストコードの実装
2. 単体テスト
                コーディング時に実装しなかったテストケースを実装(異常系など)
3. 結合テスト
                Seleniumコードを実装
■ 今回実装するコードは、管理画面に「HelloWorldPlugin」のメニューを表示し、リンクをク
  リックしたときに画面上に「Hello World」と表示します。




Copyright ? 2011 NTT DATA CORPORATION                 16
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
Trac Plugin開発(コーディング 2/3)

単体テストでエラー発生!!
「assert data == {‘helloworld’: ‘’}」でAssertionErrorが発生。




                                                         エラー!



  Copyright ? 2011 NTT DATA CORPORATION                         18
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
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
Trac Plugin開発(単体テスト 2/2)

追加したテストの分だけ件数が増加した。




Copyright ? 2011 NTT DATA CORPORATION          21
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
Trac Plugin開発(結合テスト 2/2)

Seleniumコードの実行結果のサマリページと各テストの実行結果証跡を出力!!!




                                               Seleniumテストの実行証跡
                                               FUNC_01_01_01.png
                                                              FUNC_01_02_01.png




Copyright ? 2011 NTT DATA CORPORATION                                             23
最後に

まとめ
  ? コーディングをする前やコーディングの早い段階でジョブを作成したので、
    様々なメリットが得られました!!!(ZFRを実践!)
                  ? リファクタリング時のデグレーションの早期発見
                  ? 実装量、テスト実施量の見える化でモチベーションアップ


      ? 単体テスト~結合テストまでの自動化で再テスト時の作業の効率化が
        できた!!!
                  ? バグ修正時の再テストの効率化
                  ? リリース毎のテスト作業の削減




Copyright ? 2011 NTT DATA CORPORATION            24
今后取り组みたいこと

      ? 初期の雛型コードと雛型ジョブを一発で作成したい。

      ? Jenkinsからバグトラッカーへの連携を設定したい。

      ? XFDをつけたい。

      ? Selenium関連のプラグインを効果的に使いたい。




Copyright ? 2011 NTT DATA CORPORATION   25
おまけ

? 現在、Jenkinsに関する本書いています!!
? 今年度中に出版します。
? ぜひ書店で見かけた際には、買ってください!

? こんな内容が書かれる予定です。
    1.     継続的インテグレーションについて
           継続的インテグレーションについて
    2.     Jenkinsについて
           Jenkinsについて
    3.     Jenkinsインストールと
                  インストールと基本設定
           Jenkinsインストールと基本設定
    4.     Jenkinsの基本的な
           Jenkinsの基本的な使い方
    5.     Jenkinsを ってWeb開発をしてみよう
           Jenkinsを使ってWeb開発をしてみよう
                      Web開発
    6.     高度な
           高度な使い方




Copyright ? 2011 NTT DATA CORPORATION   26
Copyright ? 2011 NTT DATA CORPORATION

More Related Content

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
  • 4. 社内向けの開発支援ツールの開発 SDワークベンチというツールの開発を行っています。 ■ SDワークベンチとは、Trac、Subversion、JenkinsなどOSSのツールを組み合わせオール インワンパッケージです。 ■ ツール、利用方法、開発方法の3点セットのベストプラクティスを普及活動しています。 ■ ツールの開発を行う中でTracプラグインの開発を行ってきました。 ? NTTデータならではの機能拡充 ? チケットの情報公開範囲の指定など ? Trac?プラグインのバグフィックス ? 開発?バグフィックスの中で、Jenkinsを活用してデプロイ?単体テス ト?結合テストを自動化しました。 オールインワンパッケージのインストーラの生成では、Jenkinsを活用 して日次ビルド?テストを行ってきました。 Copyright ? 2011 NTT DATA CORPORATION 3
  • 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
  • 8. Trac Plugin開発(SCMに雛型インポート) Subversionに雛型フォルダ?ファイルをインポートします。 プラグイン本体コード リポジトリ 単体テストコード Seleniumコード ポイント ? プラグインのソースコードやテストコード、Seleniumコードは、 同じフォルダ構成にするべし! ? フォルダの構成?ジョブのコピーで再利用が簡単に!!! Copyright ? 2011 NTT DATA CORPORATION 7
  • 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
  • 13. Trac Plugin開発(単体テストジョブ実行結果実例2) コードチェッカー(Pylint)とコード?カバレッジレポート ポイント ? Pythonだけど、Javaと同じように失敗個所、問題個所を簡単に確認できて、 早めの対応が可能に! Copyright ? 2011 NTT DATA CORPORATION 12
  • 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
  • 19. Trac Plugin開発(コーディング 2/3) 単体テストでエラー発生!! 「assert data == {‘helloworld’: ‘’}」でAssertionErrorが発生。 エラー! Copyright ? 2011 NTT DATA CORPORATION 18
  • 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
  • 24. Trac Plugin開発(結合テスト 2/2) Seleniumコードの実行結果のサマリページと各テストの実行結果証跡を出力!!! Seleniumテストの実行証跡 FUNC_01_01_01.png FUNC_01_02_01.png Copyright ? 2011 NTT DATA CORPORATION 23
  • 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
  • 28. Copyright ? 2011 NTT DATA CORPORATION