Action定義のテスト (ActionDefTest)

Action定義のテストとは?

[App]ActionDefTest.javaを実行しよう

LastaFlute の Example から作られたプロジェクトであれば、最初から [App]ActionDefTest.java があるはずです。まずは、その UnitTest を実行してみましょう!

e.g. ActionDefTest location {org.docksidestage, Dockside} @Directory
src/main/java // メインコード置き場
src/main/resources // メインのリソース置き場
src/test/java // テストクラス置き場
 |-org.docksidestage
    |-app
    |  |-... // アプリのUnitTestクラスたち (Actionのテストとか)
    |
    |-mylasta
    |  |-DocksideActionDefTest.java // これが ActionDefTest です!
    |  |-DocksideLastaDocTest.java  // こっちはLastaDocのメタデータ抽出
...

policeStoryOf...()だけを呼んでいる怪しいテストたちです。

e.g. ActionDefTest {Dockside} @Java
public class DocksideActionDefTest extends UnitDocksideTestCase {

    public void test_component() throws Exception {
        policeStoryOfJavaClassChase(new ActionComponentPolice(tp -> getComponent(tp)));
    }

    public void test_hotDeployDestroyer() throws Exception {
        policeStoryOfJavaClassChase(new HotDeployDestroyerPolice(tp -> getComponent(tp)));
    }

    ...    
}

そもそも ActionDefTest とは?

業務的な UnitTest ではなく、Action自体の定義、フレームワークとして使い方、などが合っているかどうかをテストする UnitTest が用意されています。それを ActionDefTest と呼んでいます。

例えば...

  • Actionがしっかり初期化できるか? (定義の仕方とか間違っていたら落ちる)
  • HotDeployを壊すようなクラス参照してないか? (appの外からappのクラスを参照してたら落ちる)
  • ActionじゃないのにBaseActionを継承してないか? (してたら落ちる)
  • webじゃないクラスがwebのクラスを使ってないか? (使ってたら落ちる)
  • 忍者のようなクラス参照をしていないか? (忍者がいたら落ちる)
  • などなど

これらは、業務的な UnitTest を書いていても、なかなかチェックされないものです。 多くの場合、これらを満たしていなくても動いてしまうので、通常だとレビューとかで人が指摘するしかありません。

そこで、LastaFlute では、(多くの場合)動いちゃうけど "アーキテクチャの根本を壊しかねないやり方" を UnitTest で検知できるようにしています。 クラス名は、[App]ActionDefTest.java となっています。 (Actionが初期化できなかったら動かないので起動すれば気付きますが...その検知は早い方がいいですよね)

とても気軽に実行しましょう

UnitTest はいくら実行しても構いませんので、ちょっと実装が進んで "大丈夫かな?" と思ったら、気軽にこの ActionDefTest を実行してみましょう。

そもそも、コミット前には UnitTest をすべて流す のがお約束です。 少なくとも、修正したアプリプロジェクトの src/test/java 配下だけでも。 なので、Actionを実装して "さあコミットするぞ" というときに、自然とこの UnitTest も実行されるでしょう。 (最初からテストが落ちまくってるとか、テストの実行に時間がかかりすぎるので難しいとかであれば、残念がりましょう: その時はピンポイントでこのテストだけでも実行で)

ActionDefTestのメソッドたち

Actionがしっかり初期化できるか?

Actionクラスは、初期化されるだけで様々なチェックが掛かります。

例えば、@Executeメソッドの引数の順序など "LastaFluteが規約に則っているか" などがチェックされます。 間違っていればアプリ起動してリクエストすれば判明しますが、事前に検知したいところです。 (Actionごとの UnitTest でも、コンポーネント化するわけではないので、検知されないところもあります)

test_component() を実行すれば、そのアプリプロジェクトのすべての Action クラスが一気にコンポーネント化され初期化処理が走り、その中のチェックで間違いが検知されれば落ちます。

e.g. test_component() of ActionDefTest @Java
    public void test_component() throws Exception {
        policeStoryOfJavaClassChase(new ActionComponentPolice(tp -> getComponent(tp)));
    }

できるだけ、画面を実際に動かさなくても色々なミスが検知できるようにしたいところです。 (もちろん、画面を実際に動かすテストはするときはしますが、インクリメンタル開発では毎リリースごと完璧に網羅するのは難しいことなので)

HotDeployを壊すようなクラス参照してないか?

