Teeda のプロジェクトでlogicクラスのテストを書いて動かしたら context(facesContext)が null だと言われる。
しかも元々のエラー発生箇所が表示されないため、エラーになるたびにげんなりする。
これはよろしくない、テストを書くのが億劫になりすぎる!!!!
という経緯で調べてみた。
結局のところプロジェクト側で用意されていた例外クラス MyException の toString 内で
FacesContext.getCurrentInstance()
していたのがよろしくなかった。
テスト対象のメソッドを実行しているときは普通に toString(の中で FacesContext のインスタンスを取得)できるが、例外オブジェクトは S2コンテナが破棄された後もJUnitによって受け渡され、そこで toString を呼ぶと当然 FacesContext なにそれ、となる。
S2コンテナ初期化 テスト実行・例外オブジェクト生成 ← ここのエラーについて知りたいのに S2コンテナ破棄 テスト結果表示 ← ここでぬるぽ
S2FrameworkTestCase を見てみるとこうなっている。
テスト結果表示は、この runBare の後で行われているっぽい。
public void runBare() throws Throwable { setUpContainer(); try { setUp(); try { setUpForEachTestMethod(); try { container.init(); // S2コンテナ初期化 try { setUpAfterContainerInit(); try { bindFields(); try { setUpAfterBindFields(); try { doRunTest(); // テスト実行 } finally { tearDownBeforeUnbindFields(); } } finally { unbindFields(); } } finally { tearDownBeforeContainerDestroy(); } } finally { container.destroy(); // S2コンテナ破棄 } } finally { tearDownForEachTestMethod(); } } finally { tearDown(); } } finally { tearDownContainer(); } }
S2フレームワークを使っている場合はS2コンテナのライフサイクルが絡むので NPE が起こるけど、もうちょっと抽象的な話としてはエラー発生のときと toString を呼んだときとで状態が変わっているのが問題。
S2コンテナ使わずに素朴に試してみた。
package exception; public class MyException extends RuntimeException { private int value; public MyException(){ value = 0; } @Override public String toString() { value++; // toString が呼ばれるたびに状態が変わる return "value=" + value; } }
import org.junit.Test; import exception.MyException; public class FooTest { @Test public void test() throws Exception{ try { throw new MyException(); } catch (Exception e) { System.err.println( e.toString() ); //=> value=1 throw e; } } }
そうなりますよねー。