バイトコード・インジェクション・ツール BytemanとJUnitの連携

再現が難しい例外*1を、Bytemanで発生させて確認したりしている。最近、1.5がリリースされて、JUnitと連携できるようになっていたので試してみた。

Byteman - JBoss Community
Byteman 1.5.0 JUnit Integration Makes Fault Injection Simple
Sample.returnTrue()は、trueを返すだけのメソッド。SampleTest.testReturnTrue()の@BMRuleアノテーションで、例外を投げるよう定義している。
Sample.checkNumberPrivate(int i)はprivateメソッドなので、直接テストコードから呼び出せないが、@BMRuleアノテーションで同じく例外を投げるよう定義している。


どちらもRuntimeExceptionが投げられて、テストは成功する。


Sample.java

public class Sample {

    public boolean returnTrue() {
        return true;
    }

    public boolean checkNumber(int i){
        return checkNumberPrivate(i);
    }

    private boolean checkNumberPrivate(int i) {
        if (i > 0) {
            System.out.println(i);
            return true;
        } else {
            throw new RuntimeException();
        }
    }
}

SampleTest.java

@RunWith(BMUnitRunner.class)
public class SampleTest {

	@BMRule(name = "throw runtime exception",
			targetClass = "sample.Sample",
			targetMethod = "returnTrue",
			condition = "true",
			action = "throw new RuntimeException()")
	@Test(expected = RuntimeException.class)
	public void testReturnTrue() throws Exception {
		Sample s = new Sample();
		s.returnTrue();
	}
	
	@BMRule(name = "throw runtime exception 2",
			targetClass = "sample.Sample",
			targetMethod = "checkNumberPrivate(int)",
			condition = "true",
			action = "throw new RuntimeException()")
	@Test(expected = RuntimeException.class)
	public void testCheckNumber() throws Exception {
		Sample s = new Sample();
		s.checkNumber(1);
	}
	
}


今のところ私は、JUnit連携は使ってないし、任意の場所で例外投げたり、スタックトレースを出力したり、といった使い方しかしてないが、もう少し深く掘り下げてみようかな。


最初Eclipseでうまく動かなくて難儀したが、単に環境変数BYTEMAN_HOMEも、システムプロパティorg.jboss.byteman.homeも設定していなかっただけだった。正確には、zshrcにはBYTEMAN_HOMEを設定していたんだが、~/.MacOSX/environment.plistに設定していなかったから、eclipse環境変数が渡ってなかった。長年Macを使っているのに、environment.plistって知らんかった…。

*1:と思ったが、実際使ったケースではそうでもなかった。