root権のない一般ユーザーでも開発環境でCI(Jenkins/PHPUnit/Phing)してみたい (前編)

JenkinsやPHPUnitやPhingの導入についてのありがたいまとめはいっぱいあります。

世の中のJenkins/PHPUnitのインストール情報はroot権ある人用 (当たり前)

レガシーな開発環境にはPHPUnitなんぞ入ってなかったりします。
そのわりにポートはしっかり閉じられててJenkins起動しても8080で接続できないとか。

  • 仕方ないのでローカルのWindowsPCにJenkinsとか入れて、レガシーコードの方をWindowsPCに落としてきたけどまともに動かない
  • しょうがないのでサンプルスクリプトでちょこっといじって終了

一般ユーザー権だけでJenkins/PHPUnit/Phingしたい

でも実際に業務で触ってるものに対してPHPUnitでテスト書いて、JenkinsとPhingでCIしてみたい。
sudoできる人たちに「努力の方向が間違ってる」といわれそうですが。
でもいいんです。持つものには持たざるものの気持ちなど分からんのです。

ということで以下、一般ユーザーでJenkins/Phing/PHPUnitでCIする説明。

Jenkins インストール

落としてきて起動する。簡単。
$ mkdir jenkins
$ cd jenkins
$ wget http://mirrors.jenkins-ci.org/war/latest/jenkins.war
$ java -jar jenkins.war --prefix=/jenkins > jenkins.log 2>&1 &
# もしくは nohup java -jar jenkins.war --prefix=/jenkins > jenkins.log 2>&1
確認
$ wget http://localhost:8080/jenkins/ -O jenkins.html

これでちゃんとJenkinsダッシュボードのHTMLが取れてればOK。

ブラウザからアクセス

http://hoge.example.com:8080/jenkins/ (開発環境ドメインhoge.example.comの場合)
これでJenkinsさんが出てくればOK。出ない場合は↓に続く。

開発環境にポート8080でアクセスできない場合

jenkinsは動いてるのにローカルPCのブラウザからのアクセスが通らない。
ポート閉じられてる場合の対応。

方法1 ポートかえる

あいているポートがあるならJenkins起動時のオプションに --httpPort=XX つけてそっちにアクセスすればいいかも。
やったことない。

方法2 Apacheを通してアクセスする

設定をいじれる場合(.htaccess不可)に限りますが、プロキシしてあげればいけます。

LoadModule proxy_module modules/mod_proxy.so

ProxyPass /jenkins http://localhost:8080/jenkins
ProxyPassReverse /jenkins http://localhost:8080/jenkins

http://hoge.example.com/jenkins/ にアクセスすればご対面。

方法3 SOCKS Proxyする

個人用のhttpd.confが割り当てられてたりwebサーバー再起動したりできるケースは少ないと思うのでこちらで。
ブラウザからのアクセスをSSHでプロキシしてもらう方法。

  • まずはPuttyの設定
    • Puttyを立ち上げて開発環境に接続する設定を選択して「読込」
    • 接続>SSH>トンネル
    • 「源ポート」に「8080」
    • 「ダイナミック」選択
    • 追加ボタン

と設定して開発環境に接続します。立ち上がったターミナルはそのまま。

  • ブラウザの設定 (Firefoxの場合)
    • ツール>オプション>詳細>ネットワーク>接続設定
    • 手動でプロキシを設定する
    • SOCKSホストにlocalhost、ポート8080

この状態で、
http://localhost:8080/jenkins にブラウザからアクセスすればご対面。


次回pearでのphpunitとphingいんすとーりんぐ

uncaught exception: Syntax error, unrecognized expression: [@name=hoge]

jQuery1.2.6からjQuery1.6.2にアップデートしたらこんなのが出てきた。

jQuery内部でエラーが出てる

確か1.3でセレクタのエンジンがSizzle変わったといってたのでそのせいかしら。なんとも今更な。
昔の話しすぎて日本語の情報があんまり情報が引っかからなかったのでメモ。

セレクタの属性値に@が使えなくなってた

http://semooh.jp/jquery/cont/doc/release_1.3/

$("input[@name=hoge]"); // × 古い
$("input[name=hoge]");  // ○ 新しい

と直して解決。

おまけ

attr(), prop() 関連

こっちは最近の話だけど、属性とプロパティの扱いが厳密になった件で1.6でいろいろ動かなくなるものが出まくって問題になってた。
1.6.1からは後方互換になったので安心!変な書き方してるやつらぷぎゃーーwwww
→ とか思ってたら自分もだめだった。