appパッケージ配下は HotDeploy (アプリを再起動しなくても修正が反映される) になりますが、appの外 (mylasta, dbfluteなど) からappのクラスを参照すると、その HotDeploy の機能は無効になってしまいます。

仕組み上の都合でもありますが、そもそも業務的にもそういった参照は "参照カオス" のきっかけになるので、そういった参照は避けましょうというところです。

app配下 to app配下
OK
app配下 to appの外
OK
appの外 to app配下
Bad (HotDeployが壊れるし、そもそも参照カオス)
つまり app配下 to mylasta配下
OK (e.g. Action から UserBean など)
つまり mylasta配下 to app配下
Bad (e.g. AssistantDirector から Action など)

test_hotDeployDestroyer() を実行すれば、そのアプリプロジェクトのすべてのクラスでチェックされます(抜け道はあるかもですが、おおよそ検知します)

e.g. test_hotDeployDestroyer() of ActionDefTest @Java
    public void test_hotDeployDestroyer() throws Exception {
        policeStoryOfJavaClassChase(new HotDeployDestroyerPolice(tp -> getComponent(tp)));
    }

こういった参照をしてしまっても普通に動いてはしまうので、ぜひともこの UnitTest で検知したいものです。気付いたら HotDeploy が全然効かなくなっていた、とかにならないように。

ActionじゃないのにBaseActionを継承してないか?

つまり、Logicクラスが、Actionのスーパークラスを継承しちゃっていないか?を検知します。

e.g. test_nonActionExtendsAction() of ActionDefTest @Java
    public void test_nonActionExtendsAction() throws Exception {
        policeStoryOfJavaClassChase(new NonActionExtendsActionPolice());
    }

つまり、こういうことはしないようにしましょう、ということです。

e.g. No way, logic should not extend action @Java
public class SeaInParkLogic extends DocksideBaseAction { // ★絶対ダメ
    ...
}

webじゃないクラスがwebのクラスを使ってないか?

つまり、Logicクラスが、FormクラスやSessionManagerなどを使っちゃってないか?を検知します。

e.g. test_nonWebHasWebReference() of ActionDefTest @Java
    public void test_nonWebHasWebReference() throws Exception {
        policeStoryOfJavaClassChase(new NonWebHasWebReferencePolice());
    }

つまり、こういうことはしないようにしましょう、ということです。

e.g. No way, logic should not depend on web @Java
public class SeaInParkLogic {

    public void land(SeaForm form) { // ★絶対ダメ
    }
}

忍者のようなクラス参照をしていないか?

例えば、A画面が、B画面のFormクラスを使ってるとかを検知します。

e.g. test_webPackageNinjaReference() of ActionDefTest @Java
    public void test_webPackageNinjaReference() throws Exception {
        policeStoryOfJavaClassChase(new WebPackageNinjaReferencePolice());
    }

つまり、こういうことはしないようにしましょう、ということです。

e.g. stileto reference @Directory
app
 |-web
 |  |-sea
 |  |  |-SeaAction.java
 |  |  |-SeaForm.java
 |  |-land
 |  |  |-LandAction.java // uses SeaForm

@ResourceのDI定義が変じゃないか?

例えば、privateではないフィールドでの DI を検知します。(publicやprotectedなど)

Exampleデフォルトでは、アプリ内のDIは、すべてprivateフィールドということで統一しています。

e.g. test_injectedResourceDefinition() of ActionDefTest @Java
    public void test_injectedResourceDefinition() throws Exception {
        policeStoryOfJavaClassChase(new InjectedResourceDefinitionPolice().shouldBePrivateField(field -> {
            return true; // means all fields
        }));
    }

その他、LastaFluteから何かあれば

何か "明らかにLastaFluteでは違うと思うような書き方" について検知します。

その内容は徐々に追加され、アップグレードすることでチェックが自然とされるようになります。

e.g. test_lastaPresentsSomething() of ActionDefTest @Java
    public void test_lastaPresentsSomething() throws Exception {
        policeStoryOfJavaClassChase(new LastaPresentsSomethingPolice());
    }

オリジナルのPoliceStoryを作ろう

こういったチェックを、自分で作ることもできます。そのプロジェクト固有のオリジナルのチェックを作り、UnitTest で自動的にチェックされるようにすると良いでしょう。

policeStoryOf...() のに合う PoliceStory を実装すれば誰でも作ることができます。 別のテストクラスにしてもいいですし、集約させるために ActionDefTest に追加してもいいでしょう。