<< < 1 2 3 > >>

How Tomcat Works を購入した


私はServletと出会ったのは3年ほど前と、比較的日が浅いです。
生のServletと格闘した日々はほとんどなく、Struts等のWebフレームワークを通してしかServletを知りません。
むしろServlet APIは意識させないことがWebフレームワークの特性なので、Servletはあまり知らない部類の人間でしょう。

そんなわけで前から欲しかった How Tomcat Works を購入しました。
現在は eBookのみこちらから購入できるようです。
既に読了して解説されている方もいます。
How Tomcat Works その1 tokobayashiの日記:

この本は最小限のHTTPサーバーからTomcatにまで育てていく過程を解説している本です。
対応しているTomcatのバージョンはTomcat4、Tomcat5です。
Tomcat6が出ている今となってはやや古いのですが、私には十分かと思って購入しました。
ちなみに、本書のIntroにある想定読者は以下の通りです。

  • Servlet/JSPをTomcat上で使用する人、興味のある人
  • Tomcatの開発に参加したい人
  • Tomcatのような大きなシステム開発を学びたい人
  • Tomcatの設定やカスタマイズを行いたい人

一番目の理由はServlet/JSPの動きに興味があったからですが、Tomcatのような複雑で大きなシステムの成り立ちも勉強出来そうで、これから楽しみです。

jMockitで依存クラスをモックオブジェクトに差し替える


これまでモックオブジェクトを作成する EasyMock や jMock を見てきました。
モックオブジェクトをパラメータとしてテスト対象に渡せる場合は、問題ないのですが、テスト対象のクラスの中で new されていると、一気にテストは困難になります。

jMockit は 実行時にクラスをモックオブジェクトへ差し替える機能を提供するツールです。
バイトコード操作の為のJVMへのコマンドライン引数、 -javaagent を用いてクラスの差し替えを行います。

Eclipseの場合はJREの設定を変える必要があります。JVMの引数に -javaagent:jmockit.jar を追加します。
設定自体は簡単なのですが、この点が壁になるかもしれません。

テスト対象は以下の通りです。MockSampleクラスのコンストラクタでサービスクラスを初期化しています。
この様に、テスト対象のクラス内で new されたクラスはモックオブジェクトを適用するのが困難です。
jMockitはこういったクラスをモックオブジェクトに差し替える機能を提供しています。
 public class MockSample {
     private ServiceImpl serviceImpl;
     public MockSample() {
         this.serviceImpl = new ServiceImpl();
     }

     public String execute() {
         return serviceImpl.echo();
     }

     public static class ServiceImpl {
         public String echo() {
             return "hoge";
         }
     }
 }
テストクラスは以下の通りです。差し替えられたMockServiceImplが実行されます。
 import mockit.Mockit;
 import org.testng.annotations.Test;
 import test.MockSample.ServiceImpl;
 import static org.testng.Assert.*;

 public class TestJMockitSample {
     @Test public void jmockTest() {
               // クラスの差し替え
         Mockit.redefineMethods(ServiceImpl.class, MockServiceImpl.class);
         MockSample sample = new MockSample();
         assertEquals(sample.execute(), "Mocked!!!");
     }
     public static class MockServiceImpl {
         public String echo() {
             return "Mocked!!!";
         }
     }
 }
コードはかなり簡単ですね。
JVMの引数をいじるため、Eclipseを設定したり、Mavenを使っている場合も一工夫がいる点が面倒ですが、
一度設定してしまえばかなり完結にテストをかける強力なツールです。

Mockソリューションを試す。 EasyMock と jMock の比較


ここまでで EasyMock と jMock の簡単な使い方を見てきました。
まだまだ使いこなしてはいないのですが、ここまでの知識をまとめておきたいと思います。

まず、Mock と Stub の違いについて。
EasyMock とか jMock に触れる前は、Mock と Stub が自分の中で明確に区別できていませんでした。
Wikipediaによると両者は異なるもので、Mock は Stub と比較して正しく利用されているかを検証する機能が付加されているとの事です。
確かに今まで Stub と認識していたものは、「呼ばれたときに値を返す」部分を代用する様なものでした。
しかし EasyMock や jMock はメソッドが想定されたパラメータ、順序で呼ばれるかを検証することができました。
この点で Stub とは 区別される物のようです。Mock が Stub の一種と言うことですね。

さて、 EasyMock、jMock ともに機能に大きな違いはなさそうです。
  • interface に対する Mockの作成
  • concrete class、abstract class に対する Mockの作成
  • メソッドが呼ばれているかを検査
  • メソッドが想定するパラメータで呼ばれているかを検査
  • メソッドが想定する順番で呼ばれているかを検査
  • メソッドの戻り値を偽造