$('#myCheckBox').attr('checked', 'checked'); // × 古い
$('#myCheckBox').attr('checked', '');        // × 古い

$('#myCheckBox').prop('checked', true);  //○ 新しい
$('#myCheckBox').prop('checked', false); //○ 新しい

どういうことなのかは↓
http://ginpen.com/2011/05/20/jquery-1-6-1/

RDD (Release Driven Development) リリース駆動開発

とりあえずリリースして様子を見る、という開発手法。

ちょっと変更してすぐリリース

  • 変更がちょっとなのでバグを埋め込みづらい
  • 変更がちょっとなのでバグがあっても対処しやすい
  • 変更に対する反応を確認しやすい
    • 変更が大きいと、何が原因でこんな反応が返ってきたんだ・・・?となる

→今更気付いたけどこれって継続的デプロイ!

自動化しない

ユニットテストとかは別として、変更が頻繁で作成コストも高いviewのテストを自動化できるようにしっかりやってると大変なので・・・
自サイトで利用の多いブラウザに絞ってみんなで手動確認。使い心地などのレビューも兼ねます。

自動化しないとかアホかwww

でも、実装してみてorリリースしてみて、やっぱこっちの方がいいやというのが頻繁にある場合、そのたびごとにテスト作成するとかやってられません。そしてその流れが継続的である場合は、テストを作るタイミングがつかめなかったり。
もちろん粒度の細かめなプログラマのためのとかはどんどん自動化してやっていくべきだと思います。

バージョン管理

なんかあったらすぐ戻す。やっぱり前の方が良かったな、というのもさっくり戻す。

バグの埋め込みや複雑化を避ける

これすぐやって! みたいな事は言わない

「急いで!」といわれると、エンジニアは進行中の作業を一旦止めて割り込み作業に対応します。それが終わると「えーと、なにやってたっけ」→ 無駄・バグ。

やらなくていい仕事をやらない

システムの中身に興味のない営業の人などがよく言うんですが「資料がにぎやかになるので○○追加して」とか「誰も見ないけど慣例だから○○も出力して」とか。これらをばっさり切り捨てる。

比較する

テストがなくても出力の比較くらいは簡単にできます。変更前の出力と変更後の出力を比較して、変なところでデグレがないかは簡単に確認できます。ここは自動化。

前提条件

現在正しく動いているシステム

テストはなくともとりあえず今の状態は正しい。というのは必要で、それをあんまり手を掛けずに維持するというのが主目的。

寛容さ

やっぱり出るときは出るので、ちょっとくらいの障害は握りつぶすくらいの・・・

修正の取捨選択と作業量調整ができる事

やらなくていい修正やるべきでない修正はやらない、というところを徹底して、作業の流量をうまいこと調整するのが必須。修正作業に当たるエンジニアなのか、プロジェクトの管理者なのかはわかりませんが。

いや、普通にCIすればいいじゃん

まぁそのとおりなんですが・・・
CIとかそういった手法って、エンジニア自身のリテラシーとかアジャイルなマインドとかをある程度必要とするものだと思います。ここで上げたRDDでは、一人これをうまくできる人がいれば後の人はそんなにがんばらなくても。

ただし

キーマンがいなくなったら死亡。
→ 実際いなくなったのであわてている。

恥知らずなTDD使いがいた!

俺はRDDを使い手なんだがプロジェクトリーダーがが残念なことにTDDを使ってきたので「お前それで良いのか?」と言うと「何いきなり(テスト書かずに)実装してるわけ?」と言われた。
俺の前プロジェクトチームがTDDの熟練者なのだがおれはいつもデスマーチにするから気の毒になったので聞いただけなんだがむかついたので「お前中身が空のテストでボコるわ・・」と言ってプロジェクト開始直後にバグを溜めてコミットしたら多分リアルでビビったんだろうな、、カバレッジ測定してきたから無視してカカッとコミットしてから障害だしたらかなり青ざめてた
おれは一気に次の実装をしたんだけどリーダーがテンパってておれの作業を見失ったのか指示出してこなかったから独自インデントでコーディング規約を崩した上についげきのオレオレデザインパターンでさらにプロジェクトへのダメージは加速した。
わざとチームから距離をとり「俺はこのまま納期に間に合わなくてもいいんだが?」というとようやく必死な顔してなんか部屋のはしっこにプログラマ増員してきた。
チームは必死に仕事するが、時すでに時間切れ、マジックナンバーと謎のif分で固めたスパゲッティにスキはなかった
たまに来る効果的なリファクタリングもコンフリクトで撃退、終わる頃にはズタズタにされて白髪の増えたメンバーがいた

