ソフトウェアテストの設計は、ソフトウェアサービスの設計と同じ

今日、「エンジニアが如何にテストに取り組むか」みたいな話を聞きに行ったけど、最初から最後まで何か違和感があった。

勿論テストケースがコケると色々面倒だし、サービスが続けば続くほどテストケースがメンテできなくなってくるけど、
本来、テストケースでエラーが出たときは「テストケースでエラーが出たおかげで、本番リリース前にデグレードが見つかって助かった」と喜ぶべきだし、それを踏まえれば、テストケースのNGが増えてくるのはそれだけサービスが成長してる証として喜んでも良いのではないのだろうか。

そう、「サービスで不具合が起きる事を予防する」のがテストであり、何故それが必要であるかと言えば、「不具合が起きた時のリスク・コストを軽減する」ためだ。

上記を踏まえると「その機能に不具合が起きた時にどれぐらいのリスク・コストがかかるか把握してない」エンジニアがいくらテストを書いてもメンテナンスする価値のあるテストは書けないのではないだろうか。

テストを作る人間、テスト工程を管理する人間がビジネスを理解してなければ、ロクに使われてない機能のためにテストケースのカバレッジを100%にするとか、本番でのバグが検知できない意味のないテストコードとか、無駄なコストや負債が増えるだけではないだろうか。

逆に、エンジニアが「ビジネスモデル」を理解していれば「ほんとうに必要なテスト」というのが自ずと定まってくるはずだ。
「このプロジェクトは仕様が全然定まらないからテストは書けないんだよ」というのは自分が関わってるサービスを真に理解してないエンジニアの言い訳ではないか。

そしてエンジニア以外のサービスに関わってる人達もテストに向き合う方が良いはずだ。ソフトウェア開発は製品と製品の製造ラインを同時につくるものだから、製品のリリースだけに目を向けて、テストを含めた製造ラインを見なければサービスの真のコストは把握できない。

昨日CAのインフラエンジニアの方とランチをして、すごく有意義な時間を過ごせた。

CAの仕事は半年弱受けていたが、その時に知り合いになった方ではなく、偶然twitterのフォロー/フォロワーだったのでこれを機会にランチでもしましょうと以前から決めていて、それを昨日実行してそこで初めて対面した。

その人のツイートはいつも技術系の話題ばかりだったので、会う前はナードな感じの人かと想像していたが、実際に会うと朗らかでありながら礼儀正しく、私服であっても、奇抜さやだらし無さもなくて、とても紳士的な印象だった。

実際に話した感想は「CAにこんな人がいるのかぁ」といった感じだった。

僕が今までに知り合ったCAの若手エンジニアが持っている「技術に関するどん欲さ」と、エンジニアとしてのキャリアの長さを感じさせる「仕事に対する冷徹さ」を併せ持ってる様な人で、単なる世間話以上の有意義な話ができた。

「初心を保ちながら経験を活かす」「目の前のタスクに夢中になりつつ、客観的に仕事を俯瞰する」こういう相反する事がバランスよくできれば、仕事で不安を覚える事も無いんだろうな。

PHPのTraitをやたらと使って感じたこと

以前、php5.4のプロジェクトに関わっていて、
そこのプロジェクトの方針でやたらとTraitを作っていたが、Traitの多用は「鬼門」だと正直思った。

class MyModel extends BaseModel 
{
    use User;
    use Photo;
    use Album;
    use Relation;
   //....
   //....
   
   public function getData($id) 
   {
        $tmp = $this->getFriendList($id);      //どのTraitに実装されているか
        return $this->getPhotoSet($tmp);       //調べるのが結構たいへん!
   }
}

NetBeansの場合はtraitのコード補完・関数ジャンプの機能があるからまだ楽だけど、
それでも実装があちこちに飛んでしまうのは階層の深い継承と同じストレスがあるし、
Traitの関数名を迂闊に変えるて、他のTraitの関数と名前が衝突してしてコンパイルエラーが起こるので、多重継承と同じストレスもある。

しかし、継承を使わずにプロパティにアクセス出来る実装を使いまわせるのは便利だった。

