2011年10月29日土曜日

暇だから MongoDB でも入れてみるか

せっかくの休日、Eclipse の Xtext を試してみたかったのに、Indigo はコアダンプ吐いて即死するし、Helios はプロジェクトの作成に失敗するわで、15分くらいで心が折れた。

で、代わりの暇つぶしとして、最近 NoSQL について下調べしていて見かけた MongoDB とか言う奴でもいじってみようかなと思いつく。この辺りを参考(飽くまでも参考)にして、Fedora15 の 32bit 環境でやってみた。

実は、Fedora で管理しているMongoDB が「ソフトウェアの追加と削除」で最初からリストアップされてるんだけど、これをインストールしても、なんかちゃんと起動しなかった。

====

まずはインストールだけど、配布元の 10gen を Fedora (というか yum)に認識させるために、ファイル /etc/yum.repos.d/10gen.repo を作って以下の記述を追加する。

[10gen]
name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686
gpgcheck=0

そうすると、「ソフトウェアの追加と削除」にリストアップされるから、以下を選択して[適用]

  • mongo-10gen-server-2.0.1-mongodb_1
  • mongo-10gen-2.0.1-mongodb_1

インストールされたら、起動してみたい所だけど、その前に、なんかデータを保存するフォルダを作っとく必要があるらしい。

$ sudo mkdir -p /data/db/
$ sudo chown `id -u` /data/db

準備ができたので、Fedora の[サービス]ツールで mongod を開始する。

端末で、mongo を起動して動作確認。

$ mongo
> one = {name: "one", value: 1}
> two = {name: "two", value: 2}
> three = {name: "three", value: 3}
> db.things.save(one)
> db.things.save(two)
> db.things.save(three)
> db.things.find()
{ "_id" : ObjectId("4eabd63586daea21618266ca"), "name" : "one", "value" : 1 }
{ "_id" : ObjectId("4eabd63786daea21618266cb"), "name" : "two", "value" : 2 }
{ "_id" : ObjectId("4eabd63a86daea21618266cc"), "name" : "three", "value" : 3 }
> db.things.find({name:"one"}, {_id:0})
{ "name" : "one", "value" : 1 }
> db.things.find({value:2}, {name:1})
{ "_id" : ObjectId("4eabd63786daea21618266cb"), "name" : "two" }
> db.things.find({value:3}, {name:1,_id:0})
{ "name" : "three" }

ちゃんと動いてるらしい。

次は、java コードから動かしてみたい(チュートリアル)。

2011年10月27日木曜日

JavaScript + XSLT の小技

ここ数年、一部界隈でアンチXMLみたいな動きがあったりするが、現実には XML はいろんな場面で使われている。中には数万行のものがあったりするけど、さすがにそのサイズになると、直接、内容を読もうとすると、結構不便だったりする。

というわけで、最近重宝している ブラウザと Javascript と XSLT を使った小技を書いてみる。

こんな感じの XML があるとする。

<?xml version="1.0"?>
<numerals>
  <numeral><arabic>1</arabic><jp>壱</jp><en>one</en></numeral>
  <numeral><arabic>2</arabic><jp>弐</jp><en>two</en></numeral>
  <numeral><arabic>3</arabic><jp>参</jp><en>thee</en></numeral>
</numerals>

これを XSLT で HTML に変換して、ブラウザ上で左下のようなリストを表示し、それぞれのリスト要素に設定されているハイパーリンクをクリックすると、右下の様に詳細が表示されるといった事をしたい。

数字2
日本語
英語two

以下、その方法。

====

リストを表示する XSLT は以下のようなものになる。ポイントは<a>要素を生成しているところで、javascript 関数 showDetail()に arabic 要素を渡すようなリンクが作られる。

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <ul>
      <xsl:for-each select="numerals/numeral">
        <li>
          <xsl:element name="a">
            <xsl:attribute name="href">
              javascript:showDetail(<xsl:value-of select="arabic"/>)
            </xsl:attribute>
            <xsl:value-of select="jp"/>
         </xsl:element>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>
