Hatena::ブログ(Diary)

Yamashiro0217の日記 RSSフィード

2011-12-01

居酒屋北海道から割引券がもらえるまで頑張るブログ

00:33 |  居酒屋北海道から割引券がもらえるまで頑張るブログを含むブックマーク


- 12/10 北海道恵比寿店30人規模 scala会議懇親会 (予定)2日連続北海道は辛かったので回避

2011-11-25

大規模懇親会を支える技術〜ドタキャン率と戦う方法〜

12:51 | 大規模懇親会を支える技術〜ドタキャン率と戦う方法〜を含むブックマーク


年の瀬ですね。そろそろ皆様忘年会とかを企画してるのではないでしょうか?

そこで、年間10回ぐらいは技術系の勉強会の飲み会の幹事をしている僕が大規模懇親会をどう設計しているかについて書いてみます。

人数の確定

人気のある勉強会ともなると参加人数は百人近くになります。
幹事が知らない人も沢山くるでしょう。

さてそのときに、どうやって懇親会・飲み会の人数を確定させるか。

確定しません。
ドタキャンとか当たり前にあります。
ドタキャンどころか無断欠席もザラです。

ドタキャン率(無断欠席も含む)は、
知り合いの数が少なければ少ないほど高く、
懇親会参加人数が多ければ多いほど高いのが僕の実感です。

つまり、知ってる人が少なくて懇親会参加人数が多いほどドタキャン率が高い。
僕の経験で一番ひどかったときは5割超えました。
あんときはさすがに辛かったですね。ははは。

じゃあどうするか。次へ進む。

店選び・予約

そのようなドタキャン率の場合にどう店を選ぶか。
選択肢は限られます。

1.貸切
2.広い店を探す

1.はハードルが高いのであまりやったことがないのであまり助言はできませんが、
貸切にすれば割りと融通は効くのではないでしょうか。
でも貸切は少し割高になりそうなイメージがありますね。店との交渉次第でしょうか

僕は2.をよく使っています。
というか居酒屋北海道を使っています。
居酒屋北海道新宿渋谷池袋などなど、都区内の主要な駅にあることと、
とにかく広い店ばかりだからです。


じゃあ広い居酒屋を探したとして、どう予約を取るか。
ドタキャン率の見積もり具合によりますが、
例えば「参加予定人数80人だけど、ドタキャン結構でそう…」という場合を想定します。

この場合、店には「あのーだいたい席を50〜70ぐらいでとってもらって、料理は50人分確定で、飲み放題はきた人数分で。人数沢山きたら料理はアラカルトで追加します」
で予約します。
こういう予約が無理な店はパスしましょう。

最初の注文

何十人とかでお店に入ると、最初の注文が大事です。

なぜならみんなに好き勝手に注文されてしまうと、全員の注文が揃うまで乾杯ができない。
乾杯ができないとなんか手持ち無沙汰でだるい。

また、たいてい、居酒屋では2時間制だったりするので時間が無駄ですね。

なので、最初の注文だけは幹事が決めてしまいましょう。
ビールピッチャーで各テーブルに2個。あと烏龍茶もピッチャーで各テーブルに」と注文し、
参加者には「最初の乾杯だけは、ビール烏龍茶でお願いします。そのあとは自由に」がいいと思います。
参加者のメンツによっては僕はこれにプラスしてコーラのピッチャーを頼みます。

会計

さて、大事な大事な会計です。

会計も沢山人数いるとだるいですね。
だるいので、僕は、各テーブルで分散会計集計システムを利用することが多いです。

「一人5000円なので、各テーブルで誰か責任もってあつめて僕まで持ってきて」
時代は分散ですよ!

また、コミュニティにもよりますが、最初の予算が4000円で、
ドタキャンが2〜3人でお金足りない!とかのときは、
「やましろからのお願いをお読みください」的な寄付を一口500円とかで募るとかもいいですね
みんな大人なので500円ぐらいはすぐ集まります。
集まらなかったとしたら、ちょっとそのコミュニティやばいんじゃないですかね