例えば、SPLにあるArrayAccessインターフェイスとかはTraitにしたほうが便利なのではないかと思う。

/**
 * ArrayAccessで典型的な処理をTraitで実装
 */
trait ArrayAccessHelper
{
    public function setProerty($offset, $value) 
     {
        if (!empty($offset)) {
            $this->$offset = $value;
        } 
    }
    public function proertyExists($offset) {
        return property_exists($this, $offset);
    }
    public function unsetProerty($offset) {
        unset($this->$offset);
    }
    public function getProerty($offset) {
        return property_exists($this, $offset) ? $this->$offset : null;
    }
}

/**
 * 上記をArrayAccessインターフェイスと合わせて使う
 */
class ArrayObj implements arrayaccess 
{
    use ArrayAccessHelper;

    public function offsetSet($offset, $value) 
    {
        return $this->setProerty($offset, $value);
    }
    public function offsetExists($offset) 
    {
        return this->proertyExists($offset]);
    }
    public function offsetUnset($offset) 
    {
        return $this->unsetProerty($offset);
    }
    public function offsetGet($offset) {
        return $this->getProerty($offset]);
    }
}

まぁこのやり方でも、インターフェイスの実装は必要なだけど、幾つものクラスにインタフェイスを実装するときはこの方法でタイプの量をかなり減らせる。
(※1)

あと、以前のプロジェクトではCodeIgniterのModelにTraitを使っていた。

  • コネクションを保持するのはCI_Model の$db
  • 特定のテーブルのアクセスはメソッドはTraitにまとめる
  • アプリ内で使用するModelは上記2つを合わせて構築する

上記のルールでテーブルへのアクセス処理をかなり共通化出来たのですが、
その代わり最初に書いた問題も露呈指摘たのでTraitの使い処は難しい。


(※1) ArrayAccessを実装したクラスをを継承したほうが手っ取り早い事に気づいた、、、

複数環境でPHPを動かすときに気をつけていること。

ほとんどのサーバー再度開発は Local→ステージング→本番の様に コーディングからリリースまでに複数の環境で動かすことが多いと思います。

この時に問題になるのが“Localでの環境設定がそのまま本番にUPされてしまった。”といった環境依存コードの管理ではないでしょうか?

今回自分が環境依存コードの管理で気をつけている事をまとめてみました。

1. 環境の設定をSCMに入れない。

subversionやgitにどの環境を判定・設定するコードを入れずに、サーバーの設定や、デプロイ時のコマンドで対応するようにします。

下記の様なコードをSCMに上げていると”Localの環境が本番に上がってた。” といった危険が増えます。

#本番では下をコメントアウトする。
define(DEBUG, true);

上記を防ぐために環境切り替えのためのコードはSCMで管理している場所以外に置くようにします。

よく使う場所としてはApacheのconfです。

<localhost *:80>
...
SetEnv APP_ENV development
...
</localhost>

上記で設定した環境変数はPHPではgetenv関数で取得する事が可能です。

$env = getenv('APP_ENV');

switch($env) {
    case: 'production'
        include('prod_conf.php');
        break;
    case: 'development'
        include('dev_conf.php');
        break;
    case: 'test'
        include('test_conf.php');
        break;
}

上記の様な工夫により環境の設定ミスを起こす可能性がかなり減らせます。

2. 非機能要件は抽象化する。

非機能要件→キャッシュ、ログ、セキュリティなどは必ず何らかのラッパーを入れて
環境により切り替えられるようにしないと、「テストのためだけにコメントアウトしたコードが本番に上がる」
といった事態が起こります。

//windows環境で動かせないので下をコメント(といった一時的なコードがそのまま本番に上がってしまう。)
// $settings = $memcache->get('setting');

SymfonyやSpringなど、DIコンテナが使えるのであればそれを使ってキャッシュやログ管理を抽象化するようにしましょう。
そういったものが使えない場合もライブラリを導入するか自作するかして、他のプログラマが、キャッシュ機構やログ機構を意識しないような設計にするのは重要です。

