Selenium RC のテストを別環境で動かすときの注意点
Selenium を使った自動テストを今までは主に Windows 上で実行していましたが、最近 X Window が乗った Linux 上で実行させる機会がありました。いくつかひっかかったポイントがあったので記録しておきます。
なお、僕の場合、テスト作成〜実行までの基本的な流れは以下のような感じです。
- Windows 上で Firefox と Selenium IDE を使ってテストケースの主要な部分を録画・作成
- Selenium IDE でそれを Perl にエクスポートし、Test::WWW::Selenium を Selenium RC のクライアントドライバーとして利用したスクリプトを作成
- 上の Perl スクリプトを Linux 上で実行して、Windows 上で起動している Selenium RC サーバをキックし、その Windows の Firefox でテストを流す
今回は最後の、Perl スクリプトを実行する Linux と、 Windows 上の RC Server + Firefox でのテストの部分を、別の X Window 付 Linux に置き換えた場合の問題について記述しています。
テストが終了しても Firefox が閉じない
Windows 上の Selenium RC Server + Firefox でテストを実行した場合は、テストケースの終了と同時に勝手に Firefox が終了していたのですが、 Linux の X Window 上で実行すると Firefox が開いたままになりました。
解決は単純に、テストケース中で start と stop を明記すればよいようです。
use Test::WWW::Selenium; my $sel = Test::WWW::Selenium->new{ (省略) }; $sel->start; ・・・テスト内容・・・ $sel->stop;
全角チルダ/波ダッシュ問題
Windows で作成したテストケース中で、表示されたウェブページ内の文字列の確認(VerifyText 等)を行っている場合、いわゆる「全角チルダ」「波ダッシュ」の問題にひっかかる可能性があります。
$sel->is_text_present("1件〜30件"); |perl|< たとえば、上のように「1件〜30件」という文字列が表示されていることを確認しようとした場合、「〜」の部分を波ダッシュ(U+301C)として文字マッチしてほしいのに、Windows上で作成したテストケース中では全角チルダ(U+FF5E)として保存されてるために文字マッチしないようです。Linux 上で、テストケースを作成すれば、本来の波ダッシュ(U+301C) として保存されるので、期待通りマッチします。 参考リンク: - http://www.informe.co.jp/useful/character/character14.html - http://ja.wikipedia.org/wiki/Unicode#.E6.97.A5.E6.9C.AC.E8.AA.9E.E7.92.B0.E5.A2.83.E3.81.A7.E3.81.AEUnicode.E3.81.AE.E8.AB.B8.E5.95.8F.E9.A1.8C - http://ja.wikipedia.org/wiki/%E6%B3%A2%E3%83%80%E3%83%83%E3%82%B7%E3%83%A5#Unicode.E3.81.AB.E9.96.A2.E9.80.A3.E3.81.99.E3.82.8B.E5.95.8F.E9.A1.8C ** (おまけ) マルチバイト文字列の扱い これは Selenium とは関係ない Perl の問題で、日本語文字列などの扱いに注意が必要かもしれません。(特に、別ファイルから文字列を読み込んだりしている場合など) 個人的に今回ひっかかった問題は、YAMLファイルから一部のロケーター(例: "//input[@value='ログイン']")を読むようにしていた箇所で、最初は以下のように記述していました。 >|perl| use YAML qw(LoadFile); my $config = LoadFile("config.yaml"); decode("utf8", $config->{login_button_locator}); $sel->click_ok($config->{login_button_locator});
最初手元の環境で利用していた YAML モジュールの古いバージョン(0.66) ではこれで動作していたのですが、新しいバージョン(0.72) が使える環境にもっていくと以下のエラーで落ちます。
Cannot decode string with wide characters at /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/Encode.pm line 174.
最初原因が分からなかったのですが、YAML の LoadFile の中を見ると新しいバージョンでは
binmode $IN, ':utf8';
として、デコードしてくれるようになったからのようで、すでにデコードされて utf8 フラグが立ってる文字列をさらにデコードしようとしてエラーになっていた模様。
今回の件はだいぶニッチな感じでしたが、 Perl やCPANモジュールのバージョンが異なる環境で動かす場合は、このあたりのエンコードのタイミングなども注意が必要なことが、実装の仕方によってはあるかもしれません。
Windows上の Emacs 日本語入力セットアップ(DDSKK のインストール)
ここではQAエンジニアと言いつつ、数ヶ月前から現在の所属は一介の開発エンジニアなので開発も多少するわけですが、コーディングには基本的に Emacs を使ってます。使ってるだけ。使いこなせてません。
少し前に出た WEB+DB PRESS Vol.58 に載っていた Emacs 特集記事が、僕のように昔に少し使ったっきりで再入門したい者にはうってつけでした。基本的にこちらの記事に習って、Windows 上の Emacs を 少しずつ設定していってます。
昔は Windows だと Meadow とか入れたものですが、最近は本家 GNU Emacs のWindows版でも違和感なく使えるようになっていてすばらしい。とは言え、日本語入力についてはそのままでは少し難があり、特にカタカナ語がふつうの変換で出てこないのが僕にとっては大きな問題でした。今回ようやくその対処として、DDSKK をインストールしてみたのでそのときのメモ。
上の WEB+DB の記事をベースに設定していたので、同じ作者さんによる
http://d.hatena.ne.jp/tomoya/20100905/1283681474
をベースとして参照しつつ、Windows 固有の部分は
http://d.hatena.ne.jp/andjuny/20090928/1254144096
なども参考にさせていただきつつ。
導入手順
APEL、DDSKK をダウンロードして解凍
APEL のインストール
APEL の makeit.bat にて、変数設定部分を下のように:
set PREFIX=%HOME%\.emacs.d set EMACS=c:\bin\emacs-23.2\bin\emacs.exe set LISPDIR=%PREFIX%\elisp set INFODIR=%PREFIX%\info set VERSION_SPECIFIC_LISPDIR=%PREFIX%\elisp set DEFAULT_MAKE_ARG=
その上で、
C:\bin\apel-10.8> makeit.bat elc C:\bin\apel-10.8> makeit.bat what-where C:\bin\apel-10.8> makeit.bat install
makeit.bat の部分については以下も参照:
http://www4.kcn.ne.jp/~boochang/emacs/apel.html
SKKのインストール
こちらの makeit.bat も上と同様に設定。
DDSKK の SKK-CFG にて、以下の設定:
(add-to-list 'load-path "c:/Users/xxxxx/.emacs.d/elisp/emu") (add-to-list 'load-path "c:/Users/xxxxx/.emacs.d/elisp/apel") (setq APEL_DIR "c:/Users/xxxxx/.emacs.d/elisp/apel") (setq EMU_DIR "c:/Users/xxxxx/.emacs.d/elisp/emu") (setq SKK_DATADIR "c:/Users/xxxxx/.emacs.d/etc/skk") (setq SKK_INFODIR "c:/Users/xxxxx/.emacs.d/info") (setq SKK_LISPDIR "c:/Users/xxxxx/.emacs.d/elisp/skk") (setq SKK_SET_JISYO t)
とした上で
C:\bin\apel-10.8> makeit.bat install
.init.el、.skk の設定
設定は
http://sheephead.homelinux.org/2010/06/18/1894/
を大いに参考にさせていただきました。コピペともいう。
まだ内容もちゃんと把握していないので、このあたりは使いながら必要に応じてカスタマイズしようと思います。
操作
Ctrl+x j で SKK モードに切り替え。
アルファベットの大文字から始めるつもりで K a n j i などと入力すると、変換候補が出てくる。
まだマニュアル読んでるところ。
http://openlab.ring.gr.jp/skk/skk-manual/skk-manual-ja.html
ちょっと慣れは必要そうですが、使いながら馴染んでいこうと思います。
jQuery の .text() で取得した文字列で文字マッチする場合の注意
数ヶ月前にQAエンジニアから開発エンジニアに転身したので、最近はコード読み書きしてます。
今日は珍しく、IE で意図したとおりに動くのに Firefox で動かない、という JavaScript の現象に遭遇。
以下のようなHTMLがあるのに対し
<td id="hoge1"> <div> <div></div> <span> <input name="title_name" value="【ほげ】もげ" type="hidden">【ほげ】もげ </span> </div> </td>
以下のような jQuery を使った JavaScipt で TD 内のテキストが【ほげ】で始まるかどうか判定しようとすると
var isThisHoge = $("#hoge1").filter(function() { return /^【ほげ】/.test($(this).text()); }); if (isThisHoge.length) { // この場合の処理 }
Firefox などの場合、text() で返される文字列の前後に改行などが入ってしまい、期待する文字列(【ほげ】)が先頭に来ないという状態になりました。
なので、以下のようにしてあらかじめ先頭の改行・空白等を除去してやればOKでした。
var isThisHoge = $("#hoge1").filter(function() { var titleText = $(this).text().replace(/^\s+/, ''); return /^【ほげ】/.test(titleText); }); if (isThisHoge.length) { // この場合の処理 }
.text() が、エレメント内のテキストすべてを返すということを考えれば、IEで動いたのはむしろたまたまで、必ず空白除去するようにするのが筋なのかも。
ちなみに、.replace() が置換後の文字列を返すというのを忘れて、なんで置換されないのだろう。。。とかなりの時間悩んだことは秘密です。。
読書メモ『実践アジャイルテスト』
書籍「実践アジャイルテスト」について、実際に読んだのはだいぶ前で、今年(2010年)3月下旬から5月にかけて、読書会に参加してたのですが、まとめを何も書いてなかったので今更ながら僕なりの読書メモを公開します。
本の概要
いわゆるアジャイル開発プロセスの中で、テスト担当者・QAエンジニアの役割や、テストがどう設計され、実施されるべきかを網羅的に説明した内容です。アジャイル関連の本はいくつも出ていますが、その中でテストをメインテーマに据えたものは和訳本ではこれがはじめてなのではないかと思います。
前半はアジャイルプロセスやテストの概要など大枠の話が多いですが、後半にはいろんなレベル(単体、API、GUI)での自動テストや、反復の各フェーズで実施する作業内容など、だいぶ具体的なプラクティスまで説明されています。
ちなみに読書会は、自分含め 9 名の参加者で、(当然ながら)テストが主な仕事の方が多かったですが、開発寄りの方もいらっしゃいました。各章ごとに担当者が割り当てられ、自分の担当分の簡単なまとめを発表してその後その内容についてディスカッションする、というスタイルで、計5回ほどで1冊分を終えました。
個人的に学んだこと
僕が個人的にこの本で得たポイントは主に以下の3点です。
- テストの4象限という分類の仕方
- 自動テストのピラミッド
- GUIレベルのテストはキーワード駆動&データ駆動で
アジャイルテストの4象限
開発プロセスの中では、様々な目的に応じていろんな種類のテストを実施する必要があります。ともすればその多さに圧倒されてしまいそうになりますが、この本で紹介されていた「4象限」による分類は各種テストをうまくカテゴライズしていました。
「技術面」と「ビジネス面」という視点の違いと、「チームを支援する」ことと「製品を批評する」ことという目的の違いによる2軸で4つの象限に分類されています。4隅の雲は、それぞれのテストが主にどういう手段によって行われるべきものであるかを示しています。
第1象限は「技術を用いたチームを支援するテスト」となり、ユニットテストによるテスト駆動開発(TDD)が中心となります。主に内部品質に関わるものであり、ソースコードのバージョン管理や開発環境(IDE)やビルド自動化ツールなどもこの範疇で取り上げられています。
ちなみに読書会では、「テスターが単体テストを書くことを避けるべきです」(p119)とある点が議論になりました。TDDは設計活動だからコードを書く開発者が行うべきであるというのが本にある理由ですが、いかなる場合もそうなのかというのがいまいち釈然としない雰囲気でした。おそらく、TDDという観点からは本書の主張が正当なのでしょうが、コードが作成されてしまった後で、カバー率の足りてない部分を強化するためにテストを追加することなどは間違っていないように思います。
第2象限は、第1象限が技術寄りだったのに対し、もっとビジネス寄りの視点に立ったものを含みます。機能テストなどのほか、プロトタイプやシミュレーション、例など、仕様や画面設計など開発を進めるための活動も一種のテストとしてここで扱うようです。また、ここでのテストは、ビジネス担当者が理解できる言語や様式で記述され、その多くは自動化されるべきであるとされています。
Fit、 Selenium、 Watir や Canoo WebTest などによる自動化テストはこの領域に含まれます。読書会のメンバーからは Cucumber というものも挙げられていました。また、ウェブサービスのテストのためのツールとして、CrossCheck、Ruby Test::Unit、soapUI が紹介されています。(pp167 - pp175)
第3象限は、引き続きビジネス視点に立ったものでありつつ、製品を批評(critique)するためという目的に転じます。このセクションの説明の最初にデモを行うことの重要性について触れられていること(p191)からも、ここでは基本的にできたものに対して行うテストが中心であると読めます。第1、第2象限で自動化の側面が強かったのに対し、ここでは手動での、人間にしかできないテストが強調されています。
探索的テストやユーザビリティテストが特に話題の中心にあり、探索的テストの具体的な実践手法として「セッションベーステスト」が紹介されています。探索的テストが捉えようによってはただの闇雲なテストになりがちなのに対し、限られた時間枠内で実施する、統一されたフォーマットで結果を残すなどの枠に沿って実施することで、直感を活かしつつテスト対象についての不具合情報を効率的に収集するための手法のようです。*1
第4象限は、パフォーマンステストやセキュリティテストなど、基本的に技術を用いなければ実施が難しい製品に対するテストです。パフォーマンス、スタビリティ、拡張性、信頼性テストをまとめてPSRテスト(performance, stability, scalability, reliability)と言ったりするそうです(p221)。
ちなみにこれらの第1〜第4の象限は必ずしも実施の順番を示しているわけではなく、この第4象限にあたるパフォーマンステストなども単体レベルのテストや開発初期から考慮するべきであるとも言われています(p223 など)。
本書ではこの4象限のマトリクスを、「地図」として用いることを勧めています。テストおよび開発の中で各種テストがどれだけ適切に行われているかといったプロファイリングや、これから取り組むプロジェクトやスプリントの中で、どこに注力していくか、あるいはどこを省いてよいのかといった戦略を立てるためのチェックリストとして使うのがよさそうです。また、実施している、あるいは実施しようとしているテストが、何のためのテストなのか?ということを確認するためにもこれを参照すれば、チームや組織内での共通認識がとりやすいのではないかと思います。
テスト自動化のピラミッド
これはテストの自動化戦略の中で示されていた図です。基礎に単体テストが最も大きな割合を持つ土台としてあり、より大きな機能の集合を対象とするテストがその上に乗るかたちになっています。つまり、自動テストを支えるのはまず単体テストであるということです。これは僕にとっても少し意外で、実際に本書でも、いわゆる「V字モデル」とは反対であるため、「多くのチームがこの考え方と格闘することになる」と述べています。(p274)
「通常は前のプロジェクトから引き継がれたことによって逆さまになります」(p276)とあるのを読んで、実際自分のチームも最も上位のGUIレベルの自動テストが肥大化した状態だったため、やはり現実はそうである場合が多いようです。しかし、「3匹の子豚」の比喩で、ピラミッドの下から「レンガ」「枝」「藁」と例えられているように、最上位のGUIレベルのテストはちょっとした画面変更などによって容易に崩れる上、実行にも時間がかかるため高速なフィードバックを提供することができません。藁や枝は比較的積みやすいですが、レンガを積み上げるのは学習コストも高いので初期コストがかかりますが、スキルがつけば最も高速で大量に実行できるため、なるべく多くのテストをこのレベルに積み込むべきであるとされています。
ただし、すべてが単体レベルの自動テストで済むわけではなく、上位レベルのテストや、さらに上の手動テストももちろん必要です。このあたりは、上の4象限での話と同様で、目的が異なるものについては相互補完的にそれぞれ実施されるべきであると解釈できます。
キーワード駆動テスト、データ駆動テスト
もうひとつはちょっと細かい点ですが、ちょうど個人的に抱えていた問題とはまったことで、先の第2象限での自動テストの中で述べられていたキーワード駆動テストおよびデータ駆動テストというものについてです。(p182)
データ駆動については単体テストの文脈で聞いたことはありました。投入するテストデータのバリエーションを増やすだけでケースが増やせるよう、ロジック(主にループ処理を行う)とデータを分離するものであると理解しています。
キーワード駆動という単語はこの本ではじめて知りました。キーワード、あるいはアクションワードを決め、それに対応する処理を定義するスタイルのようです。たとえば、Signup や Signoff というキーワードを決め、引数としてログインIDなどを渡せるようにしておくことで、自然言語に近いスタイルでテストが記述されるようになります。
個人的にはこれらの手法はテストの再利用性のために重要だと感じました。これまで Selenium で自社製品のテストを作ってきましたが、それらの大部分はただ録画したものを順番に実行するだけです。当初は、学習コストが少ないため、たとえばアルバイトたちにも同じスタイルでテストを追加してもらえたし、逆にプログラムを書いて複雑化するとメンテや作成が余計大変になってしまうと思っていました。
しかしテストが増えていくにつれて、メンテナンスやテストの可読性がかえって膨大になっていくのを実感しました。
GUIレベルのテストでは画面仕様が変わると大きな影響を受けてしまいます。そのたびに、その画面が出てくる個所をすべて置換してまわらなければならないのはかなりのコストです。単純な文字の置換であれば一斉置換で済みますが、変更内容によっては手作業でやらざるを得ない場合もあります。また、テストの記述が冗長なため、テストケースを見て内容を追うのが大変で、自分が作ったテスト以外は読む労力が半端ではありません。新しくメンバーが入った場合などは特にそうです。
キーワードによって処理が一段抽象化されていれば、キーワードとデータを並べていくだけでテストが作成できるようになるため、可読性もメンテナンス性も向上します。このあたりについては、現在取り組み中のところもあるので、別の場で詳しく書いてみたいと思います。
読書会で出た話題
以上、3点だけピックアップしましたが、他にもこの本ではアジャイル開発やその中でのテストの実践方法について、多くの示唆が得られます。少なくとも自分にとっては、仕事で行っているWEBアプリケーションのテストとリンクする部分が多く、極めて実際的であると思いました。
以下ではおまけで、読書会の中ででたいくつかの話題について、自分のメモからログを残しておきます。雰囲気だけでも伝わればと。
顧客って?
- 顧客とのコミュニケーションや協業が強調されている
- しかし(いわゆる)ウェブサービスや B2C なサービスの場合、顧客って?
- 顧客と作業する、といわれても。。
- 顧客が明確な場合でも、参加したがらない?労力が増えるのをいやがる
- 「顧客チーム」って何?そんなの皆さんの会社にありますか?
- 企画部門が近いかも
どこまでテストするか
- 即座にリリースすることが重要な場合ある
- 海外とか、バグがあってもどんどん出してくる。初期のFacebookとか
- 品質基準が違う?
- セキュリティバグはそれでいいのか?
バーンダウンチャートを前にして進捗はなす
- プロダクトオーナー/顧客/管理者に話すときにもそれを前提に
- 手書きがおすすめ。積み上げなきゃいけないときのプレッシャーがハンパない
テストケースレビューってしてる?どうやるのがいい?
*1:http://www.satisfice.com/sbtm/index.shtml に情報あり
楽天テクノロジーカンファレンス2009
行ってきました。とりあえず、個人的なメモ(数字とかキーワード)だけ。
- (追記:2009-10-27) よりよいまとめ/詳細なログが下記などにあるので、そちら参照されたほうがきっとよいです。
- http://d.hatena.ne.jp/suno88/20091024/1256337834
- エラー管理アプリは Haptoad じゃなくて、Hoptoad だった。。検索して出ないわけです
楽天
概要的な話
- サーバ数千台
- 開発拠点、全国に(5, 6カ所?)
- スーパーDB (集約された顧客DB)
- 流通総額 6638億/年
- 4700万会員
開発
- 100以上ドメイン(プロダクト?)
- 900インスタンス
- 店舗ごとに同じ構造のスキーム(64/server)
- KPI
- 流通総額 1兆円
- コスト/流通総額 1%以下
- 稼働率 99.95%
- 3兆円規模でも耐えられるように目指す
- ピーク時トランザクション(買い物)は 1000件/分
- ブランチはけっこう別れる
- みんな(数十人?)が一室に集まってメンテナンスリリース
- 新たな試み
- HTTPセッション使わない
- Stateful なWebAPIで、Stateless な買い物かごの実現
- KeyValueStore &非同期処理で、脱RDBMS
- HTTPセッション使わない
- エンジニア: 全2000人のうちの 3分の1 くらい(600人超?)
- プロデューサーとか含む
- "プロジニア" = プロデューサー兼プログラマ
クックパッド
- 月間770万ユーザによるアクセス
- 3.5億PV
- 16-18時、秋-バレンタイン がアクセス多い傾向
- 各"キャスト"の欲求を要件化
- 3つの期間「設計」「開発」「質の向上」
- 公開前にアナウンスしない、不安いだかせるだけ
- 機能を説明しない(直感で使えるようじゃないとダメ)
- 値段をつける(金払う価値があるレベルじゃないと無料でも使ってもらえない)
LinkShare
アジャイル
- 10日1スプリント
- フェーズ毎(開発、テスト等)に担当者の名前書いた付箋
- 不具合も付箋に書いてはる
- Lessons Learned (スプリントのふりかえり)
- Try, Keep, Problem 的なものを、色分けして各人が付箋はる→みんなで投票
- Waterfall 型の頃は初期にバグの30%しか発見できてなかった
楽天USA
- チーム構成: 4 Developer, 2 QA, 1 GUI designer, 1 DBA
- 指標大事
- コード:テストコード = 1:1.3
- QA用サーバ on AmazonEC2 (3台: trunk, branch, release)
- Capistorano (Deploy Tool)
- New Relic RPM (モニタリングツール)
- リリース毎のパフォーマンスチェック、視覚化
Haptoadhoptoad アプリエラー処理アプリ
Bugzilla用Mechanizeスクリプトの更新
Bugzilla が3系になったのに伴い、WWW::Mechanize で Bugzilla にアクセスして検索結果の数を取得していたスクリプトを修正していたところ、いろんなとこにちょっとずつはまって、思いのほか時間がかかりました。。
まずひとつは、バグジラの設定。
User Agent (ブラウザ)が Accept-language を明示していない場合、バグジラの「パラメータ」の「Localization」の「defaultlanguage」に設定してある言語によってページが表示されます。しかしここに、「languages」と同様に「ja,en」と複数指定しまっているとバグジラがエラーになります。デフォルトなんだから、ひとつでいいよね、という話。
Accept-language を持たない Mechanize でアクセスしたことで初めて発覚した設定ミス。
次に、文字コード関連。
3系になってバグジラは全体的にUTF8になったわけですが、このページを Mechanize (ないしはLWP)で取得した場合、そのコンテンツはUTF8だけどUTF8フラグが立たないようで、以下のように明示的にUTF8フラグをONにすると日本語による正規表現の文字マッチもできるようになりました。
utf8::decode($mech->content)
さらにもうひとつ、一部のリクエストに対するレスポンスの結果が「500 Line too long (limit is 4096)」となってしまいました。調べたところ、レスポンスのヘッダに長すぎるものがあるようで、以下のようにすれば回避できました。
my $mech = WWW::Mechanize->new(); { no warnings 'once'; push(@LWP::Protocol::http::EXTRA_SOCK_OPTS, MaxLineLength => 16*1024); }