さいごに

居酒屋北海道のかた、こんだけ宣伝したので割引券ください!
(ちなみに今月も50人規模の飲み会北海道でやったし、来月は、50人規模 * 2 やります!)

ともきらともきら 2011/12/05 06:00 私も100名規模の飲み会を定期的にしています。ドタキャン苦労、他人事に思えません。半数ドタキャン苦労自分の経験にもあり笑えない、、、次回130超えるので北海道当たってみます。ありがとう。あと新宿ならティキティキも融通効きますよ( ^ ^ )/□

ともきらともきら 2011/12/05 06:00 私も100名規模の飲み会を定期的にしています。ドタキャン苦労、他人事に思えません。半数ドタキャン苦労自分の経験にもあり笑えない、、、次回130超えるので北海道当たってみます。ありがとう。あと新宿ならティキティキも融通効きますよ( ^ ^ )/□

Yamashiro0217Yamashiro0217 2011/12/07 00:16 130とかすごいですねー。
北海道も店舗によりますが150〜200とかぐらいの規模なので貸切かもですねー。

ティキティキ覚えましたー。
ありがとうございます!

2011-10-14

賃貸マンションで絶対に損な入居をしない方法が確立されてしまった

| 14:54 | 賃貸マンションで絶対に損な入居をしない方法が確立されてしまったを含むブックマーク

あわせて読みたい
中古マンションで絶対に損
な買い物をしない方法が確立されてしまった


タイトルは釣りです。


賃貸物件を探すときには、インターネットを使いこなしている皆様は、物件サイトを検索し、自分が住みたいと思っている地域の相場などを把握しつつお探しのことでしょう。
物件サイトに載っている情報は当然現時点での情報です。過去の情報はわかりません。


そんなわからない過去の情報が見える、賃貸物件サイト業界的にかなり画期的なサイトがあるので紹介してみます。
その名もHOME'S不動産アーカイブ


HOME'S に掲載された過去の物件の情報が見えます。
これによって何が嬉しいか。

  • 新しい物件を探すとき

住みたい物件の退去率がわかる。
物件の規模に関して掲載情報が沢山あるということは退去率が高い。何か問題があるのではないか?

住みたい物件の時系列での家賃がわかる。
どんどん下がってたりするなら、家賃交渉に使える。

住みたい物件の周りの時系列での家賃がわかる。
周りがどんどん下がってるのに住みたい物件は下がってないなら家賃交渉に使える。

  • 今住んでる物件に対して

新しい物件と一緒で時系列での家賃の変動が、住んでる物件、周りの物件で見える。
契約更新時などに、家賃交渉に使える


などなどがあります。
とりあえず面白い機能なので、自分の住んでるところを見てみるといいよ!

2011-09-21

Selenium2.x で Ajax なWebアプリケーションをテストしよう 〜 Facebook の自動あいさつ返答機能を実装 〜

23:03 |  Selenium2.x で Ajax なWebアプリケーションをテストしよう 〜 Facebook の自動あいさつ返答機能を実装 〜 を含むブックマーク


この記事では、Facebook のあいさつ(Poke)機能への返信を題材に、沢山たまるとウザい嬉しいあいさつを自動で返すスクリプトを書くことで、Selenium2.x の使い方、特に Ajax アプリをテストする方法について学べるようにする。
Ajax がからんだWebアプリケーションのエンドツーエンドの最近のテスト手法についてのまとめにもなっていると思う。

最初の3節ぐらいは「Seleniumとは〜」とか「テストってのはさー」とかゴタクをごたごた書いているので、Seleniumの実際のコード見た方がはえぇよ。って言う人はコードが出てくるまで記事を飛ばすと良い。