以上です。
こういうのはユニットテストなんかを書くと早めに気づけるので、テストコードを書きつつ設計を見直すことをおすすめします。

Macのeclipseにsubclipseを入れても使えずに困った。

MacBook Pro(Lion)にPHPの開発環境を作るために、eclipseとsubclipseを入れてチェックアウトしようとしたら

変なエラーが出て進めない、、、

なんだかJavaHLライブラリがないと言うエラーのようで検索してみるとMac Portsでインストールできる事が判明。

$ sudo port install subversion-javahlbindings

これでようやく使える様になった。手こずってしまった、、、

ForkwellのおおかゆかさんとStartup Datingで話した。

興味本位で登録していたFacebookグループ”Startup Dating”でForkwellの話を聞くことが出来るというので、初めて参加してみた。

http://www.startup-dating.com/2012/01/startupdating-salon/

ココでForkwellディレクターのおおかゆかさんや、アメリカのスタートアップの環境に詳しい方々から非常に濃い話を聞いたのでまとめてみる。

Forkwellの話

  • 最初からシリコンバレーで使われる事を念頭に置いているので、デフォルトの言語は英語・サーバーはAWSの西海岸リージョンを使用している。
  • スキルのあるエンジニアが一つの会社に縛られることなく、プロジェクトを渡り歩ける様にしたい。
  • 登録者のスキルに+1出来るのはFacebookで友達になってる人だけなのでレピュテーションの信頼性が高い。
  • スキルのロゴに自動的にフォントを適用するなどデザインには相当こだわってる。
  • 将来の求人サービスは、あまり「求人スパム」的なものが来ないような仕組みを作りたい。

おおかゆかさんから聞いた話

  • 今、Slerの若い優秀なエンジニアがどんどんWeb系の起業に転職している。
  • 最近、SI案件で大きな失敗の事例が増えているのは、優秀なエンジニアが逃げ出してるのもひとつの要因では?
  • PG/SE/PMみたいに職種分けされる事にエンジニアが嫌気を感じている。
  • 私はドリコムでソーシャルゲームの開発も手がけていたが「自分のサービスが億単位の金を動かしている」のを体感するのは一度経験する価値がある。
  • 「日本のデザインはアメリカではうけない」とか言われているけど、私はそうは思わない。nintendoのコンテンツなどアメリカでヒットしている日本のデザインはある。

アメリカのエンジニアの環境の話など

  • 日本のエンジニアは優秀な人が多く、アメリカに行けば年収600~900万は見込めるのに日本の仕事に満足しているのはもったいない。
  • 日本のエンジニアの技術力は、米国のエンジニアと比較しても遜色はない。
  • 英語が不自由でも、技術がある人を欲しがってる会社は西海岸にはたくさんある。
  • 日本のエンジニアは「オープンソースのコミッタになった」とか、「〇〇のライブラリを公開した」とか、技術者としての名誉を得ることへの関心は強いが、経済的な成功を得る事への関心が低すぎる。
  • エンジニアはもっと自分のプロダクトや、技術がマネタイズされる事へ関心を持って欲しい。

僕が話したこと、思った事

  • 技術者がマネタイズに関心が薄い理由は、SIerとか長らくシステム部門が「コストセンター」として扱われてきた事が大きいと思う。仕様を削る、人件費を削るなどコスト削減に強いエンジニアは周りにも多いけど、「こういう機能つけたら利用者数増える」とか「アクセスログからしてこの機能は使われてないからテコ入れしたい」みたいな事を考えるエンジニアは少ない気がする。
  • 日本のエンジニアが「アメリカに行って稼ごう」と思わないのは、「日本でもそこそこの給料がもらえる仕事が十分にあるから」と、「プログラミングや開発といった行為が十分楽しいので、その職種につける事に満足してしまうから」かなぁ。。。
  • でもこの前エルピーダが倒産した時の竹内健さんのtweet、これこれを見て、エンジニアももう少しお金ハングリーな方がむしろ技術スキルが上がる様な気がしてきた。
  • 金稼ぎに興味が無いことはエンジニアに限った話でないと思う。 ただ、「ITでお金を稼ぐ」事は「ITエンジニアの雇用を生む」「ITエンジニアのキャリアを積む機会を作る」事に繋がるから、結果的に「IT業界に貢献する」のは間違いない。「OSSに貢献する」とか「〇〇の本を執筆した」以外にもITや社会に貢献する方法はある。
  • おおかゆかさんはディレクターなのにRubyもScalaもPythonも出来てカッコイイ!」と思ってたら、元々エンジニアとして参画してたけど、ディレクション担当になりそのまま今のポジションに落ち着いているようです。(garbsさんは代表取締役以外全員エンジニアなのかな?)