また、基本的な使い方の比較をコードでしてみます。
まず、Mock を作る部分。
 //EasyMock
 mock = createMock(IService.class);

 // jMock
 context = new Mockery();
 mock = context.mock(IService.class);
次に想定される結果を検査する部分。
EasyMockの場合。
 // モックオブジェクトに対して想定される呼び出しを記述します。
 mock.echo();

 // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
 replay(mock);
 sample.execute();
 verify(mock);
jMockの場合
 // モックオブジェクトに対して想定される呼び出しを記述します。
 context.checking(new Expectations() {
    {oneOf(mock).echo();
     }});

 // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
 sample.execute();
 context.assertIsSatisfied();
EasyMockは想定される呼び出しを記述し、replay〜verifyメソッドで囲われた部分を検査します。
jMockは想定される呼び出しを context.checkingメソッド内のExpectationsクラス初期化子に記述し、context.checking〜context.assertIsSatisfiedメソッドで囲われた部分を検査します。
個人的には jMock の書き方の方が決められた場所が明確で好きなのですが、好みの問題かもしれません。

他にも "Mock" という文字列を含むソリューションがありましたが、試すまでには至りませんでした。
まず rMock なのですが、対応していないのかもしれないのですが TestNGにからの利用方法がわからなかったので、試しませんでした。
ただ、ドキュメントを読む限り BDD的な要素を絡めているようでおもしろそうではあります。
あと、ClassMock と呼ばれるものもありましたが、こちらは Mock と呼ばれるには必要な、正しく呼ばれているかを検証する機能がなく、簡単にクラスを作るための仕組みを提供している様です。
Mockとしての機能は他のソリューションに委ねている様で、ドキュメントにも jMock と連携した場合の例が載せられています。

Mock の使いどころとして浮かぶのは Servlet とかのテストなのではないでしょうか。
つまり擬似的な実装クラスを作るのが面倒な部分です。
Wicket だと WicketFilter クラス何かは Mock を使ったテストなんかは有効なのではと思います。

Mockソリューションを試す。 EasyMock + classextension


jMockではインターフェース以外のクラスについてモックオブジェクトを作れましたが、EasyMockについてもEasyMock Class Extension を導入することで実現できます。
まずEasyMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
 <dependency>
    <groupId>org.easymock</groupId>
    <artifactId>easymockclassextension</artifactId>
    <version>2.4</version>
 </dependency>

以前、インターフェースに対してモックオブジェクトを作成する部分はこんな感じでした。
 // IServiceクラスのモックを作成します。
 mock = createMock(IService.class);
EasyMock Class Extensionを導入する場合は、classextensionパッケージ下にある上記と同名のメソッドを使用するようにします。
具体的には、org.easymock.EasyMock.createMock から org.easymock.classextension.EasyMock.createMockメソッドを使用するようにimport文を変更します。
他にも replyメソッドやverifyメソッドもclassextensionパッケージ下のものを使うことになるので、インポート文に下記を加えておくとよいでしょう。
 import static org.easymock.classextension.EasyMock.*;

これでjMockと同様に、インターフェース以外のモックオブジェクトの作成が可能になります。

Mockソリューションを試す。 jMock


前回はEasyMockについて簡単なサンプルを作って見ました。
今回はjMockで同じような事を試して、EasyMockとどう違うか見ていきます。

まずjMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
 <dependency>
       <groupId>org.jmock</groupId>
       <artifactId>jmock</artifactId>
       <version>2.5.1</version>
     </dependency>
 </dependency>
テスト対象のクラスは前回と同じです。再掲します。
 package test.easymock;

 public class MockSample {
     private IService service;
     public MockSample(IService service) {
         this.service = service;
     }
     public void execute() {
         service.echo();
     }
     public String getName() {
         return service.get();
     }
     public interface IService {
         public void echo();
         public String get();
     }
 }