こんな記事を気合入れて書いて公開した当日に…FacebookUIが変わって…作ってたスクリプト動かなくなってしまった…orz。俺が何かやったり買うと事件が起きるんや・・「なにか買うとその直後に安くなったり、新機種がでたりするという都市伝説」は本当やった…http://theinterviews.jp/yamashiro/12194

明日か週末、スクリプトと記事の内容修正します…orz


Selenium とは

最初に Selenium とはなんなのか簡単に触れる。

Selenium とはWebアプリケーションのテストツールである。
ボタンをクリックする、リンクをクリックする、チェックボックスを選択する…などを表すスクリプトJavaRubyC#Pythonでかけます)を記述すると、それをSeleniumが解釈し、ブラウザを実際に立ち上げ、スクリプトに従ってSeleniumブラウザを操作し、Webアプリケーションが正しく動作してるかをテストすることができる。

以下のような特徴がある。

  • サポートしてるOSが豊富
  • サポートしてるブラウザが豊富
  • サポートしてる言語が豊富
  • Selenium IDE や Grid などといった周辺ツールが豊富

Ajax のテストの難しさ

Ajax(ここではjsでの通信だけでなく、DOM操作などひっくるめてAjaxと呼ぶ)をふんだんに使ってるようなアプリケーションは、XHRで通信を行うし、HTML も動的に変更される。
そのため、単純にはテストをすることが難しい。例えば、単純に Http通信をエミュレートするようなテストツールでは、Webアプリケーション全体をエンドツーエンドでテストしたとは言い難い。

Selenium は実際にブラウザを操作し、例えばボタンをクリックすると書いておけば、実際にブラウザ上で onclick のイベントが発生し、 JavaScript が動作するので、Ajaxのテスト、真にエンドツーエンドのテストができると言えよう。

ただし、Selenium といえどもコツはいる。例えば、「fooボタン」をクリックするとAjaxで通信が行われ、DOMが操作され、次なる「hogeボタン」が表示される。というアプリに対して

  • fooボタンをクリック
  • hogeボタンをクリック
  • hogeボタンを押した結果が正しいかをチェック

のようなテストシナリオを書いたとする。

このシナリオでは「fooボタン」をクリックしたあと、すぐに「hogeボタン」をクリックしようとしてしまうが、実際にはAjax通信したあとに、HTMLが生成され、それがDOMとして描画されないと「hogeボタン」をクリックすることができない。

なので、以下のようなシナリオである必要がある。

  • fooボタンをクリック
  • hogeボタンが表示されるのを待つ
  • hogeボタンをクリック
  • hogeボタンを押した結果が正しいかをチェック

"「hoge」ボタンが表示されるのを待つ" というシナリオが追加されている。

テストの価値について

結論から言おう。Seleniumのテストは書いてはいけない。"お前は何を言ってるんだ"って思った人たち。ちょっとゴタクにお付き合いください。

Seleniumのテストというのは書くのが大変で、かつ実行時間が長い、かつUIというのはすぐ変わる。自動テストというのは闇雲に書いていいものではない。より価値の高いテストを書くべきだ。
だから、基本はより価値の高い単体テストを先に充実させるべきだ。

モダンなWebアプリケーションフレームワークならば、擬似的にHttp通信の代わりとなるテストフレームワークが用意されいると思う。例えばRailsWicketCakePHPなどは、Controller層も含めてテストする仕組みが備わっている。単体テストの次はこれらのテストを書くべきだ。

また、JavaScript 側も、DOM操作などのブラウザ依存な部分を切り出し、非ブラウザ(DOM)依存の部分をQUnitなどのJavaScript単体テストを実行できるようにしておくべきだ。最近だと、node.js 上でテスト走らせるとかも流行りよねnodeunitとか、node + jasmine とか。

じゃあ Selenium はいつ書くべきなのか。

などに向いていると思われる。