</xsl:stylesheet>

で、次に詳細を表示するためのXSLTは以下で、ポイントはパラメータ arabic をグローバルで宣言しているところ。

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="arabic"/>
  <xsl:template match="/numerals/numeral">
    <xsl:for-each select=".[arabic=$arabic]">
      <table>
       <tr>
         <td>数字</td>
         <td><xsl:value-of select="arabic"/></td>
       </tr>
       <tr>
         <td>日本語</td>
         <td><xsl:value-of select="jp"/></td>
       </tr>
       <tr>
         <td>英語</td>
         <td><xsl:value-of select="en"/></td>
       </tr>
      </table>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

いよいよ、Javascript と それを含むHTMLだけど、ソースは以下のようなものになる。

<html>
 <head>
  <script type="text/javascript" src="sarissa.js"></script>
  <script type="text/javascript">
   function loadXmlFile(fileName) {
     var result = Sarissa.getDomDocument();
     result.async = false;
     result.load(fileName);
     return result;
   }
   function replaceResultArea(xmlDom) {
     var serializer = new XMLSerializer(); 
     var output = serializer.serializeToString(xmlDom.documentElement);
 
     document.getElementById("resultArea").innerHTML = output;
   }
   function showList() {
     var xml = loadXmlFile("test.xml");
     var xslt = loadXmlFile("ul.xsl");

     var processor = new XSLTProcessor();
     processor.importStylesheet(xslt);
     var xmlDom = processor.transformToDocument(xml);

     replaceResultArea(xmlDom);
   }
   function showDetail(arabic) {
     var xml = loadXmlFile("test.xml");
     var xslt = loadXmlFile("detail.xsl");

     var processor = new XSLTProcessor();
     processor.importStylesheet(xslt);
     processor.setParameter("", "arabic", arabic);
     var xmlDom = processor.transformToDocument(xml);

     replaceResultArea(xmlDom);
   }
  </script>
 </head>
 <body onload="showList()"/>
   <div id="resultArea"/>   
 </body>
</html>

冒頭で参照している sarrisa.js はXMLを扱うための JavaScript ライブラリで http://dev.abiss.gr/sarissa/ からダウンロードできる。ただし Chrome がどうもサポートされてないっぽい。本当にそうなら残念(まあ、それほど困らないけど)。

以上、「(1)パラメータを与えつつ、(2)動的に XSLT を適用して、(3)ブラウザ上で表示を切り替える」方法。サンプルなんで、簡単なものだけどなんぼでも応用がきく。(例えば、静的ソース解析ツールが吐いたXML形式のレポートを分析したり、XMIで出力したUMLのドキュメントを解析してメトリクスを試算したりとか。)

ちなみに、個人的には XML それ自体に対する忌避感みたいなのは、全くといって良いほど無い。他の大抵の技術要素と同じく、上手く使えば役に立つし、下手に使えば役に立たないという単純な問題。

2011年10月24日月曜日

簡単な後付けテストで基本を再確認

こんなクラスがあるとする。

public class Foo {
   private final Bar bar = new Bar();
   public String hoge() {
      return this.bar.baz().hoge();
   }
}
見ての通り、Foo は メソッド hoge()の中で、関連オブジェクト bar から取得した baz に、hoge() 処理を移譲している(日本語にすると、なんのこっちゃだが…)。

Bar と Baz は以下のような感じで後回しにしてある。

public class Bar {
   public Baz baz() {  throw new AssertionError();  }
}
public class Baz {
   public String hoge() {  throw new AssertionError(); }
}

ここで、Foo#hoge() をテストするにはどうするか。つまり以下の/* ??? */ のところに何を入れたらテストできるか。