アンチパターンとしてのRDD

ふと思ったこと

xUnit,Jenkins,tracなどのアジャイルプロセスのためのツールを使っていないプロジェクトがありました。
でも、こまめなデプロイ(機能リリース)を繰り返してCIっぽくうまくいっていたことに気付いたので。

RDD (Release Driven Development) リリース駆動開発

リリース駆動開発とは

とりあえずリリースして様子を見る開発手法である。
手戻りに強くすばやいリリースが少ないリソースで可能。だが一歩間違えると単なるTDDのアンチパターンになるという諸刃の剣。素人にはお勧めできない。

RDDで開発を行う理由

既にレガシーコードのシステムが稼働中

テストを書くためには大掛かりなコードの修正が必要だったり、進め方を変えるのに偉い人の判断が必要だったり、なんかいろいろと壁があってTDDに切り替えられない。
TDDで開発を進めるのはプログラマ個人の問題だったりするのでそんなに問題にはならなそうですが、既存のものにテストを書いていくとなると話が違います。

開発担当者が全体を把握しているので何とかなる

長い時間をかけてコードが育つのを、特定のエンジニアがずっと見てきた場合。てゆか育ててきた場合。
ハウルの動く城みたいなコードベース。 誰が見てもとっちらかってるけど作った本人は中身をよく把握してて、掃除されると逆に大変になるみたいな。

ツールじゃないんだ、人なんだ

TDDやCIの手法やツールなしにうまいことプロジェクトが回っていた理由。

あとでかく。
→ 書いた RDD (Release Driven Development) リリース駆動開発 - くろまほうさいきょうでんせつ

テストコードのディレクトリ構成

今日のレガシー対応

ディレクトリ構成のお手本が見つからない

PHPUnitのテスト用ディレクトリ構成はこうしなさい、みたいなのが見つからなかったので悶々と悩み続け・・・

Rails的なディレクト

テストの内容でディレクトリを分けるか・・・