余談だが、Seleniumみたいなブラウザ操作するテストは遅い。けど、JavaScriptがブザウザで動いてることとかもテストしたいという欲求を満たすために、HeadlessなテストをするPhantomJSのようなツールもある。
Headlessなテストができるツールとは、例えばWebKit のエンジンを使ってブラウザを直接起動しないんだけど、JavaScriptDOMなども含めたテストができるツールである。
マルチプラットフォーム、マルチブラウザがターゲットじゃなくてよければそういう解決もありだと思う。ただ、試してないけど、スクリプト書くの大変じゃないのかしら???

Selenium2.x とは

Selenium2.0 が 2011/07 にリリースされた。

今までのコードベースを大幅に変更し、WebDriverというAPI、実装を使った実装に変った。
WebDriver API を使うようになったので、テストのスクリプトコードも大きく変更されることになった。

リリースが最近のことなので、ネットを探してもあまり実際のテストスクリプトコードの情報が見当たらない。というのがこの記事を書いた動機の一つである。テストスクリプトの例を探すときは、「Selenium」でググる以外に「WebDriver」でググってもいいかもしれない。

Facebookのあいさつの返信機能をSeleniumから叩く

さて、本題。

Facebok のあいさつの返信機能を Selenium から叩いてみよう。
今回の例のコードはすべて github に上げてある。ちなみにスクリプトJava である。Rubyとかもそのうちかけたらかく。
https://github.com/yamashiro/facebook-poke

github がわかってる人はよろしく clone するなりなんなりするように。
わからない人は、上記 URL のページの「Downloads」から tar.gz なり zipダウンロードして解凍する由。
動作する jar ファイルが入ってるので、結構重い。

Facebookのあいさつ(Poke)機能とは

次のように、Facebookで人のプロフィールを見ているときに「あいさつする」という機能がある。
f:id:Yamashiro0217:20110921225818p:image


これを使うと、その人のページに、あいさつしてくれた人一覧が表示される。
f:id:Yamashiro0217:20110921225822p:image

「あいさつを返す」というリンクをクリックすると、あいさつを返すダイアログAjaxで開き、
f:id:Yamashiro0217:20110921225821p:image

「あいさつする」ボタンを押すと、あいさつが返され、結果ダイアログ一定時間表示される。
f:id:Yamashiro0217:20110921225819p:image

この操作をSelenium で実行する。

とりあえず実行

デフォルトFirefox で動かすようにしてあるので、Firefoxインストールおねがいしゃっす。
SeleniumIEChromeOperaなどにも対応してるがとりあえずFirefoxで動作させる。例えば Chrome のやり方はこちらを参照してコードをいじって欲しい。

Javaも必要なのでJavaインストールしておねがいしゃっす。

