モック・オブジェクト 対 コンテナ内テスト

Last update : July 6 2002
Doc for : v1.3

Cactusについて
  • Cactus とは
  • ニュース
  • 変更履歴
  • 特徴/開発状況
  • 目標
  • ロードマップ/ToDo
  • 協力者
  • 協力者募集
  • Cactus ユーザ
  • テスト済環境 ...
  • ライセンス


  • ダウンロード
  • ダウンロード


  • ドキュメント
  • Cactus の仕組み
  • さぁ始めよう
  • モック対コンテナ
  • Javadocs
  • └Javadocs
  • よくある質問


  • Howto ガイド
  • クラスパス Howto
  • 設定 Howto
  • アップグレードHowto
  • テストケース Howto
  • セキュリティHowto
  • Ant Howto
  • HttpUnit Howto
  • サンプル Howto
  • EJB Howto
  • IDE Howto
  • JUnitEE Howto


  • サポート
  • Bug DB
  • メーリングリスト


  • その他
  • 名前の由来
  • ロゴコンテスト
  • 参考文献
  • アクセス状況
  • └WebAlizer


  • 開発者向け
  • CVS
  • コード規約
  • ビルドの結果


  • モック・オブジェクト 対 コンテナ内テスト

    Note この文章を通じて、モック・オブジェクトを MO と、コンテナ内 (In-Container)戦略を IC と略記します。 Throughout this article, we'll use the abbreviations MO for Mock Objects and IC for In-Container strategy.


    MO とサーバ側テスト

    MO は Java クラスの単体テストをするための一般的な戦略です。 Servlet や Filter、Taglib、EJB といったサーバ側コードの単体テストにも、 そのまま使用することができます。 MO に関する詳細はオリジナルの文書である "Endo-Testing : Unit Testing with Mock Objects" を参照してください。これだけ見ると、 MO は Cactus の競合のようにも見えます。 しかし、この文章の残りの部分では、 MO は Cactus を置き替えるものではなく、 実際には Cactus を補完するものであるということを説明します。 MO is a strategy to unit test java classes in general. As such it is also applicable to unit test server side code like Servlets, Filters, Taglibs, EJBs, ... See the original article "Endo-Testing : Unit Testing with Mock Objects" for full details on MO. As such it is a challenger to Cactus but we'll see in the rest of this article that it is in fact complementary rather than a replacement.

    MO の目的は、 周辺オブジェクトから独立した形でメソッドの単体テストを行うことです。 このため、MO アプローチでは実際のオブジェクトの代わりに、 それをシミュレートするコピー(モック・オブジェクト)を使って 周辺オブジェクトを偽装します (MO の偽装オブジェクトはスタブとして働き、 その中でいかなるロジックも実装されないことに注意してください。 モック・オブジェクトの全ての挙動は単体テスト自身によって制御されます)。 これにより、周辺環境の影響を受けることなく、 メソッドの精細な単体テストを行うことが可能になり、 そのロジックを単体テストすることだけに集中できるようになります。 The main goal of MO is to unit test a method in isolation of other domain objects. Thus, the MO approach is to fake domain objects by using simulated copies instead of the real objects (be careful MO does not implement any logic in it's fake objects, that would be "stubbing". All behaviours of mock objects are controlled by the unit test itself). This enables to finely unit test the method with no environment "noise" and to concentrate on unit testing its logic.

    Servlet のテストに使用する場合、モック・オブジェクトは Servet コンテナをシミュレートする必要があります。言い替えると、 Servlet API のモック実装が提供される必要があります(もちろん、 シミュレートされる必要があるのはテストされるメソッドで使用されている Servlet API メソッドだけです。 ホワイト・ペーパーで紹介されているアプローチは、 小さなモック・オブジェクトから出発して、 必要になったときにモック実装を追加していくというものです。 また、コンパイル時や(JDK 1.3 の動的プロキシーを使って)実行時に モック実装を生成することも可能です。 参考文献参照)。 When applied to servlet testing, it means mock objects need to be provided for simulating the servlet container. In other words a mock implementation of the Servlet API need to be provided (of course only the Servlet API methods used in the classes under test need to be mocked and the approach described in the white paper is to start small and develop the mock implementations as they are needed - Note that mock objects can even be generated at build time or at run time with JDK 1.3 dynamic proxies, see resources-).

    一方、Cactus の戦略は Servlet API の実装を提供する本物のコンテナに依存しています。 この文章の残りの部分では、 各々のアプローチについて利点欠点を論じた上で結論をまとめます。 Cactus' strategy on the other hand has been to rely on the real container to provide the implementation of the Servlet API. In the rest of this article, we'll discuss the pros and cons of each approach and draw a conclusion.

    まずは MO をより良く理解するための例から始めましょう。 Let's start first by an example to understand better what are MO.



    Cactus を使った簡単なテスト・ケース : Simple test case using Cactus :

    [...]
        MyServlet myServlet = new MyServlet();
    [...]
    
    public void beginXXX(WebRequest theRequest)
    {
        theRequest.addParameter("param1", "value1");
        theRequest.addParameter("param2", "value2");
    }
    
    public void testXXX()
    {
        myServlet.init(config);
    
        myServlet.myMethod(request, response);
        assertEquals("some value", session.getAttribute("some_name_set_in_mymethod"));
    }
    
    public void endXXX(WebResponse theResponse)
    {
        String result = theResponse.getText();
        assertEquals("<html><head/><body>A GET request</body></html>", result);
    }
    
    

    モック・オブジェクトを使った簡単なテスト・ケース : Simple test case using Mock Objects :

    [...]
        MockHttpServletRequest myMockHttpRequest = new MockHttpServletRequest();
        MockHttpServletResponse myMockHttpResponse = new MockHttpServletResponse();
        MockServletConfig myMockServletConfig = new MockServletConfig();
        MyServlet myServlet = new MyServlet();
    [...]
    
    public void testXXX()
    {
        myMockHttpRequest.setupAddParameter("param1", "value1");
        myMockHttpRequest.setupAddParameter("param2", "value2");
    
        myMockHttpRequest.setExpectedAttribute("some_name_set_in_mymethod", "some value");
        myMockHttpResponse.setExpectedOutput("<html><head/><body>A GET request</body></html>");
    
        myServlet.init(myMockServletConfig);
        myServlet.myMethod(myMockHttpRequest, myMockHttpResponse);
    
        myMockHttpRequest.verify();
        myMockHttpResponse.verify();
    }
    

    MO と IC の違い

    MO 対 IC の利点・欠点の比較 Comparison of pros and cons of MO versus IC.

    Note この表は MO を使用することの利点、 欠点についての包括的な表ではありません。 これは(Cactus が焦点としているところの)サーバ側コードのテストにおける 利点・欠点により焦点をあてています。 This table is not meant to be comprehensive in term of benefits/inconvenients of using MO. It is more focused on pros and cons of MO when used for unit testing server side code (i.e. what Cactus is focusing on).

    Note '+' は肯定的なポイントです。 たとえば、MO 列の '+' は MO が IC に対して有利な点を表わしています。 A '+' indicates a positive point.For example a '+' in the MO column shows that it has an advantage over IC.

    問題点   MO   IC  
    MO は周辺オブジェクトの準備ができる前(つまり、それが実装される前、 または実装を選択する前)にメソッドをテストすることが可能です。 したがって、たとえばコンテナを選択する前に Servlet コードを書くことが可能です。 このことは、「インフラの選択は可能な限り遅らせる」 「単体テストを先に書く」と教える XP とうまく調和します。 MO let us test methods even before the domain objects are ready, i.e. before the implementation are ready. Or before a choice of implementation has been made. Thus, for example, it is possible to write servlet code before choosing a container. This is in accordance with XP that says : "not commit to infrastructure choice before you have to" and "write unit test first".   +    
    MO は包括的で、普遍的です。 これは全ての種類の単体テストに適用できます : Servlet 単体テスト、JDBC 単体テスト、Struts 単体テスト、...。 Cactus はサーバ側テストしか扱えません。 これはつまり、もし Servlet コードの中で JDBC 接続が使用されていて、 単体テストをデータベース・アクセスから切り離したいと考えた場合、 さらに MO 風の戦略も必要となるということを意味します。 こうして、あなたは 2つの戦略を学び、理解しなくてはならないのです。 MO is comprehensive/universal. It adapts to all kind of unit testing : Servlet unit testing, JDBC unit testing, Struts unit testing, ... Cactus only addresses server-side testing, meaning that if in your Servlet code you have JDBC connections and you want to unit test in isolation the methods that does database access you still need to have a MO-like strategy, thus you need to understand and learn 2 strategies.   +    
    MO テストはコンテナ上で走らせる必要がないので、非常に高速です。 これにより、テストを頻繁に実行することができます。 IC テストでは、コンテナを起動し、テストを実行し、 コンテナを終了する必要があります。 しかしながら、これは Ant やリロード可能なコンテナ(コンテナの多くは 動的リロードを実装しています)を使うことで緩和できます。 Running MO tests is very fast as it does not rely on having to run a container. Thus tests can be run very often. IC testing needs to start the container, run the tests, stop the container. However, this can be alleviated by using Ant and by using a reloadable container (the majority of containers implement dynamic reloading).   +    
    MO を使うことで、 開発者はコードのリファクタリングを強要されます。 例えば、周辺オブジェクトのモック実装を開発するためには、 そのインターフェースが提供されていなくてはなりません。 他にも、多くの細かいデータを渡す代わりに、賢いハンドラを渡す (こうしてより良いカプセル化がもたらされます) といったような、もっと微妙なリファクタリングも行なわれます。 これは XP のリファクタリング規則に従っています。もし、 あなたが既に存在するコードのテストを書かなくてはいけないとしたら、 それは容易に悪夢と化すであろうことを覚えておいてください...。 Using MO force the developer to refactor his code. As an example he needs to ensure that interfaces are provided for domain objects so that a Mock implementation can be implemented. There are other more subtle refactoring involved like smart handler passing instead of more fine grained data (thus leading to better encapsulation). It follows XP refactoring rules. Note that if you need to implement tests for existing code it can easily become a nightmare ...   +   +  
    MO を使っても、 そのクラスが選択されたコンテナで正しく動作するかどうかは判りません。 一方、 IC テストは全てのコードがコンテナ内で完璧にうまく動作することを保証します。 Using MO, it is not sure the classes will run correctly in the chosen container. On the other hand, IC tests ensures that all code will run perfectly well in container.     +  
    MO テストは粒度が非常に細かくなる傾向があります。そのことで、 オブジェクト間の連携が正しく動作することの保証がなくなり、 もっと粒度の粗いテスト(統合テストや機能テスト)が必要になります。 MO tests tend to be very fine-grained. Thus, there is no assurance that object interactions will work properly and thus more coarse grained tests tests are a must (integration tests, functional tests).     +  
    汎用の MO ライブラリを使うと、 いくつかの MO プラクティスに反することになります。 たとえば、周辺オブジェクトのアサーションをテスト・ケースではなく、 モック実装に含めて集約すること (これはリファクタリングされたアサーションと呼ばれます) は良い習慣であるとされています。 これはモック実装がプロジェクトに特化したものでなくては不可能です。 ですので、 MO は汎用ライブラリという考え方には馴染まないところがあるのかもしれません。 おそらくは中庸となる点が見つかることでしょう。 Using generic MO libraries is against some of MO practices. For example, a good practice is to factorize domain object asserts in the mock implementation instead of in the test case (this is called Refactored Assertions). This is possible only if the Mock implementation is project specific. So, for some parts, MO does not fit that well with the idea of generic libraries. A middle ground could probably be found.     +  
    MO を使うということは、単純なことではありません。それは いくらかの訓練と経験を必要とします。 JUnit を使った単体テストと同様に、従うべき方法論が存在しま す。そのいくつかは : Using MO is not simple. It needs some discipline and some experience. Same as for unit tesing using JUnit, there are some methodologies to follow. Some are :
    • 無数の MO を実装するハメになって押し潰されないように、 MO の実装は可能な限り単純である必要があります。 言い替えれば、最初は何もしないメソッドを、 そしてゆっくり時間をかけて、リファクタリングを通して、 そのときに必要なものだけを実装しなさい。 In order not to be weighted down by having to implement myriad of MO, these implementation need to be the simplest possible, i.e. do nothing method at first and then slowly over time, during refactoring, implement what is needed at the current time.
    • MO の中で周辺オブジェクトのロジックを再実装する誘惑に負けてはいけません。 全ての MO は、 ほとんどロジックが含まれていないようにしなくてはいけません。 One must resist the temptation to reimplement the domain logic in the MO. There must be almost no logic at all in MO.
    • MO から他の MO を呼び出してはいけません... そうしなくてはいけないのであれば、 その MO はリファクタリングを必要としています! MO must not make calls to other MOs ... When it happens, there is a need for MO refactoring !
     
      +  
    いくつかのケースでは、 MO は通常であれば必要とされない生成 API を必要とします。 たとえば、Servlet 内部で使用される周辺オブジェクトのモック版を初期化するために Servlet の中で init(MockObject) を呼び出す必要があったりします。 また、コードはビジネス要件のためではなく、 テストのために(より柔軟性を持つとしても)より複雑になります : たとえば、通常は必要のないファクトリが、 MO オブジェクトを取得するためだけに必要となるかもしれません。 In some cases MO mandates creating API that are no normally needed, like having to offer a init(MockObject) method in a Servlet in order to initialize a mock version of an internally used domain object. Also the code may become more complex (even if more flexible) because of the need for testing and not because of business requirements : for example, one might need to introduce a factory when it was not needed simply to be able to provide MO objects from the factory.     +  
    全ての要件を満たす汎用 MO ライブラリを作ることは不可能でしょう。 たとえば、汎用 JDBC MO ライブラリは作れないでしょうし、 おそらくはデータベース MO ライブラリを必要とするでしょう。 また、汎用 MO ライブラリのコストと複雑性は、 必要なシミュレート部分だけを一から再実装するのと比べても、 高くつくものになるでしょう。 It may not be possible to create generic MO libraries that fit all the needs. For example a generic JDBC MO library may not be possible and might need database specific MO libraries. Also the cost and complexity of a generic MO library may be higher than just reimplementing from scratch just the needed mocked parts.     +  
    MO はいつもうまくいくとは限りません。たとえば、シミュレー トされた API は正しいインターフェースを持たなくてはいけませんし、 内部オブジェクトをオーバーライド/セットしなくてはいけません。 MO does not always work well. For example the API being mocked need to offer the correct interfaces and means to override/set internal objects.     +  

    結論

    モック・オブジェクトは通常の Java クラスの単体テストと、 コンテナ・コンポーネントのサーバ側テストのギャップを埋める可能性のある、 非常に興味深い単体テスト手法です。 Cactus の現在のテスト方法と、モック・オブジェクトの最も大きな違いは、 Cactus テストはより粒度の粗いテストを目指していて、 開発コードがコンテナの中で動作することを保証するということです。 一方、モック・オブジェクトは Servlet の単体テストに限らず、 コードのあらゆる領域をカバーしているため、より包括的です。 Mock Objects are a very interesting way of doing unit testing which could bridge the gap between standard java class unit testing and server-side testing of container components. The biggest difference between the way Cactus currently works and Mock Objects is that Cactus tests tend to be more coarse-grained and they also ensure that developed code will run in the container. On the other hand, Mock Objects are more satisfactory intellectually because they are not limited to servlet unit testing but cover the whole spectrum of code.

    私達は Cactus テストの粒度は丁度良いレベルだと信じています。 粒度の細かいテストをすることは素晴らしいことですが、 しかしそれには多くの労力を必要とします。 Cactus テストは中庸にあるようで、単体テストを十分に行いつつ、 同時にデプロイ後にもコードが正しく動くという*十分な*自信を与えます。 We believe the graininess of Cactus tests is just correct. Having fine-grained tests is nice but it is also a lot of effort. It seems Cactus tests are a middle ground where you can still do unit tests fine enough and at the same time it gives you *enough* confidence that your tests will run fine when deployed.

    また、目標でも述べた通り、 将来、より多くのコンポーネントが使われるようになれば、 Cactus のようなフレームワークはますます有益なものになると、 私達は考えています。Cactus は、 コードがコンテナの中で正しく動作するのだという自信を与えてくれます。 Also, as described on the Cactus' goals page, we think that a framework like Cactus will be more and more useful as we use more and more components in the future because it will provide the needed confidence that the code will run ok in the container.

    私達はまた、正しい MO テストを書くことは難しく、 いくらかの訓練を必要とするものであると考えています。 MO は、より良いコードを書かせてくれるという点では素晴しいですが、 Cactus テストに比べて難解です。 We also believe that writing correctly MO tests is a difficult process that need some training. MO are good because they let you write better code but it is more difficult to understand than Cactus tests.

    最後に、 私達は MO と Cactus は以下の 2領域で相互補完的であると信じます。 Finally, we believe that MO and Cactus are complementary in 2 areas :

    • 開発フェーズにおいて、 MO はコードのロジックだけを単体テストするのに使用できます。 テストは実行が早く、容易に IDE の中から使用することができます。 数時間に一度、 オブジェクト間の連携とコンテナとの連携をテストするための Cactus テストを実行します。 そして、実際のプラットフォームにデプロイした後、 機能テスト/受け入れテストを実施します。 これは、あなたがフル・サイクルのテストを 実施したい場合の話だということに注意してください。 ショート・サイクルのテストでは、 Cactus テストと機能テスト/受け入れテストだけで良いでしょう。 During the development phase, MO could be used to unit test the code logic only. They run fast and you could use them easily inside your IDE. Then every few hours, you'll run the Cactus tests which test interactions between objects and container interactions. Then you'll run the functional/acceptance tests when you deploy to the real platform. Note that this is if you want to do the full cycle. A short cycle could simply be Cactus tests and functional /acceptance tests.
    • Cactus はサーバ側コードに焦点をあてたものです。 しかし、MO (あるはスタブ)は Cactus テストの中で例えば JDBC 接続や LDAP 接続などをシミュレートするのに使用することができます。 そこに制限は存在しません。 Cactus focuses on server side code. However you can use MO (or stubs) within Cactus tests for simulating a JDBC connection, an LDAP connection, ... for example. There are no restrictions.

    私達は MO vs IC について、 ぜひフィードバックをいただきたいと思っています。 何かご意見がありましたら、 Cactus メーリング・リスト までお送りください。 We would very much like to have your feedback on MO vs IC. Please post your feedback on the Cactus mailing list.

    どうもありがとう。 Thanks a lot.


    MO に関する参考文献

    リンク : Some links :




    Copyright © 2000-2002 The Apache Software Foundation. All Rights Reserved.