public class FooTest {
   @Test public void hoge() {
      /* ??? */
      Assert.assertEquals("hello", new Foo().hoge());
   }
}

とりあえず2個だけ例を書いてみると(ちょっとずつニュアンスの違うものを何通りも書けるが)、以下のようになる。

@Test public void hoge1() {
   new Expectations() {
      @Mocked("baz") Bar bar;
      @Mocked("hoge") Baz baz; {
         bar.baz(); result = baz;
         baz.hoge(); result = "hello";
      }
   };
   Assert.assertEquals("hello", new Foo().hoge());
}
@Test public void hoge2() {
   new NonStrictExpectations() {
      Bar bar;
      Baz baz; {
         bar.baz(); result = baz; times=1;
         baz.hoge(); result = "hello"; times=1;
      }
   };
   Assert.assertEquals("hello", new Foo().hoge());
}
JMockit を使い始めたばかりだと、結構、とまどうんじゃないかと思う。デバッガで追ってみると、例えば "result = baz" のところで、初期化していないはずの baz に何故かインスタンスが設定されているし、代入されたと思った result が null のままだったりして、普通の Java ソースの感覚で読んでいると訳が分からなかったりする。

とは言ってもまあ、すぐ慣れるし、そうなると逆に、JMockit 無しのテストを考える事が、時折難しく感じられてくる。

ただ、JMockit を使うと後付けテストもかなり簡単になるにはなるけど、やっぱりテストファーストが基本というのは押さえておいた方が良い。この例はもともと簡単だから、それほど大変なテストコードにはならないけど、もっと複雑なものになると、後付けとテストファーストではテストコーディングの生産性が大きく違ってくる。

====

テストファースト版は、こっちに書いた。

2011年10月23日日曜日

Fedora 15 + JavaDB

自宅マシンで javaDB を使って試したいことがあったんだけど、OpenJDK には JavaDB が入っていないらしい。「ソフトウェアの追加と削除」にもリストアップされない。という訳でRPMからインストールしてみた。

まず、ここからダウンロード

で、Oracle サイトのここを見てインストール

$ chmod +x javadb-10_5_3_0-linux-rpm.bin 
$ ./javadb-10_5_3_0-linux-rpm.bin 
# su
# cd javadb-10.5.3.0/
# rpm -ivh sun-javadb-*.rpm

ij コマンド で起動して、試してみる

ij> connect 'jdbc:derby:testdb1;create=true'; 
ij> create table t1 (col1 int primary key, col2 varchar(10));
ij> insert into t1 (col1, col2) values (100, '俺'));
ij> select * from t1;
COL1       |COL2      
----------------------
100        |俺   

まあ問題なさそう。
※実はエラーメッセージとか help とかが文字化けしてたけど、javaコードから使うのがメインだから放置。

2011年10月18日火曜日

Coq をインストールしてみた

今日、現場の昼休みに、最近噂の Coq のチュートリアルを見つけたので、早速インストールしてみる。

Fedora 15 でアプリケーションの追加と削除を開くと、Coq 8.3がリストアップされてきたので、チェックを入れて適用。

さらに、端末を開いて coqide と打ち込んでみると、ひとしきり追加のインストールが実行されたあと、CoqIDE が開いた。 とりあえず、チュートリアル 一回目の練習問題を解いてみるが、初回なんで難しくない。

結構面白い。明日もやろっと。

2011年10月17日月曜日

リファクタリング再読 4章 テストの構築

このブログで、何度も引用してきたけど、10年以上前に読んだリファクタリングが、今読んでも面白い。

面白いと言っても、当たり前の事が普通に書いてあるだけなんだけど、忘れた頃に読み返すと、自分が現場でいつも言っている事と大部分一致していて、若い頃に受けた影響ってやっぱりずっと残るんだと実感する。(さすがに、このレベルの名著ですら、やはり10年も経つとある程度の陳腐化は避けられず、全てに同意はできるわけではないけど…)

