Problem
一個java的console programe,免不了需要使用System.exit讓caller知道程式的執行結果。這就像是C中的exit,C還可以透過main的return value來當exit code。我們先看看下面的範例程式:
package org.tonylin.powermock; public class Example { public Example(){ } public void doSomething(){ } public static void main(String[] args) { try { new Example().doSomething(); } catch( Exception e ){ System.exit(1); } finally { System.exit(0); } } }
main中僅是call Example的doSometing,有問題就dump exit code為1,正常exit code為0。而然如果在Junit中直接呼叫Example.main,這測試馬上就結束了,下面的測試案例也不用繼續進行。也許你可以把main精簡到不能再精簡的地步,不去管它或是透過外部程式的方式去測它;不管怎樣,它的line coverage還是會少掉這幾行,對一個基於外部程式的系統而言,也非常的傷。我們可以透過PowerMock以下的方式,去捕捉到Exit code與避免程式離開。
How to solve?
我們先撰寫基本的skeleton:
package org.tonylin.powermock; import org.easymock.EasyMock; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.easymock.PowerMock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest({Example.class}) public class TestExample { @Test public void testMain(){ } }
首先加入Run With annotation讓測試透過PowerMockRunner執行,接著將Example.class加到PrepareForTest中。而測試案例的實做,首先必須透過PowerMock的mockStatic去mock System.clss,接著執行System.exit(0)與PowerMock.expectLastCall().times(1)讓PowerMock知道我們想去mock的method與預期的接收參數為0。如果main所執行的是System.exit(1),就會test failed。最後是使用PowerMock.replayAll()讓所有的mock work並呼叫我們的待測method。
@Test public void testMain(){ PowerMock.mockStatic(System.class); System.exit(0); PowerMock.expectLastCall().times(1); PowerMock.replayAll(); Example.main(new String[0]); }
以上完成後,你可能會有另外一個問題: Exception path該如何測? 我們必須先mock new Example,再讓它拋出exception,這個問題可以參考Mock Constructor。如果在執行完System.exit之後,你希望main程式能夠離開,那在PowerMock.expectLastCall()的地方要改為PowerMock.expectLastCall().andStubThrow(new RuntimeException()),而call main的地方要去catch它,這樣就可以與原本main的流程相同,否則在執行完System.exit後,程式是會繼續執行下去的! 因為這是假的System.exit阿!
留言
張貼留言