test
|-- fixtures テスト用のデータ
|-- functional コントローラのテスト
|-- integration コントローラをまたがるようなテスト
|-- mocks モック
`-- unit ユニットテスト
CakePHP的なディレクト

テスト対象ファイルで機械的に分けるか・・・

test
|-- README
|-- cases テストケース
|   |-- controllers
|   |-- helpers
|   `-- models
|-- fixtures テスト用のデータ
`-- groups テストをまとめて実行するグループ
ZendFramework的なディレクト
tests
|-- application
|   |-- controllers
|   `-- models
|-- library
`-- phpunit.xml

悩む意味はなかった

RailsCakePHPディレクトリ構成はそれぞれのテストフレームワークに従ったやり方であり、レガシーなところにとりあえずPHPUnitだけ持ってきただけの今の状態でどうこう言ってもしょうがない。

とりあえずCakePHP式に

一瞬見て何がどこにあるかすぐ分かるから。テストを導入しようとして匙を投げた先人たちは、導入の入り口付近でとまってた気がしたので、とにかく無駄に悩むのをやめるぞジョジョーーー!!
「やありゃいいんだよ」。なんか気づいたら後から直せばいいじゃん。とりあえず手を動かそう。

課題

コントローラのテストができない

フレームワークなしに自分でコントローラのテストができるようにPHPUnitの拡張を用意するのは大変。
消極的な解決策としては

  • 処理は極力modelに移動する
  • あとはSeleniumでのViewのテストでカバー
    • と思ったけどseleniumによるテストは作成コストが高すぎて割に合わない感じ
    • だいぶ抽象的ななassertionにしないとレイアウトやデータの変更ですぐだめになるし
      • コントローラーテストの代わりにはならない。

privateメソッドのテスト

今日のレガシー対応

privateメソッドをテストハーネスに入れた

あるクラスのprivateメソッドのテストをしたい
class Hoge {
	public pubMethod(){
		$this->meth1();
		・・・
		$this->meth2();
		・・・
	}
	private meth1(){
		・・・
	}
	private meth2(){
		・・・
	}
}
本当は設計を見直して別のクラスに切り出すべき
class Hoge {
	public pubMethod(){
		$fuga = new Fuga();

		$fuga->meth1();
		・・・
		$fuga->meth2();
		・・・
	}
}
class Fuga {
	public meth1(){
		・・・
	}
	public meth2(){
		・・・
	}
}

(pubMethodでFugaをnewしてるのがいいのかはまた別として・・・)

でも派手なリファクタリングはできないのでprivateをprotectedにした
class Hoge {
	public pubMethod(){
		$this->meth1();
		・・・
		$this->meth2();
		・・・
	}
	protected meth1(){
		・・・
	}
	protected meth2(){
		・・・
	}
}

↓テストコード

class HogeTest extends PHPUnit_Framework_TestCase {
	public function setUp(){
		$this->obj = new TestingHoge();
	}
	public function testMeth1(){
		・・・・
	}
	public function testMeth2(){
		・・・・
	}
}

class TestingHoge extends Hoge {
	public meth1(){
		return $this->meth1();
	}
	public meth2(){
		return $this->meth2();
	}
}

これってどうなの?

既存のコードに大幅に手を入れることができないので

しょうがなかったんや・・・

privateメソッドをprotectedにすることが(今回は)安全だった

↓のような理由でアクセスレベルがゆるくなる分には問題なしと判断できた。
このクラスを継承したりする場合にはなんかあるかもしれないけど、このプロジェクトではクラスの継承は特定のケースに限られている。

残る疑問

ほかのクラスのメソッドを呼び出してるメソッドのテスト

先の例で言うと「Hoge::pubMethod()」。
このメソッドのテストってどうするべきなのか・・・

  • 普通にこのままテスト
    • Fugaクラスのメソッドのテストを2重に行うことになるのでは?
  • Fugaクラスのモックを用意してテストすべき
    • ややこしくなってまいりました
  • Hoge::pubMethod()自体がやってることがほとんどないなら無理にテストしなくても
    • それもどうかと
参考にできる実例がなかなかない

いやレガシーコード改善ガイド嫁といわれればそれまでですが、時間や権限や状況によってとりうる手段が限られてきたりすると思うので・・・
ほんとにゆるーい感じの個人的な実例なども見てみたいです。

PHPでTDD&CIワークショップ に参加してきました

勉強会は初めてで敷居が高いもののどうしてもテストがうまく書けなかったのでヒントをもらおうと、勉強会に勉強しに行った素人です。とても勉強になりました。レガシーコードに立ち向かう気力も頂きました。ほんとうにありがとうございました。
以下、TDD等について特にためになったこと。

TDDで行うテストとは

  • プログラマプログラマによるプログラマの為のテスト
  • 軽量
  • インターフェース境界、接合部をテストすべし (○振る舞いのテスト ×手続きのテスト)
  • テストを書くのはプロとしてのたしなみである

TDDはリズムが大事

少しずつ進むのがよい (よく出てくる「インクリメンタルな開発」)
波に乗れると・・・
  • テストが気持ちよく書けている = 多分いいコードになってる
  • 黄金の回転ができている = 良い設計になっていることが多い
ちょっとずつセーブしながらRPGを進めていく感覚
  • すばやく対象に近づいてフィードバックを得ながら、ちょっとずつ、頭で理解しやすいサイズでやる

ライブペアプログラミング

モニターでペアプロでライブコーディングしてくれた方のコーディングがとても良かったです。

  • 頭のなかで考えたことを声に出しながらやる
  • ペアプロ中の発言などをソースのコメントとして書いていくとみんな見やすい
  • エディタは白地に黒文字が見やすい、文字の拡大などの設定は事前に確認しておくとよい
  • てゆかPHPってメソッド名に日本語使えたのかすごい

いざレガシーコードと対峙して

まずはできることから、とにかくやる

やはりとにかくできるところからやっていくといういのが正解っぽいです。とりあえず手を動かさないことにはなにも前に進まないから。
いっぺんにやらなくてもとりあえず趣味の日曜プロジェクトでお試しTDDとか、バグ修正したときにその部分のテストを書いておくとか、写経で勉強するとか、手がかりは必ずあるはず。

http://d.hatena.ne.jp/norry_gogo/20110622/1308716752

最後の@kakutaniさんを囲んでアジャイル周辺の質疑コーナーでは「やればいいんだよ」というメッセージが印象的でした。
指示や許可を待つのではなく、各自で考えて、足元で出来る事をやる!

学ぶべきスキルは他にも

今回のワークショップで扱われた基礎的な手法以外に、実際にレガシーコードと戦うための武器はまだまだ。モックを使ったDB周りのテストや、Seleniumを使ったviewのテストや。
レガシーコードレジスタンスの次回ブートキャンプにご期待ください! といった感じの締めでした。

その他

稲川淳二によるPHPホラーもっともっと聞きたかったです。