今日は 『4章 テストの構築』から、何点か引用して考えてみたい。

====

テストを完全に自動化して、その結果もテストにチェックさせること。(P.90)
xUnit Patterns の言葉で言うと、Manual Interventionは止めようねって事で、まあ、JUnitの基本中の基本。とはいっても、@Test メソッドの中で普通のアサーションで書ける比較を、標準出力に書き出して目視確認してたおバカさんを、こないだ発見した。こんなレベルの人が「JUnit 使用歴○○年です!」なんつってプロジェクトに入り込んできちゃったりするから、この業界っておそろしい。


テストを頻繁に実行せよ。コンパイル時にはテストを局所化して、一日に最低一度は全てのテストを実行せよ。(P.94)
今は普通に CIサーバでコミット時にテスト走らせたり、incremental test とかも普及してるから、一日一度はさすがに10年前って感じがするが、なるべく頻繁に実行すべしってのが、この文の趣意。まあ、なんぼ言っても、他人のテストコードをコケさせておきながら、コンパイルが通ってるってだけでテストもせず、平気でコミットする間抜けが減るどころか永遠に増え続けてるから、気が滅入るが…。


バグレポートを受け取ったら、まずそのバグを明らかにするための単体テストを書け。(P.97)
これは今も昔も良いプラクティスで、余り反論する人を見たことがないけど、数年前までは、いざバグを再現させるテストコードを書こうにも、そもそもテスタビリティが低すぎて、異常に難易度が高い場面もあった。今は、instrumentation を活用したテストフレームワークが普及しているから、そうでもないけど。(テストツールのテスト能力が高まっただけで、ソースコードのテスタビリティは大して進歩していないのが悲しいが)


ここでお話しするのは「単体テスト」です。これはプログラマの生産性を向上するために行うものです。これで品質管理部門が満足するとしても、それは副作用に過ぎません(p.96)
リファクタリング〜xUnit の文脈の中で、「機能テスト」と対置されて語られる「単体テスト(UnitTest)」って、ウォーターフォール・モデルの単体テストを単に自動化しただけのものとはかなり違うんだけど、この勘違いは今でもかなり根強い。 自分が、現場で説明する時には、「外部品質じゃなくて内部品質をあげるためのツールだから」とか言う事がある。分からない人はどう言い換えてもやはり分かってくれないけど、わかる人はなるほどそうかと納得してくれたりする。


不完全なテストでも、書いて実行する方が、実行できない完全なテストよりもましだ。
テストを書けば自分が楽になるって認識が成立しないとプログラマはテストを書きたがらないわけで、つまり下手なプログラマって、テストコードによって自分で自分の作業を楽にする事ができないプログラマなんだけど、その手の輩に無理やりテストを書かせようとするから無理なカバレッジ基準が制定されて、後付けでパスを通そうとするから、却って生産性が下がる。そんなのどうせ、まともなアサーションが書かれるわけないのにさ。

本当は、自覚して UnitTest を使えてるプログラマが、どこにどの程度テストコードを書くか自分で判断すればいい事なんだけど、最低辺も含めて、いろんな人が集まるプロジェクトでは、うーんやっぱ難しいのかな…


大事な事は、一番怪しいと思う部分をテストすることです。それが最も効率の良いテスト方法です。(p.97)
失敗の恐れのある境界条件を考えて、そこを集中的にテストせよ。(p.99)
失敗すると予想される時に、例外が上がることをテストし忘れないこと。(p.100)
境界条件や例外条件なんかをテストするにしても、テスト駆動/テストファーストでやるのが、結局は一番楽で確実なんだけど、現場のレベルによってはいくら言い聞かせても浸透しない。後付けでテスト書いたって、カバレッジだけに必死になって、どこが「怪しい」かなんて完全に眼中の外になってしまう事が分かりきってるんだけどね。