久しぶりにバイト時代の先輩と会う

大学時代の知人の死があり、10年ぐらいぶりに大学の友だちと集まる機会があった。その集まりの後、僕にずっと親身にしてくれているバイト時代の先輩の元に出かけた。

その先輩は学生時代から人間や時代に対する観察眼が鋭く、話を聞く度に感心させられる事が多い。

その時の3時間ぐらいの雑談も非常に内容が濃かった。

続きを読む

++[[]][+[]]+[+[]] = 10

「javascriptで ++[[]][+[]]+[+[]] = 10 になるのを説明できる人いる?」
って質問がStackOverFlowにUPされてたけど、
http://stackoverflow.com/questions/7202157/can-you-explain-why-10

その回答611が完璧!
これを理解するのに一番の肝は+[]で、

+[]を実行すると
1,[]のtoStringが実行され、+””と評価される。
2,さらにNumber型への変換がされ、0と評価される。

++[[]][+[]]+[+[]] を書き直すと

++[[]][0]
+
[0]

と評価され

++[[]][0]
+
[0]

その後

++([[]][0])
+
[0]

更に

([] + 1)
+
[0]

最後に

1
+
"0"
== "10"

で結果 10になる!

PHP5.4でコンストラクタを呼ばずにインスタンスが作れる

こんにちは。PHP5.4 ADVENTカレンダー13日目PHP5.4の新機能の一つ: アップロード進捗の取得に続いて14日目の担当のPaulです。

PHPはReflectionを使うことにより、privateとかprotectedとかのルールを無視して色々出来ます。
下はReflectionを使ってprivateメソッドを呼び出す例です。

class LockedDoor
{
    private function open()
    {
        return 'privateだけどドア開いちゃったよ!!';
    }
}

$refClass = new ReflectionClass('LockedDoor');
$refMethod = $refClass-&gt;getMethod('open');
$refMethod-&gt;setAccessible(true); //無理やりprivate解除
 //privateだけどobjectの外から実行できる
echo $refMethod-&gt;invoke($refClass-&gt;newInstance());

PHP5.4(Beta1)でReflectionClassにnewInstanceWithoutConstructorというメソッドが加わりました。
そうです!名前通りコンストラクタ使わずにインスタンスが作れるのです。

PHP5から__constructでコンストラクタが書ける様になりました。あれはあれでクラス名に依存しないので便利なんですけど、それのお陰でコピペのコンストラクタが増えたと思うのは僕だけでしょうか?
そういう問題には本来「親クラスを作って必要な処理はそこにまとめて、、、」やるべきなのでしょうが、「それが面倒だけど、コンストラクタの余分な処理をなんとかしたい!」って時に使えます。

例えば下の様なクラス

class MyClass
{
    private $lang;

    public function __construct() 
    {
        $this-&gt;lang = $_SERVER[HTTP_ACCEPT_LANGUAGE];
    }

    public function getLang() 
    {
        return $this-&gt;lang;
    }

    //意味ねーよ!
    public function setLang($lang) 
    {
        if ($this-&gt;lang == null) {
            $this-&gt;lang = $lang;
        }
    }
}

気持ちは分かるけどsetLangの意味がねーよ!と。

それもReflectionClass::newInstanceWithoutConstructorでなんとか出来ます。

$_SERVER[HTTP_ACCEPT_LANGUAGE] = 'ja';
$obj = new MyClass();
$obj-&gt;setLang('en');
echo $obj-&gt;getLang(), PHP_EOL; //jaのまま