モックオブジェクとを作成して、テスト対象のクラスに注入して、適切に呼ばれているかをテストするという流れはEasyMockと同様です。
想定される呼び出しの記述にインスタンス初期化子を使っている所が個人的には見やすく感じました。
戻り値の指定も同様にインスタンス初期化子の中に記述します。
 package test.easymock;

 import org.jmock.Expectations;
 import org.jmock.Mockery;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;

 import test.easymock.MockSample.IService;

 public class TestJMockSample {
     private Mockery context;
     private MockSample sample;
     private IService service;
     @BeforeMethod
     public void setupMock() {
         // IServiceクラスのモックを作成します。
         context = new Mockery();
         service = context.mock(IService.class);
         // MockSampleのコンストラクタの引数としてmockオブジェクトを渡します。
         sample = new MockSample(service);
     }
     @Test public void callEcho() {
         // モックオブジェクトに対して想定される呼び出しを記述します。
         context.checking(new Expectations() {
             {oneOf(service).echo();
             }});
        
         // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
         sample.execute();
         context.assertIsSatisfied();
     }
     @Test public void callGet() {
         // モックオブジェクトに対して想定される呼び出しと戻り値を記述します。
         context.checking(new Expectations() {
             {oneOf(service).get();
             will(returnValue("hoge"));
             }});
        
         // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
         // また、戻り値が想定された値かをチェックしています。
         assertEquals(sample.getName(), "hoge");
         context.assertIsSatisfied();
     }
 }
ドキュメントを読む限りEasyMockと機能的にはそれほど大差ないのではないかと思ったのですが、jMockではインターフェースのないクラスもテストすることができます。
jMockでは内部的に使用しているProxyクラスに関して、reflectionを使う場合とcglibを使う場合に切り替ることができます。
cglibを使うことで、インターフェースのないクラスのモックオブジェクトを作成出きるようになっています。

インターフェースのないクラスのモックオブジェクトを作る場合は、pom.xmlに下記を追加します。
 <dependency>
   <groupId>org.jmock</groupId>
   <artifactId>jmock-legacy</artifactId>
   <version>2.5.1</version>
 </dependency>    
インターフェースのないクラスをテストするときは、Mockeryクラスを生成するときに、インスタンス初期化で下記の様に指定します。
 context = new Mockery() {{
    setImposteriser(ClassImposteriser.INSTANCE);
 }};

この様に、jMockはEasyMockと比べるとインスタンス初期化子で記述する点や、インターフェースがなくてもモックオブジェクトを作成できる点で、優れている所も多そうです。

Mockソリューションを試す。 EasyMock


ユニットテストをやってると、クラスの依存関係によってテストクラスの作成が難しいことがよくあります。
今の現場は特にユニットテストを作成する事が多いので、この障害にぶち当たることが頻繁です。

こういったケースではモックオブジェクトを作成したりするのですが、Javaではモックオブジェクトを作るためのライブラリーがいろいろあるようです。
ざっと見た限り、jMock, rMock, djUnit(Virtual Mock) EasyMock等が上げられます。

最終的にはこれらの違いを考察したいのですが、今回はEasyMockを試してみます。

まずEasyMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
 <dependency>
   <groupId>org.easymock</groupId>
   <artifactId>easymock</artifactId>
   <version>2.4</version>
 </dependency> 

テスト対象のクラスは以下のようなものを用意しました。
コンストラクタでIServiceクラスをインターフェースとするクラスを受けとります。
このクラスのメソッドを実行するだけのメソッドexecute()、getName()があります。
 public class MockSample {
     private IService service;
     public MockSample(IService service) {
         this.service = service;
     }
     public void execute() {
         service.echo();
     }
     public String getName() {
         return service.get();
     }
 }
 interface IService {
     public void echo();
     public String get();
 }
モックオブジェクトの使用法としては、まずモックオブジェクトを作成する処理を記述します。
createMock(IService.class);のようにしてモックオブジェクトを作成します。この部分はGenericsを使っているため、キャストもなくすっきりしていますね。

次に、生成したモックオブジェクトを何らかの方法でテスト対象のクラスに注入して、適切に呼ばれているかをテストしていきます。
想定される呼び出しを記述し、replay(mock)〜verify(mock)の間で想定通りに呼び出されているかをテストしていきます。
また、モックオブジェクトが呼び出された時の戻り値も指定することができます。
 package test.easymock;

 import static org.easymock.EasyMock.*;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;

 public class TestEasyMockSample {
     private IService mock;
     private MockSample sample;
     @BeforeMethod
     public void setupMock() {
         // IServiceクラスのモックを作成します。
         mock = createMock(IService.class);
         // MockSampleのコンストラクタの引数としてmockオブジェクトを渡します。
         sample = new MockSample(mock);
     }
     @Test public void callEcho() {
         // モックオブジェクトに対して想定される呼び出しを記述します。
         mock.echo();

         // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
         replay(mock);
         sample.execute();
         verify(mock);
     }
     @Test public void callGet() {
         // モックオブジェクトに対して想定される呼び出しと戻り値を記述します。
         expect(mock.get()).andReturn("hoge");

         // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
         // また、戻り値が想定された値かをチェックしています。
         replay(mock);
         String str = sample.getName();
         assertEquals(str, "hoge");
         verify(mock);
     }
 }

