redmine のテストを Cucumber と Selenium2.0 WebDriver を使って firefox と IE の2つのブラウザでやってみる
というわけで次は cucumber と組み合わせてみる。
準備するもの
さっきの続きなので、追加したものだけ。
Selenium陣営
- Development Kit : http://github.com/downloads/oneclick/rubyinstaller/DevKit-tdm-32-4.5.2-20110712-1620-sfx.exe
- ruby 1.9.2 は入れてる。
Redmine陣営
特になし。
Capybara 使った cucuber の雛形を持ってくる
Railsアプリを準備
> rails new test-app
Gemfile に cucumber-rails, capybara を追加
group test に突っ込んだ。
group :test do # Pretty printed test output gem 'turn', :require => false gem 'cucumber-rails' gem 'capybara' end
雛形を作る
> cd test-app > ruby script/rails generate cucumber:install --capybara
雛形だけがほしかったので、
test-app\features にあるファイルとフォルダを、どこぞにコピーする。
+ using-cucumber + step_definitions + web_steps.rb + support + env.rb + paths.rb + selectors.rb
動くようにコードを改変
rails アプリの中で動かすならこれでいいのだけれど、
今回は外から rails のテストをするので、改変しないとエラーが出て動かない。
※ 多分ちゃんとやり方があるに違いない。
env.rb
# -*- encoding: utf-8 -*- require "Capybara" require "Capybara/cucumber" require 'test/unit/assertions' World(Test::Unit::Assertions) Capybara.app_host = "http://127.0.0.1:3000" Capybara.default_driver = :selenium Capybara.javascript_driver = :selenium Capybara.default_wait_time = 2 Capybara.add_selector(:name) do xpath { |name| XPath.descendant[XPath.attr(:name) == name.to_s] } match { |value| value.is_a?(Symbol) } end Capybara.add_selector(:class_name) do xpath { |class_name| XPath.descendant[XPath.attr(:class) == class_name.to_s] } match { |value| value.is_a?(Symbol) } end
feature をもくもくと書く
web_steps.rb も成長させる
できあがった feature はこんな感じ。
redmine_walpurugis_night.feature
Feature: redmine_walpurgis_nights In order to extend life of universe As Incubator wants more energy. Scenario: Register new issue Given I go to toppage When I follow "ログイン" And I fill in "username" with "qb" And I fill in "password" with "homuhomu" And I press "login" When I select "Walpurgis Night" And I follow "New issue" When I select "Feature" from "issue_tracker_id" And I fill in "issue_subject" with "As Incubator, I want magical girl." And I fill in "issue_description" with "foobaa" And I select "High" from "issue_priority_id" And I select "qb incubator" from "issue_assigned_to_id" And I press "commit" When I follow "Issues" Then I should see following data at the head of issue list: |tracker|status|priority|subject |assigned_to | |Feature|New |High |As Incubator, I want magical girl.|qb incubator|
詰まったところ。
- テーブルを見つけて、目的の値を取り出す(WebDriver と同じような操作で大丈夫だった)
初めての人がつまづきそうなところは、まとめたほうがいいかもなー。
残りの宿題
- コードを github にあげる
- akephalos を使ってみる
- SeleniumIDE の formatter として cucumber(capybara) を作る
- アル程度、定型にしちゃえば何とかできるじゃないのかな? と思ったり。これないと不便でしょうがない。
redmine のテストを Selenium2.0 WebDriver を使って firefox と IE の2つのブラウザでやってみる
概要
某ベンダ系アジャイルコミュニティの合宿で Selenium を使ったテストをやってみたので、
復習がてら Windows XP SP3上でタイトルのことをやってみたりする。
テストの内容
- ログイン
- チケット作成
- チケット一覧で作成したチケットを確認
- ログアウト
まで。
なぜ Selenium(WebDriver) なの?
なんで WebDriver なの?という説明が Selenium 2.0 と WebDriver — Selenium 日本語ドキュメント にあった。
準備物
Redmine 陣営
- Redmine 1.2.1 : http://rubyforge.org/frs/download.php/75099/redmine-1.2.1.zip
- ruby 1.8.7 : http://rubyforge.org/frs/download.php/75107/rubyinstaller-1.8.7-p352.exe
各自、適当にインストールする。
selenium-webdriver と cucumber を 1.8.7 に同居させられなかったので Selenium 陣営は 1.9.2 を選択…。
プロジェクト "Walpurgis Night" と ユーザ "qb" をあらかじめ作っておく。
諸般の事情により、とりあえず英語。
操作を記録して、Test::Unit ファイルに吐き出す
firefox -> ツール -> Selenium IDE を選択。
以下の操作を行う。赤い●のボタンを押すと記録開始、も一回押すと記録終了。
- ログイン
- チケット作成
- チケット一覧を表示
- ログアウト
記録した操作を実行して動くか確かめる
メニュー -> アクション -> 現在のテストケースを実行。
念のため、記録した操作を保存する
メニュー -> テストケースを保存。
記録した操作を Test::Unit に吐き出す
これは便利だなぁ!
メニュー -> テストケースをエクスポート -> Ruby Test::Unit(WebDriver)
ここで、 テストスイート にしちゃうとエラーが出るので注意。
Test::Unit を使って保存した操作を再現する
1.9のおまじないと、rubygems をインポートする
# -*- encoding: utf-8 -*- require "rubygems"
タイムアウト?の時間を2秒にする
@driver.manage.timeouts.implicit_wait = 2
URLを変更する(何のためって言えばいいのだろう)
@driver.get "http://127.0.0.1:3000"
Option を選択するところは自動出力してくれないので自力で書く
select - How do I set a an option as selected using selenium-webdriver (selenium 2.0) client in ruby - Stack Overflow を参考に。
メソッドに抜き出しておいてもよさそう。
実行する
無事チケットができてたらOK
ruby \path\to\testcase.rb
結果を検証する
ちゃんと希望通りの内容になっているのか検証をする。
手抜きをして、
- チケット一覧で目的のタイトルのチケットがあること
- 該当するチケットのトラッカー、優先度、担当者が期待通りであること
- チケット番号は動的に変わってしまうからチェックなし
のみ検証してみる。
検証するために必要な操作
- チケット一覧(テーブル)を見つける
- 見つけたテーブルの一番上にあるデータを取り出す
- タイトル、トラッカー、優先度、担当者の値を取り出し、チェックする
ができないといけない。ふぅ。
ここで Web Developer の出番な訳ですよ!
Web Driver を使って要素の情報を取り出す
使い方は(後で書くかも)
情報 -> 要素の情報を表示する が使えると思う。
テーブルを見つけて値を取り出し、検証する
チケット一覧テーブルの一番上にある行を取り出せば良い感じ。
トラッカーの内容が Feature であることを確認するにはこんな感じ。
@driver.find_element(:xpath, "//table[@class='list issues']/tbody/tr[1]") assert_equal "Feature", latest_issue.find_element(:class, "tracker").text, "tracker"
実行して確かめる
> ruby \path\to\testcase.rb Loaded suite Redmine-WalpurgisNight Started . Finished in 16.453125 seconds. 1 tests, 6 assertions, 0 failures, 0 errors, 0 skips Test run options: --seed 31656
できた!!
firefox だけじゃなくって IE でも動かしてみる
これができてこそ。setup の最初の行をちょいちょいと書き換える
@driver = Selenium::WebDriver.for :ie
ログインする時にパスワードをうんぬんのダイアログが出て最初は失敗したけど、
2回目は成功。
すげーすげー。
でも、いちいちここ切り替えるの面倒。
上手い指定の方法が思いつかないので、環境変数使うことにする。
上手く切り替わったゾ!
ここまでのコード
今度は Cucumber と組み合わせてできるかやってみよう。
# -*- encoding: utf-8 -*- require "rubygems" require "selenium-webdriver" require "test/unit" class RedmineWalpurgisNight < Test::Unit::TestCase def setup raise "Please set environment variable 'WEBDRIVER_TYPE' (ex. firefox, ie)" unless ENV["webdriver_type"] @driver = Selenium::WebDriver.for ENV["webdriver_type"].to_sym @driver.manage.timeouts.implicit_wait = 2 @verification_errors = [] end def teardown return unless @driver @driver.quit assert_equal [], @verification_errors end def test_redmine_walpurgis_night @driver.get "http://127.0.0.1:3000" @driver.find_element(:link, "ログイン").click @driver.find_element(:id, "username").clear @driver.find_element(:id, "username").send_keys "qb" @driver.find_element(:id, "password").clear @driver.find_element(:id, "password").send_keys "homuhomu" @driver.find_element(:name, "login").click select_option @driver.find_element(:css,'select'), "Walpurgis Night" @driver.find_element(:link, "New issue").click select_option @driver.find_element(:id,'issue_tracker_id'), "Feature" @driver.find_element(:id, "issue_subject").clear @driver.find_element(:id, "issue_subject").send_keys "As Incubator, I want magical girl." @driver.find_element(:id, "issue_description").clear @driver.find_element(:id, "issue_description").send_keys "foobaa" select_option @driver.find_element(:id,'issue_priority_id'), "High" select_option @driver.find_element(:id,'issue_assigned_to_id'), "qb incubator" @driver.find_element(:name, "commit").click @driver.find_element(:link, "Issues").click latest_issue = @driver.find_element(:xpath, "//table[@class='list issues']/tbody/tr[1]") assert_equal "Feature", latest_issue.find_element(:class, "tracker").text, "tracker" assert_equal "New", latest_issue.find_element(:class, "status").text, "status" assert_equal "High", latest_issue.find_element(:class, "priority").text, "priority" assert_equal "As Incubator, I want magical girl.", latest_issue.find_element(:class, "subject").text, "subject" assert_equal "qb incubator", latest_issue.find_element(:class, "assigned_to").text, "assignee" @driver.find_element(:link, "Sign out").click end def element_present?(how, what) @driver.find_element(how, what) true rescue Selenium::WebDriver::Error::NoSuchElementError false end def select_option(my_select, option_text) my_select.find_elements( :tag_name => "option" ).find do |option| option.text == option_text end.click end def verify(&blk) yield rescue Test::Unit::AssertionFailedError => ex @verification_errors << ex end end
Fest + JRuby + Cucumber で Swing のテストをする - 破
何も破ってない気もするけれど。
Step03 最初のステップ - 画面を見つける
cucumber の詳細についてはググってもらうとして。
step を作成する
test-feature/step_definitions/ticket_viewer_steps.ja.rb
Given /^"([^"]*)" が表示されていること$/ do |window_name| case window_name when /メイン画面/ component_name = "MainFrame" else component_name = window_name end #==================================================================================== # fest の API - WindowFinder を使って、画面を見つける # 変数の前に @ をつけておけば、他のところでも参照できる。(仕組みはイマイチ分からんが) #==================================================================================== @current_window = FestFinder::WindowFinder.findFrame(component_name).using($my_robot) end
タイトルで検索するメソッドはない(自作すればできる)ので、タイトルを画面名に変換して検索。
WindowFinder.findFrame を呼ぶだけでは画面は見つけられなくって、 using でロボットを指定する必要がある。
画面が見つからない場合、 fest が以下のようなエラーを吐くので、その辺のコードはいらない。
そういえば、ロボットは実行中のテストで唯一のほうがいいと、どっかのページで見たことがあったぞ…。
WindowFinder, ロボットを唯一にするために、 env.rb を修正する。
test-feature/support/env.rb
#======================================================================================= # Java のライブラリを使えるようにする #======================================================================================= include java #======================================================================================= # festの名前空間を定義 #======================================================================================= # クラスパスを通して Dir["#{File.dirname(__FILE__)}/../../lib/fest\*.jar"].each { |jar| require jar } # 名前空間を定義する module FestFeature include_package 'org.fest.swing.fixture' end module FestFinder include_package 'org.fest.swing.finder' end module FestCore include_package 'org.fest.swing.core' end #======================================================================================= # 製品の名前空間を定義 #======================================================================================= # こっちも同様に require "#{File.dirname(__FILE__)}/../../dist/AutomatedUITestingJava.jar" module MyPrjTicketViewer include_package 'local.myproject.ticket_viewer' end #======================================================================================= # このテスト用のロボットを作成する #======================================================================================= $my_robot = FestCore::BasicRobot.robotWithNewAwtHierarchy #======================================================================================= # 画面を作って、表示する #======================================================================================= # Fixture の詳細は http://easytesting.org/swing/apidocs/index.html 辺を参照のこと main_frame = FestFeature::FrameFixture.new $my_robot, MyPrjTicketViewer::MainFrame.new main_frame.show #======================================================================================= # Cucumber のテストが終わった後にやること #======================================================================================= at_exit do # fest が使った資源をきれいさっぱり掃除する main_frame.cleanUp end
実行する
> cucumber ..\test-feature --format html --out cucumber.html
無事メイン画面を見つけて、ステップが成功。
整理する
Webアプリのテストとかだと support/paths.rb とかで画面名とURLの対応を取ってる。
それをまねして。
test-feature/support/window_name.rb
def window_name_to(window_title) case window_title when /メイン画面/ ret_val = "MainFrame" else raise "画面名をマッピングできませんでした #{window_title}" end return ret_val end
変数名がちょい変わったので、steps 側も修正。
Step04 テキストボックスに値を入力する
次のステップ。さっきと同じように step_definitions/ticket_viewer_steps_ja.rb にコードを追加すればOK。
fest の FrameFixture クラスにある textBox メソッドにコントロールの名前を渡せばOK
test-feature/step_definitions/ticket_viewer_steps_ja.rb
When /^"([^"]*)" に "([^"]*)" を入力する$/ do |name, value| case name when /URL/ control_name = "ctlURL" else raise "コントロール名をマッピングできませんでした #{name}" end #==================================================================================== # textbox に値を入力する場合は setText を使う (enterText だと http:// が http+// になってしまった…) #==================================================================================== @current_window.textBox(control_name).setText(value) end
実行する
おや? http:// じゃなくって http+// になるぞ?
setText にすると http:// になるな。なぜだ…。気持ち悪いけど setText でいくことにする。
整理する
やっぱり control_name.rb があったほうがいいかなぁ。
test-feature/support/control_name.rb
詳細は略。
Step05 ボタンをクリックする
同じように FrameFixture の button メソッドでボタンを見つけて、click メソッドを呼べばいい。
test-feature/step_definitions/ticket_viewer_steps_ja.rb
When /^"([^"]*)" ボタンをクリックする$/ do |name| control_name = control_name_to(name) #==================================================================================== # click でボタンをクリック。説明なんていらないよな。 #==================================================================================== @current_window.button(control_name).click end
コントロール名への変換は control_name.rb で。
実行する
押してるっぽい。ポーズとかできると良いんだけども…。
Step06 値を検査する
テキストで書いた表を使って値の検査ができるのは、 cucumber ならでは。
同じように step_definitions/ticket_viewer_steps_ja.rb に追記する。
test-feature/step_definitions/ticket_viewer_steps_ja.rb
Then /^リスト "([^"]*)" に以下が表示されていること:$/ do |name, table| control_name = control_name_to(name) #==================================================================================== # FrameFixture.list で JList を見つける。 # contens メソッドはリストの内容を String の配列にしてくれる。 # Cucumber::Ast::Table の diff! を使えるよう、[["hoge"],["fuga"]] のように配列の配列にする # 多分もっと良い方法があるに違いない。 #==================================================================================== list = @current_window.list(control_name).contents.map {|project_name|[project_name]} #==================================================================================== # Cucumber::Ast::Table の diff! を使って簡単に内容を比較できる(こりゃ楽だ) #==================================================================================== table.diff!(list) end
ここはちょっとだけ説明。上記のように書いておくと、
ならば リスト "プロジェクト一覧" に以下が表示されていること: |r-labs | |Hudson | |IssueExtension| |WikiExtension |
の内容を見て name に "プロジェクト一覧" が、 table に表の部分が読み込まれる。
table の正体 Cucumber::Ast::Table クラス。
このクラス、 diff! メソッドで内容を比較してくれる。便利!
評価したい内容を 配列の配列、または ハッシュの形にして、 diff! メソッドに渡せば良い。
table の内容と違っていると...
のように違ってる場所を示してくれる。さらに便利!
Fest + JRuby + Cucumber で Swing のテストをする - 序
本人が行き当たり場当たりな性格なので、予告なく内容が変わることがあります(特にコード)
動機
材料
- Windows XP
- JDK 1.6.0 update 23
- JRuby 1.6.3 http://www.jruby.org/download
- Fest http://code.google.com/p/fest/
- fest-assert 1.4
- fest-mocks 1.0
- fest-reflect 1.2
- fest-swing 1.2
- Cucumber 1.0.1
- Ant 1.8.2
インストール
JDK/Juby/Ant インストール
適当で。
Cucumber インストール
> jgem install cucumber
Fest インストール
- fest-assert-1.4.jar
- fest-mocks-1.0.jar
- fest-swing-1.2.jar
- fest-util-1.1.2.jar
- fest-util-1.1.2.jar
を適当なディレクトリに置く。
Step01 - 最初の一歩
ディレクトリを掘る
automated-ui-testing-java + src + local + myproject + ticket_viewer + test-feature + step_definitions + support
テストコードを書く
# language: ja フィーチャ: チケット画面を出すところまで シナリオ: No1. プロジェクト一覧を取得する 前提 "メイン画面" が表示されていること もし "URL" に "http://www.r-labs.org/projects.xml" を入力する かつ "ctlFetchProjectList" ボタンをクリックする ならば "プロジェクト一覧" に以下が表示されていること: |r-labs | |Hudson | |IssueExtension| |WikiExtension |
おもむろにテストを実行する
Windowsのコンソールに出すと文字化けするので、仕方なく HTML に出力する
結果は cucumber.html に出力される。
> cucumber test-feature --format html -out cucumber.html
> cucumber.html
当然のごとくテストは動かないので、ここからイロイロやるわけですな。
Step02 - まずは画面を表示する
製品コードを書く
メイン画面とチケット一覧画面を作る。
適当にコントロールとボタンを配置する。
automated-ui-testing-java + src + local + myproject + ticket_viewer + MainFrame.java + TicketListFrame.java
fest を使って画面を表示する
fest FrameFixture を作って画面を表示する。
別にここでテストするわけじゃないので、単に Frame を setVisible すればイイだけではあるが。
テストシナリオの中で起動しても良いんだけども、at_exit フックを使いたかったので
テストシナリオの前に画面を表示する方向で。
support/env.rb に書けば、事前に実行してくれるんじゃないかな?
support/env.rb
#======================================================================================= # Java のライブラリを使えるようにする #======================================================================================= include java #======================================================================================= # festの名前空間を定義 #======================================================================================= # クラスパスを通して Dir["#{File.dirname(__FILE__)}/../../lib/fest\*.jar"].each { |jar| require jar } # 名前空間を定義する module FestFeature include_package 'org.fest.swing.fixture' end #======================================================================================= # 製品の名前空間を定義 #======================================================================================= # こっちも同様に require "#{File.dirname(__FILE__)}/../../dist/AutomatedUITestingJava.jar" module MyPrjTicketViewer include_package 'local.myproject.ticket_viewer' end #======================================================================================= # 画面を作って、表示する #======================================================================================= # Fixture の詳細は http://easytesting.org/swing/apidocs/index.html 辺を参照のこと mainFrame = FestFeature::FrameFixture.new MyPrjTicketViewer::MainFrame.new mainFrame.show #======================================================================================= # Cucumber のテストが終わった後にやること #======================================================================================= at_exit do # fest が使った資源をきれいさっぱり掃除する mainFrame.cleanUp end
Cobertura に日本語パッチを当てる 1.9.4.1 版
タイトルは coberturaに日本語パッチをあてる - お仕事の備忘録みたいなもの からいただきました。どもすみません。
※ cobertura のテストが幾つか失敗してます。参考程度にどうぞ。
Cobertura が用意しているテストはクリアしたので、まぁまぁ大丈夫だと思います。が、動作の保証まではできません。ごめんなさい。
- UTF-8 で "(" を含んだ文字列
が入っていると、ant の cobertura-report タスクで以下のようなエラーを吐いて死ぬ。超困る。何とかせねば。
TokenMgrError: Lexical error at line 17, column 38. Encountered: "\r" (13), after : "\"\u8b41\uff70\u7e3a\u52b1\uff1e\ufffd\ufffd.equals(arg) ) {" [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParserTokenManager.getNextToken(JavaParserTokenManager.java:2078) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.jj_scan_token(JavaParser.java:10181) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.jj_3R_198(JavaParser.java:8524) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.jj_3R_178(JavaParser.java:8924) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.jj_3R_151(JavaParser.java:8901) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.jj_3R_102(JavaParser.java:8960) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.jj_3_25(JavaParser.java:9977) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.jj_2_25(JavaParser.java:5999) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.Expression(JavaParser.java:2762) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.IfStatement(JavaParser.java:4251) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.Statement(JavaParser.java:3816) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.BlockStatement(JavaParser.java:3997) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.Block(JavaParser.java:3947) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.MethodDeclaration(JavaParser.java:2039) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.ClassBodyDeclaration(JavaParser.java:1082) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.ClassBody(JavaParser.java:941) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.UnmodifiedClassDeclaration(JavaParser.java:854) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.ClassDeclaration(JavaParser.java:761) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.TypeDeclaration(JavaParser.java:608) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.CompilationUnit(JavaParser.java:353) [cobertura-report] at net.sourceforge.cobertura.javancss.parser.JavaParser.parse(JavaParser.java:137) [cobertura-report] at net.sourceforge.cobertura.javancss.Javancss._measureSource(Javancss.java:256) [cobertura-report] at net.sourceforge.cobertura.javancss.Javancss._measureRoot(Javancss.java:339) [cobertura-report] at net.sourceforge.cobertura.javancss.Javancss.<init>(Javancss.java:419) [cobertura-report] at net.sourceforge.cobertura.reporting.ComplexityCalculator.getAccumlatedCCNForSource(ComplexityCalculator.java:104) [cobertura-report] at net.sourceforge.cobertura.reporting.ComplexityCalculator.getAccumlatedCCNForSingleFile(ComplexityCalculator.java:141) [cobertura-report] at net.sourceforge.cobertura.reporting.ComplexityCalculator.getCCNForSourceFileNameInternal(ComplexityCalculator.java:226) [cobertura-report] at net.sourceforge.cobertura.reporting.ComplexityCalculator.getCCNForPackageInternal(ComplexityCalculator.java:196) [cobertura-report] at net.sourceforge.cobertura.reporting.ComplexityCalculator.getCCNForProject(ComplexityCalculator.java:166) [cobertura-report] at net.sourceforge.cobertura.reporting.html.HTMLReport.generateTableRowForTotal(HTMLReport.java:704) [cobertura-report] at net.sourceforge.cobertura.reporting.html.HTMLReport.generateOverview(HTMLReport.java:336) [cobertura-report] at net.sourceforge.cobertura.reporting.html.HTMLReport.generateOverviews(HTMLReport.java:271) [cobertura-report] at net.sourceforge.cobertura.reporting.html.HTMLReport.<init>(HTMLReport.java:96) [cobertura-report] at net.sourceforge.cobertura.reporting.Main.parseArguments(Main.java:107) [cobertura-report] at net.sourceforge.cobertura.reporting.Main.main(Main.java:176) [cobertura-report] WARN getAccumlatedCCNForSource, JavaNCSS got an error while parsing the java file D:\Development\_Projects\cobertura-test\.\src\local\my\project\HelloWorld.java [cobertura-report] TokenMgrError in STDIN [cobertura-report] Lexical error at line 17, column 38. Encountered: "\r" (13), after : "\"\u8b41\uff70\u7e3a\u52b1\uff1e\ufffd\ufffd.equals(arg) ) {"
調査
Cobertura をビルドできるようにする
> ant jar
で JAR作成してくれる。
やばそうなところを見つける
スタックトレースから追いかけるだけの簡単なお仕事。
最初は net.sourceforge.cobertura.javancss.parser.JavaParserTokenManager.getNextToken
辺りを見ていたんだけど、さっぱり分からないので、呼び出し元を見ながら分かりそうなところまで行ってみる。
で、分かったこと。
- net.sourceforge.cobertura.javancss.Javancss はエンコーディングの設定ができる
- が、Javancss を使っている net.sourceforge.cobertura.reporting.ComplexityCalculator.getAccumlatedCCNForSource がエンコーディングの設定をしていない
private Complexity getAccumlatedCCNForSource(String sourceFileName, Source source) { if (source == null) { return ZERO_COMPLEXITY; } if (!sourceFileName.endsWith(".java")) { return ZERO_COMPLEXITY; } Javancss javancss = new Javancss(source.getInputStream()); /* ここだ! */
コードを修正する
Ant の cobertura-report タスクにある encoding プロパティ?を使えるように修正してみる。
net.sourceforge.cobertura.reporting.Main.java
103: ComplexityCalculator complexity = new ComplexityCalculator(finder);
104: complexity.setEncoding(encoding);
net.sourceforge.cobertura.reporting.ComplexityCalculator
67: private Map packageCNNCache = new HashMap();
68:
69: private String encoding;
95: private Complexity getAccumlatedCCNForSource(String sourceFileName, Source source) { 96: if (source == null) 97: { 98: return ZERO_COMPLEXITY; 99: } 100: if (!sourceFileName.endsWith(".java")) 101: { 102: return ZERO_COMPLEXITY; 103: } 104: Javancss javancss = new Javancss(source.getInputStream(), encoding);
245: public void setEncoding(String encoding) { 246: this.encoding = encoding; 247: }
net.sourceforge.cobertura.javancss.Javancss
413: public Javancss(InputStream isJavaSource_, String encoding) { 414: Util.debug( "Javancss.(InputStream).sJavaSourceFile_: " + isJavaSource_ ); 415: _sErrorMessage = null; 416: _vJavaSourceFiles = null; 417: this.encoding = encoding;
ビルドして実験
無事カバレッジが作成された!!
気の迷いかもしれないので、ソースを元に戻して再度挑戦。大丈夫そう。
テストを通す
せっかく作ってくれているテストは通しておきたい…。
> ant
net.sourceforge.cobertura.test.SwitchFunctionalTest のテストが1つこけるけども、これは修正前もこけてたのでよしとする。
Agile2011 でやってみること
きいてみる
やってる人に聞いてみたいことをまとめてみる。
でも、この質問、英語にしなきゃいけないし、回答も英語なんだよな…。
- スクラムやってる?
- Done の定義 Story/Sprint/Release のように分けて決めてる?
- Story と Sprint 分けてる理由って?
- Sprint の Done が達成できなかったらどうしてる?
- ScrumAlliance の Doneの定義書いた人に決めた経緯とか聞きたい
- あれをベースにこれって何故こうしてるんだと思う?って話をしてみたい
- 英語力ないとまず無理だな…orz
- スプリント0 で何してる? 期間はどのくらい?
- なぜそうしたの?
- P.O. は受け入れテストどうしてる?
- 自動化はやっぱりチームが手伝ってる?
- ドキュメントどれくらい書いてる?
- ReleaseSprint でドキュメント清書したりしてる?
- 清書するとしたら、Sprint でどれくらい書いてる?
- P.O. はどうやって要件を管理してるの?
- 全てを PBL で管理してる? それとも製品の要件は別文書として管理してる?
最終的には自分で決めなきゃいけないことは分かってるんだけど
やっぱり聞いてみたいのよね。
OpenJam でカンバンでも作って座ってたら、あっちから来てくれるかな?
そしたら話しかける勇気なくても大丈夫…。
他
- @sandayuu さんのオブ脳セッションに参加する(手伝いできればいいけど)
- AgileSamurai を買ってたら作者さんにお礼をいう(今やっていることのヒントになるはずだから)
AgileScout - 大規模開発にアジャイルを適用する際の10のTIPS
10 Tips for Agile Adoption in the Enterprise | Agile Scout より。
英語力は地を這っている人なので、中身についてはあまり信用しないように。
1. マネジメント層を取り込め
彼らを引き込まないと、ウォーターフォール向きのレポートに変換する作業がついて回る。
2. 全リリースの計画をしろ、1スプリントだけじゃない
ストーリーポイントで見積もり、ベロシティを予測し、スプリント計画を立てよう。
でも、最初に立てた計画にガチガチに縛られないよう注意。
3. スプリントを計画しろ、特別、共有なリソースのことを念頭において
遠い未来の計画に縛られる必要はないけれど、計画は立てよう。
4. 複雑な内部の依存関係は現実に起こる、それに対処せよ
リリース計画でモジュールやコンポーネントの依存関係を特定しておこう。スプリントゼロってやつ。
スプリントゼロでは全般的なアーキテクチャの要件?を設計しておく。スプリントのオーバーヘッドとならないように。
5. 妥当なスプリントの長さを継続せよ、3〜4週間
本格的なシステム統合をするには2週間では短い。
3〜4週間の長さを継続する。完全なサービスか、統合を開発し、テストできるように。
6. すべてのスプリントの成果物が製品になるとは思うな
初期のスプリントではEnd-to-Endでのシステム統合を準備することになる。
7. DONEを定義せよ、全てのチームにおいて首尾一貫しているDONEだ
8. 少なくとも2スプリントを安定化のために用意せよ
バグ出しや、システムテスト、デプロイのためのタスクの片付けとか。