$obj = (new ReflectionClass('MyClass'))-&gt;newInstanceWithoutConstructor();
$obj-&gt;setLang('en');
echo $obj-&gt;getLang(), PHP_EOL; //enと表示

個人的にはPHPは[スクリプト=コードが書き換えられる]ので、あまりReflectionに頼るのは如何なものかと思いますが、(Javaみたいにバイナリしかない場合はReflectionも必要だけど)
応急処置として頼りになるので、覚えておいて損はないと思います。

明日15日目は@srea2431さんです!お楽しみに!
あと、PHP5.4 ADVENTカレンダーまだまだ参加者募集中です!

traitでデザインパターン再考

こんにちは。php5.4adventカレンダーの10日目「PHP5.4でのhtmlspecialcharsの仕様変更とセキュリティ」を引き継いで11日目担当のPaulです。

PHP5.4で出てくるtraitこれのメリットとして、

  • mix-inができる。
  • あとでメソッド名再定義できるから、名前当たってもOK

みたいな事がRFCに書いてありましたが、

僕がRFCを眺めて感じたメリットは
親クラス作らなくても、実装の再利用が出来る!って事でした。

いや、ちゃんと親クラス作って、それを実装を追加したいクラスでextendして…
てやればタイプヒントの恩恵に預かれるのは分かってますが、、、
それをする余裕が無いことってありますヤン。
もちろんデリゲートも知ってますけど、あれはあれで追加のコード量が半端ないですヤン。
traitやったら、1個trait作っておけば、あとから実装追加したいクラスにuseなんちゃらって1行書くだけで済みますヤン。
それにもともと、タイプヒントでやってるクラスと、そうでないクラス両方に共通の実装ってなると何かと面倒でっしゃろ。

で、、、あとから実装を追加したくなるのってどのデザインパターンかなと思って
このサイトを眺めてたら

  • Decorator
  • Visitor
  • Observer

ぐらい?

この中で一番簡単そうなObserverパターンをtraitで書いて見ました。

まずはSubject trait

trait Subject {
    /**
     * Observer追加
     *
     * @param Observer $observer
     */
    public function attach(Observer $observer)
    {
        array_push($this-&gt;observers, $observer);
    }

    /**
     * SplSubjectと同じ名前で通知を定義
     */
    public function notify($funcName)
    {
        foreach ($this-&gt;observers as $key =&gt; $item) {
            $item-&gt;update([$this, $funcName]);
        }
    }
}

次はObserver

class Observer
{
    public function update(Callable $f)
    {
        $f();
    }
}
</code>

<p>最後にSubjectの実装</p>
<code>
class AKB 
{

    use Subject;                       //この2行を付け足すだけで
    private $observers = array(); //ObserverのSubjectになる。

    private $members = array();
    private $latest;

   
     /**
      * メンバーが追加される度に通知
      */
    public function addMember($member)
    {
        array_push($this-&gt;members, $member);
        $this-&gt;notify('getLatest');
    }
    
    /**
     * Observerで呼び出される。
     */
    public function getLatest()
    {
        echo $this-&gt;members[count($this-&gt;members) -1] . "が加わりました!" . PHP_EOL;
    }

}

実行すると

$o = new Observer();

$akb = new AKB();
$akb-&gt;attach($o);

$akb-&gt;addMember("前田敦子");
$akb-&gt;addMember("大島優子");
$akb-&gt;addMember("柏木由紀");
</code>
<p><br /></p>
<code>
"前田敦子が加わりました!"
"大島優子が加わりました!"
"柏木由紀が加わりました!"

僕的には作ったあと、「これ本当に便利になったかな?」って思ってしまいましたが、
既存のクラスに少し手を加えるだけ良いし、
例えばAKBクラスが親クラスを持っていたら今までは親クラスに変更を入れるかinterfaceをインプリメントするコードを書かなければいけなかったので、楽になったと思います!

次回12日目は@co3kさんです!お楽しみに

追記:12月12日 class AKBのところにバグが有ったので修正しました。