このように、モックオブジェクトは、それ自身が正しく呼ばれているか検証することができます。
ただ、内部的にはjava.lang.reflectパッケージのProxyクラスを用いていることから、インターフェースの存在が必須になっているようです。
この点がEasyMockの弱点かなと思います。
また、テスト対象のクラスにモックオブジェクトを登録する必要があります。
うまく登録できる入り口があればよいのですが、この点がテストクラスの作成を困難にすることが多々あります。
この点はモックに限らないことですが。

次はjMockを見ていく予定です。

TestNGでマルチスレッドテストを試してみた


この前m2eclipseを使ってみたのも、TestNGを試してみるためでした。
TestNGのマルチスレッドなテストを試してみたかったんです。

ちなみにJunitはずっと使ってるけど、3.x系列しか使った事がなかったので、アノテーションベースのテストクラスも初体験でした。
アノテーションに関してはJunit4.x系列なら扱えるんですけどね。

さてさて、マルチスレッドなテストを試すため、下記のようなスレッドアンセーフティーな実装をしてみました。
 package test;
 public class UnSafety {
 private static StringBuffer sb;

     public static String unsync() {
         sb = new StringBuffer();
         sb.append(“松”);
         sb.append(“竹”);
         sb.append(“梅”);
         return sb.toString();
     }
 }
TestNGでマルチスレッドなテストをするためには、アノテーションのパラメータにスレッドの数(threadPoolSize)と実行回数(invocationCount)を指定します。
下記の例ではスレッド数3で、実行回数を10回としています。
 package test;

 import org.testng.annotations.Test;
 import static org.testng.Assert.;

 public class TestUnSafe {
     @Test(threadPoolSize=3, invocationCount=10)
     public void testUnSafety() {
         assertEquals(UnSafety.unsync(), “松竹梅”);
         }
     }
 }
実行結果はこんな感じ。ばっちりテストできてるみたいです。
 PASSED: testUnSafety
 PASSED: testUnSafety
 PASSED: testUnSafety
 PASSED: testUnSafety
 PASSED: testUnSafety
 PASSED: testUnSafety
 PASSED: testUnSafety
 FAILED: testUnSafety
 java.lang.AssertionError: expected:<松竹梅> but was:<松竹梅梅梅>
 at test.TestUnSafe.testUnSafety(TestUnSafe.java:9)
 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
 at java.lang.Thread.run(Thread.java:595)
 ... Removed 16 stack frames
 FAILED: testUnSafety
 java.lang.AssertionError: expected:<松竹梅> but was:<松竹梅梅>
 at test.TestUnSafe.testUnSafety(TestUnSafe.java:9)
 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
 at java.lang.Thread.run(Thread.java:595)
 ... Removed 16 stack frames
 FAILED: testUnSafety
 java.lang.AssertionError: expected:<松竹梅> but was:<松竹梅梅>
 at test.TestUnSafe.testUnSafety(TestUnSafe.java:9)
 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
 at java.lang.Thread.run(Thread.java:595)
 ... Removed 16 stack frames* 
マルチスレッドとは関係ないのですが、TestNGではexpected(予想値)とactual(実行値)の引数の順番が、Junitと逆なんですね。
エラーメッセージを見て戸惑いました。

m2eclipse で お手軽 Maven


Mavenは使ってたけど、Eclipseとの統合はちゃんとやってませんでした。
というか、Maven使い始めの頃に試していたのですが、Maven自体の理解がちゃんとできていなかったので、Eclipse pluginは使わず仕舞いでした。
コマンドラインでは TestやDeoloyに大活躍だったのですが、改めてEclipse Pluginの方も利用する事にしてみました。

利用した感想としては、人生だいぶ損してたなって感じです。
Eclipseと連携した動作もすごく快適です。依存 jar のバージョンが上がってもすぐに追従できます。
まぁMavenの便利さは今に始まった事ではないんで、ハマった所をメモっておきます。

依存jarのソースを一緒に取得したい時があります。
m2eclipseの設定で、"Download Artifact Sources" を Onにすると、自動で依存jarのソースまでとってきてくれます。
Wicketなんかは特にソースまで追っかけることが多いので、この機能は大変便利です。
ただこの機能は私の環境ではまだまだ不安定でした。
自動でとってきてくれるのが一番なんですが、以下の操作でもソースをとってこれます。


やや面倒ですが、いままでの事を考えると便利ではあります。