テストで全てのバグが見つからないからといって、テストを書くのを止めてはならない。ほとんどのバグはテストで補足される。
そもそも開発者が要件を誤解していたり、忘れていたりしたら、そういった認識エラーは本体コードと同様にテストコードにも織り込まれてしまうから、開発者テストだけで捕捉するのは論理的に不可能。そういった限界がある事を認識した上で、機能テストも絡めた全体的なテスト計画を立てるのが常道。


====

あと、引用ではないけどテスト関連で付け加えると、『リファクタリング』に載ってるリファクタリング・パターンのほとんどに「コンパイルしてテストする」と言う手順が組み込まれている事も、改めて確認できる。

この10数年で、リファクタリングという言葉が普及したおかげもあってか、動いてるコードに手を入れる心理的ハードルが昔より低くなったけど、「動いているコードはいじるな」っていう昔からの不文律を乗り越えて良いのは、テストコードという前提あってこそという、基本中の基本が蔑ろにされ始めてるのを感じる。テストもせずにコードをいじって、CIサーバから常時テスト失敗メールが飛んできてるのに何とも思わない連中が、どんどん増えてきている気がする。

出来ている現場の出来てる人達はびっくりするかもしれないけど、まだまだこんな感じの現場が多いんだよな… いかん、また眠れなくなってくる…

2011年10月10日月曜日

自動テストのレベル分け

最近は、「xUnit で UnitTest を書いてカバレッジとってます」なんて、どいつもこいつも謳ってるけど、お前らマジかと。それで出来てるつもりなのかと…?

というわけで、秋の朝がさわやかなので、自動テストの出来てる度合いの段階区分を考えてみたい。

  Level 3: 出来てる:常時テスト成功、高カバレッジ

ちゃんとできてるプロジェクトの出来る子ちゃん達からしたら、取り立てて言及することもない当たり前の状態だと思う。

"Clean Check-In" が普通に守られているから、当然、テストも常時全件成功する。まあ基本中の基本。

で、TDD/TestFirst で開発してるから、開発の最初期から高カバレッジ(計測対象範囲内でのカバレッジ基準充足)で始まり、進捗に伴いソースコード量が増えていく中でも、高カバレッジを維持したまま推移する。


Level 2:怪しい:ほぼ常時テスト成功、低カバレッジ

"常時"かつ"全件"、テストが成功してはいるけど、カバレッジが低いプロジェクト。まあ、後付けでテストを書こうとすると、えてしてそんな感じになる。

原因としては、単にスキルがないから仕方なく後付けになったり、確信的にテストをサボっていたり、いろいろあるにせよ、結果的に以下のような感じになる。

①:テスト・コーディングに凄い余計な時間が掛かる。テスト・ファーストでやってさえいれば自然に得られたはずの保守性・メンテ性が備わってない低テスタビリティ・コードに無理やりテストコードを書いてく事になるから、まともな生産性は無理。

② :テスティング・ポイントがウヤムヤになる。本体コードを書いていた時点では脳内にあったはずのコーディング意図がとっくに揮発した状態で、テストコードを後付けする破目になる。何をテストしてるのか本人が分かってない状態。

③ :テストコードがザルになる。①で指摘した生産性の低いテストコードを、②で指摘した曖昧状態のプログラマが書いてくわけだから、「何かをテストする」事ではなくカバレッジを通す事が自己目的化してしまい、結果、Assertion も Expectation も不十分で、単に実行経路に含まれただけの空虚なテストコードになってしまう。

まあ、後付けテストの全てがそんな糞テストコードばかりとは限らないけど、テストコードは本体コードより先に書いとくに越したことはない。それが普通なのだと認識するだけで、悪い事がいろいろ避けられて良い事がいろいろ増えてくる。


Level 1:下手糞:常時テスト失敗、低カバレッジ

低カバレッジでも、取り敢えずテストが存在して差し当たり成功していれば、まだ見どころはある。また、たまたま間違えてテストを壊す事もあると思うが、そんなのすぐ直せば良い事で別に問題じゃない。