また、Facebookアカウントも必要です、また(必要なものばっかでサーセン)、これ結構大変かもしれませんが、誰かからあいさつされておいてください。
えっ。あいさつしてくれる友達いないって???複垢(ry

githubから手に入れたフォルダ(zipとかの人は解凍したフォルダ)の dist フォルダに移動してコマンドラインから以下を実行する。
この jar ファイルは maven が入っていれば mvn package で target 以下に生成することが可能であるので、独自処理とか足した場合は自分で package してほしい。

java -jar facebook-poke-1.0-jar-with-dependencies.jar -email <your_email> -password <your_password> 

あいさつ自体は実行したくないとかいう人は、

java -jar facebook-poke-1.0-jar-with-dependencies.jar -email <your_email> -password <your_password> -debug

というように-debugでデバッグモードで実行することで、あいさつの実行しないってことができるのでお試しあれぃ。

無事にブラウザが勝手に起動し、勝手にあれよあれよとメールアドレスの入力やパスワードの入力、あいさつが実行されれば成功である。

ソース解説

さて。本編中の本編。

Java のコードの全体は以下のURLにある。
https://github.com/yamashiro/facebook-poke/blob/master/src/main/java/example/selenium/facebook/pokes/PokesEraser.java

ソースを実行すると、main からよしなに pokesErase というメソッドが呼ばれる

	public void pokesErase(String email, String password,
			PokeStorategy pokeStorategy, AccountLaungage accountLaungage,
			DriverStorategy driverStorategy) throws PokeException {
		WebDriver driver = null;
		try {
			driver = driverStorategy.getDriver();
			driver.get("http://www.facebook.com/");

			// ログインしまっせ
			login(driver, email, password, accountLaungage);

			// あいさつしまくる
			returnPokes(driver, pokeStorategy, accountLaungage);

		} finally {
			if (driver != null) {
				driver.close();
			}
		}

	}

まず最初は WebDriver というのを初期化してる。

driver = driverStorategy.getDriver();

driverStorategy.getDriver()のコードは以下のようになっている。

	public WebDriver getDriver() {
		if (this == firefox) {
			return new FirefoxDriver();
		} else if (this == ie) {
			return new InternetExplorerDriver();
		} else {
			throw new IllegalStateException("hoge");
		}
	}

使用したいブラウザによって、生成するDriverの具象クラスを変更すれば、起動するブラウザを変更できる。

次に、Facebookのページをdriver.get で開いている。getは指定したURLを開くメソッドだ。

driver.get("http://www.facebook.com/");

loginメソッドFacebookログインしている。emailとかpasswordはコマンドライン引数で渡した値になる。

	private void login(WebDriver driver, String email, String password,
		AccountLaungage accountLaungage) throws PokeException {
		typeEmail(driver, email);
		typePassword(driver, password);
		doLogin(driver, accountLaungage);
	}
	private void typeEmail(WebDriver driver, String email) {
		WebElement elem = driver.findElement(By.id("email"));
		elem.clear();
		elem.sendKeys(email);
	}
	
	private void typePassword(WebDriver driver, String password) {
		WebElement elem = driver.findElement(By.id("pass"));
		elem.clear();
		elem.sendKeys(password);
	}
	
	private void doLogin(WebDriver driver, AccountLaungage accountLaungage)
			throws PokeException {
		WebElement elem = driver.findElement(By.xpath("//input[@value='"
				+ accountLaungage.gtLoginButtonValue() + "']"));
		elem.click();
	}

typeEmailと、typePasswordメソッドは、driver.findElementメソッドというHTML要素を探しだすメソッドを呼び出し、emailのinput要素やpasswordのinput要素を取得している。
その要素にたいして、sendKeysメソッドを呼ぶことで、emailなどを入力している。
doLoginメソッドで、ログインボタンの要素を取得し、click でログインボタンをクリックしている。

DriverやHTMLのタグを表すWebElementクラスの findElementメソッドには、要素を探すための By というクラスのオブジェクトを渡せば良い。
デフォルトで以下の By のオブジェクトを返すメソッドが用意されている。

例えば

<a class="hoge" name="foo" id="foo1">これはリンクです</a> 

というHTMLがあったときに以下のように検索できる。

  • By.className : 要素のクラス名で探す。 By.className("foo")
  • By.cssSelector : css セレクタで探す。 By.cssSelector("input.hoge") とか
  • By.id : id で探す。 By.id("foo1")
  • By.linkText : リンクテキストで探す。By.linkText("これはリンクです")
  • By.partialLinkText : リンクテキストの一部で探す。 By.partialLinkText("リンクです")
  • By.name : name で探す。By.name("foo")
  • By.tagName : タグ名で探す。By.tagName("a")
  • By.xpath : xpath で探す。これが一番柔軟に検索できる。By.xpath("//a[@class='hoge']")

これでログインができたので、いよいよ、面倒くさい挨拶を自動で返す!!!
returnPokes であいさつの一覧をさがし、あいさつを自動で返してる。

	private void returnPokes(WebDriver driver, PokeStorategy pokeStorategy,
			AccountLaungage accountLaungage) {
		if (false == exists(driver, By.id("pagelet_pokes"))) {
			// 挨拶のpageletがない場合は何もしない
			return;
		}

		WebElement pageletPokes = driver.findElement(By.id("pagelet_pokes"));

		// すべて表示があったら押す
		showAllIfNeed(pageletPokes);
		//WebDriverWait バージョンで wait する。showAllIfNeedWebDriverWaitVer(driver);

		int pokeCount = 0;
		List<WebElement> pokeElems = pageletPokes.findElements(By
				.xpath("//a[contains(@ajaxify, 'poke_dialog.php')]"));
		for (WebElement pokeElem : pokeElems) {
			if (pokeStorategy.isPoke(pokeCount)) {
				poke(driver, pokeElem, accountLaungage);
			}
			pokeCount++;
		}
	}

exists というメソッドはこのクラス内で定義されているHTML要素が存在するかを返すメソッドである。デフォルトでそういうのあればいいのに何故かない。

exists メソッドでは単に findElement して、例外が出たら存在しないので false を返している。

"pagelet_pokes" という id の要素は、あいさつをしてくれる友達がいない人には存在しないので、何もしない実装になっている。

	private boolean exists(SearchContext searchContext, By by) {
		try {
			searchContext.findElement(by);
			return true;
		} catch (NoSuchElementException e) {
			return false;
		}
	}

Facebookのあいさつに関する部分のHTMLは概ね以下のとおりになっている(2011/09現在)。

<div id="pagelet_pokes" data-referrer="pagelet_pokes">
(略)
	<div>
		<h4 class="uiHeaderTitle">あいさつ</h4>
	</div>
(略)
	<div class="phs">
(略)
	<li class="uiListItem  uiListVerticalItemBorder">
		<div class="lfloat fsm fwn fcg">
			<a href="http://www.facebook.com/sanemat">Aさん</a>
			<a ajaxify="/ajax/poke_dialog.php?uid=0001&amp;pokeback=1"
				rel="dialog-post">あいさつを返す</a>
			<a class="rfloat uiCloseButton uiCloseButtonSmall"
				ajaxify="/ajax/poke_hide.php?p=0001" href="#"
				rel="async-post" title="削除"></a>
		</div>
	</li>
	<li class="uiListItem  uiListVerticalItemBorder">
		<div class="lfloat fsm fwn fcg">
			<a href="http://www.facebook.com/sanemat">Bさん</a>
			<a
				ajaxify="/ajax/poke_dialog.php?uid=0002&amp;pokeback=1"
				rel="dialog-post">あいさつを返す</a>
(略)
	</li>
(略)
	<li class="showAll  uiListItem  uiListVerticalItemBorder"
		onclick="CSS.removeClass($(&quot;u970598_37&quot;), &quot;uiCollapsedListHidden&quot;); CSS.addClass($(&quot;u970598_37&quot;), &quot;uiCollapsedListVisible&quot;); return false;">
		<a href="#">
			<span class="fwb">すべて表示(13)</span>
		</a>
	</li>


全員に丁寧に心をこめてあいさつをしたいので、「すべて表示」をクリックして全員のあいさつを表示している。showAllIfNeed というメソッドがそれを実行している。

	private void showAllIfNeed(WebElement pageletPokes) {
		if (exists(pageletPokes, By.className("showAll"))) {
			WebElement elem = pageletPokes.findElement(By.className("showAll"));
			elem.click();
			// すべて表示を押して、Ajaxですべて表示がなくなるまで待つ
			waitInvisible(pageletPokes, By.className("showAll"));
		}

	}

ここで、初めて Ajax な処理を Selenium で実行、待機している。

<li class="showAll  uiListItem  uiListVerticalItemBorder"
		onclick="CSS.removeClass($(&quot;u970598_37&quot;), &quot;uiCollapsedListHidden&quot;); CSS.addClass($(&quot;u970598_37&quot;), &quot;uiCollapsedListVisible&quot;); return false;">

pageletPokes.findElement(By.className("showAll"))で、上記「すべて表示」の要素を探し、クリックしている。

Ajaxな処理なのでクリックしただけではすぐに全ての「あいさつが表示された」状態ではない。全ての「あいさつが表示された」状態は、「すべて表示」の要素が非表示になった時である。

なので、ここでは

  • 「すべて表示」要素を探す
  • 「すべて表示」要素をクリック
  • 「すべて表示」が非表示になるのを待つ

という処理を行なっている。

次に要素の非表示を待機している waitInvisible メソッドを見てみよう。

	// 非表示になるまでまつ
	private void waitInvisible(final SearchContext context, final By by) {
		Wait wait = new Wait() {
			@Override
			public boolean until() {
				WebElement elem = context.findElement(by);
				return false == elem.isDisplayed();
			}
		};
		wait.wait("Element exists", WAIT_SECOND * 1000);
	}

Selenium2.0 には Wait というクラスが用意されている。
これは until が true である間、指定した実行時間内(WAIT_SECOND * 1000)、実行し続ける。

この場合、要素を取得して、それが表示されてる間[false == elem.isDisplayed()]は処理を実行しつづける。
逆に言うと、表示されなくなったら終了する。
これにより、「すべて表示」の要素が表示されなくなるまで待つことができる。

Selenium2.x で Ajax 用に処理を待機する方法は何パターンかあるが、こちらの記事が詳細に書かれているので参考にするとよい。
リンク先の記事にもあるが、Selenium2.6以降を使っているならば、WebDriverWaitとExpectedConditionsを組み合わせる方法がおすすめである。そのバーションの waitInvisible も書いてあり以下に示すので参考にしてほしい。

	//非表示になるまで待つ。
	private void waitInvisible(WebDriver driver, By by) {
		org.openqa.selenium.support.ui.Wait<WebDriver> wait = new WebDriverWait(driver, WAIT_SECOND);
		wait.until(invisibilityOfElementLocated(by));
	}

ね。こっちのほうが簡単でしょう?

Ajaxを待つ基本パターンは解説したので、残りの処理は手短に解説する。
returnPokesの、showAllIfNeed呼び出し以降のメソッドの説明をする。

		int pokeCount = 0;
		List<WebElement> pokeElems = pageletPokes.findElements(By
				.xpath("//a[contains(@ajaxify, 'poke_dialog.php')]"));
		for (WebElement pokeElem : pokeElems) {
			if (pokeStorategy.isPoke(pokeCount)) {
				poke(driver, pokeElem, accountLaungage);
			}
			pokeCount++;
		}

pageletPokesはすでに取得した以下の要素である。

<div id="pagelet_pokes" data-referrer="pagelet_pokes"> 

「pageletPokes.findElements(…)」では上記要素の子要素で、@ajaxifyという属性に、poke_dialog.php がある要素、つまり、以下のような「あいさつを返す」リンク要素を探している。

<a ajaxify="/ajax/poke_dialog.php?uid=0001&amp;pokeback=1" rel="dialog-post">あいさつを返す</a>

次に、それぞれの「あいさつを返す」要素ループをまわしpokeメソッドを呼び出し、あいさつを返している。pokeStorategy については詳細解説しないが、あいさつする人数を制限する機能が実装されている。

次に、実際のあいさつ返しを行う poke メソッドを解説する。

	private void poke(WebDriver driver, WebElement pokeElem,
			AccountLaungage accountLaungage) {
		// 「あいさつを返す」要素をクリックする
		pokeElem.click();

		// あいさつをするボタンが出るのを待つ
		By pokeButtonBy = new ByValue(accountLaungage.getPokeButtonValue());
		if (DEBUG) {
			pokeButtonBy = By.name("cancel");
		}
		waitPresent(driver, pokeButtonBy);

		WebElement button = driver.findElement(pokeButtonBy);
		button.click();
		// ボタンがなくなってる(Ajaxで処理が終わったことを確認)
		waitNotPresent(driver, pokeButtonBy);
		
		//なんかPokeした後にOKボタンのダイアログが出て、自動で消えるとかいう糞仕様…OKボタンが消えるタイミングも微妙だし…待つしかない…
		waitNotPresent(driver, By.name("ok"));
	}

「pokeElem.click()」で「あいさつを返す」リンクをクリックする。
クリックした結果、Ajaxダイアログが開き「あいさつをする」ボタンが出てくるはずなので、waitPresentメソッドで、ボタンの出現を待つ。waitPresentメソッドとwaitNotPresentの実装は各自確認して欲しい。

「あいさつをする」ボタンが表示されたらそれをクリックする。「あいさつをする」もAjaxで、その呼出しが完了したことは、「あいさつをする」ボタンがなくなってることをwaitNotPresentメソッドを呼び出したことで確認している。
さらに、あいさつをしたあと「OK」ってダイアログが出て、自動で消えるのでそれを待ってから、次の人へのあいさつが行われるようにしている。

ByValue クラスについて詳細には説明しない。Byクラスのサブクラスのサンプルとして用意してある。この例だと value 属性で検索できるようにした。興味のあるかたは実装を覗いてみてほしい。

雑多な何か

インストロール

SeleniumJavaで使うには、maven派の人だったら pom に、

		<dependency>
			<groupId>org.seleniumhq.selenium</groupId>
			<artifactId>selenium-java</artifactId>
			<version>2.6.0</version>
		</dependency>

を入れればおk


そうじゃない人は、selenium-javazipダウンロードしてクラスパスに含めればおk。
http://code.google.com/p/selenium/downloads/list

キャプチャ

Seleniumの利点はCSSとかデザインのチェックもできると思ってるんだよね。
なぜなら、Seleniumブラウザで表示した画像をキャプチャできるので、例えば毎日画像をチェックして、
大きく変更されてないかとかチェックできるよ!

ブラウザによって大きく違わないかとか!

どうチェックするかは、あとはキャプチャした画像をどう使うかだからお前らが考えろ!

まとめ的ななにか

このようにして SeleniumAjax な Webアプリケーションを実行することができる。
今回は実行しかしてないが、JUnit と組み合わせることで、HTMLの内容やデータベースの内容を Assert して自動テストすることができる。

例えば、僕は某ブラウザゲームの自動実行ツールなどを作成したことがある。dragAndDropなどもできるので、工夫すればSeleniumにいろいろ実行させることができる。

ただし、上記のようにスクリプトファイルは難しいし、xpathなどコツはいる。

Selenium でテストを書くときは、Seleniumでテストを書きやすいように id をふったりする必要があるだろう。Ajaxアプリケーションの場合はテスト用に変更をマークするようなhiddenとかがあっても良いと思う。

では良いテストライフを!

2011-09-10

PHPカンファレンス2011 で"PHPとテストとCIと私〜愛するあなたのため〜"というタイトルで発表してきました

17:03 | PHPカンファレンス2011 で"PHPとテストとCIと私〜愛するあなたのため〜"というタイトルで発表してきましたを含むブックマーク

PHPカンファレンス2011 で"PHPとテストとCIと私〜愛するあなたのため〜"というタイトルで発表してきました。

当日は、ほとんど寝ず、午前中は #nekkonという結婚式に参加してからの発表だったから辛かった。実質寝てねーからつれー。発表つれー。


内容としては架空の某システムの裏方に入った、
架空の人が、いかにレガシーコードと戦い、TDDCI適用していったか、
また、適用するにあたりどういう便利なツールを使ったか、
また、チームにそれらの文化を浸透させるためにどうしたか。

などといった内容となっています。

以下がプレゼンのスライドを Slideshareに上げたやつです。あとUSTの録画もありました。
http://www.ustream.tv/recorded/17177077