次にハマったのが<classifier>属性。
この存在を知らずに TestNG が落とせませんでした。
classifier属性は、その名の通り分類を表す属性で、TestNGの場合この属性にjdk15を指定しないといけません。
以下、pom.xmlの抜粋です。
     <dependency>
       <groupId>org.testng</groupId>
       <artifactId>testng</artifactId>
       <classifier>jdk15</classifier>      
       <version>5.8</version>
       <scope>test</scope>
     </dependency>

まだまだMavenの奥は深そうですが、Eclipse Pluginも便利ですし、これからもお世話になりそうです。

Genericsとeraser


この前 Generics を調べてたときに、Genericsなクラスをjadで逆コンパイル掛けるととすっかりGenerics宣言がとっぱらわれてました。
コンパイル時にeraserによって全てのGenerics宣言はきれいさっぱりなくなっちゃうと思ってたけど、そうではないらしい。

イレイジャではジェネリクスの何が消えるのか 凪瀬 Blog
 しかし、型の宣言に関する部分はリフレクションで拾うことができます。ここが混乱する部分ですね。

     * 親クラスを継承する際に投入しているジェネリクス型パラメータ
     * インターフェースを実装する際に投入しているジェネリクス型パラメータ
     * メソッドの引数・戻り値に用いられているジェネリックな型とそこに渡される型パラメータ
     * フィールドに用いられているジェネリックな型とそこに渡される型パラメータ 
へー。
と言うことで検証してみました。
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;

 public class Generics {
     public static void main(String[] args) {
         try {
             Method method = Hoge.class.getMethod("getList", List.class);
             Field field = Hoge.class.getDeclaredField("list");
             System.out.println(method.getGenericParameterTypes()[0]);
             System.out.println(method.getGenericReturnType());
             System.out.println(field.getGenericType());
             System.out.println(Hoge.class.getGenericInterfaces()[0]);
             System.out.println(Hoge.class.getGenericSuperclass());
         } catch (SecurityException e) {
             e.printStackTrace();
         } catch (NoSuchMethodException e) {
             e.printStackTrace();
         } catch (NoSuchFieldException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }

     public static class Hoge extends Fuga<Integer> implements IHoge<String>{
         private List<Number> list;
        
         public List <String> getList(List<Integer> integerList) {
             List<String> stringList = new ArrayList<String>();
             for(Integer intValue : integerList) {
                 stringList.add(intValue.toString());
             }
             return stringList;
         }
     }
     interface IHoge<T> {
     }
     public static class Fuga<T> {
        
     }
 }
実行結果はこんな感じ
 java.util.List<java.lang.Integer>
 java.util.List<java.lang.String>
 Generics.Generics$IHoge<java.lang.String>
 Generics.Generics$Fuga<java.lang.Integer>
 java.util.List<java.lang.Number>
Genericsの情報はgetGenericXXX系のメソッドでとれます。
ソースを追った感じでは、最終的に下記のClassクラスのメソッドで情報をとってるみたいです。
 private native Field[]       getDeclaredFields0(boolean publicOnly);
 private native Method[]      getDeclaredMethods0(boolean publicOnly);
 private native Class[]   getDeclaredClasses0();
ここからはnativeなメソッドなんで追ってませんが、classファイルにはGenericsな情報も一部含まれてるという事みたいですね。
逆コンパイルの結果を信用し過ぎてました。

クラステストとユニットテスト


JunitとかTestNGを使ってクラスをテストするのは、いわゆる単体テストとは違うと思うんです。
JUnitとかが取り沙汰される前は、単体テストと言えば画面も業務ロジックもひっくるめた機能の事を言ってたと思うんですね。
ところがこれが混同される事が結構あるように感じるんです。

例えば90%の品質の部品群が組合わさった製品は90%より低い品質になっちゃうわけで、小さな部品の一つ一つの性能が上げるってのは非常に重要です。
そういう意味でクラスのテストは品質を挙げるためには有効な手段だと思うんですが、部品の組み合わせが正しいかという所まではテストできないですよね。
なので、これまで「単体テスト」と呼ばれていたものも行う必要があると思うんです。

ところが、JUnitを説明する時に「ユニットテスト」とか「単体テスト」とか言っちゃうと、これまでやってた「単体テスト」を置き換えるものと考えてしまう人がいる。
むしろコーディングの一貫だと説明した方がしっくりくると思うんですけどね。
んーでもコーディングと単体テストを合わせて「製造」と言ってる所もあるから、これでも誤解は解けないかな。

JUnitとかTestNGをきちんと運用している組織を見てみたいなぁ。