だけど、テストが通らないコードをコミットするのが常態化していたり、CIサーバでテストが失敗してるのに放置されていたりしたら、それはかなりの低スキル・チームの疑いがある。

実は、そうしたプロジェクトでは、Level 3 の状態を志してはいたもののスキル不足で健闘虚しくって感じじゃなくて、 Level 1 の状態で普通・正常だと思ってる開発者が大勢を占めていたりする。酷いのになると「今までに経験したプロジェクトではそれが普通だったし、テストが壊れても気にしない方がリファクタしやすい。」なんて事を真顔で主張したりする(リファクタするためにこそテストコードが必要だという最低限の常識を弁えていれば、そうした発言は出ないんだけど…)。

そうやって考えてくと、そもそも技術者の○○使用経験とか○○歴とかって何なのだろうという問題に突き当たるが、これは別の機会に考えてみたい。あと、Level 1 で生じている問題には、余りにも劣化した形で普及し実践されている『リファクタリング』もあるのだけど、これも別の機会に考える。

朝から、気が滅入ってきた。山でも歩いてこよっと。

2011年10月9日日曜日

Eclipse から XSLT 2.0 を使うやり方

今更だけど、Eclipse の XSL Developer Tool で XSLT2.0 を使うやり方が分かったのでメモっとく。
  • まず、Saxon home edition の新しいやつ (saxonhe9-3-0-8j.zip)をダウンロード。
  • ダウンロードしたZIPを適当なとこに展開して、中の saxon9he.jar を出しておく。
  • Eclipse のメニューを [Window]>[Preference]>[XML]>[XSL]>[Java Processors] ってたどる。
  • Add を押して開いたダイアログボックスで、だいたい以下の様な感じで入力。追加したやつのチェックボックスを選択しておく。
これで使えるようになる。 試しに、昔のポストで書いた XSLT で平均と分散を計算するやつを、XSLT2.0 を使ってやってみる。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" />
  <xsl:template match="/list">
    <xsl:variable name="result">
      <xsl:call-template name="stat">
        <xsl:with-param name="items" select="item" />
        <xsl:with-param name="subtotal" select="0" />
        <xsl:with-param name="count" select="0" />
      </xsl:call-template>
    </xsl:variable>
    <average>
      <xsl:value-of select="$result/avg" />
    </average>
    <varianece>
      <xsl:value-of select="$result/sumOfD2 div $result/count" />
    </varianece>
  </xsl:template>
  <xsl:template name="stat">
    <xsl:param name="items"/>
    <xsl:param name="subtotal"/>
    <xsl:param name="count"/>
    <xsl:choose>
      <xsl:when test="$items">
        <xsl:variable name="t">
          <xsl:call-template name="stat">
            <xsl:with-param name="items" select="$items[position() > 1]" />
            <xsl:with-param name="subtotal" select="$subtotal + $items[1]" />
            <xsl:with-param name="count" select="$count + 1" />
          </xsl:call-template>
        </xsl:variable>
        <sumOfD2><xsl:value-of select="($items[1]-$t/avg)*($items[1]-$t/avg)+$t/sumOfD2"/></sumOfD2>
        <count><xsl:value-of select="$t/count"/></count>
        <avg><xsl:value-of select="$t/avg"/></avg>
      </xsl:when>
      <xsl:otherwise>
        <sumOfD2>0</sumOfD2>
        <count><xsl:value-of select="$count"/></count>
        <avg><xsl:value-of select="$subtotal div $count"/></avg>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

前回と同じ入力XMLから同じ結果が得られることが確認できた。

前にやった時は、結果ツリーフラグメントの制約から template の結果を文字列にして返さざるを得なくて煩わしかったけど、XSLT 2.0 の temporary tree では 普通の XML データとして扱えるので、やってる事をより直截に表現したコードになった。