跳到主要內容

發表文章

目前顯示的是有「PowerMock」標籤的文章

UnsupportedOperationException: Unsupported copy option

Problem 於PowerMockRunner環境中,使用Files.copy且搭配ExtendedCopyOption.INTERRUPTIBLE參數時,程式會丟出java.lang.UnsupportedOperationException: Unsupported copy option例外: Files. copy ( new File ( "./test1.txt" ) . toPath ( ) , new File ( "./test2.txt" ) . toPath ( ) , ExtendedCopyOption. INTERRUPTIBLE , StandardCopyOption. REPLACE_EXISTING ) ; How to? 這問題是由於ExtendedCopyOption.INTERRUPTIBLE所造成的,我直接在@PowerMockIgnore中加入對應的套件,就順利解決這問題。 @PowerMockIgnore ( { "java.nio.*" , "sun.nio.*" , "java.util.ServiceLoader" , "com.sun.nio.*" } ) 後來查詢JDK原始碼,在JDK中的WindowsFileCopy或UnixFileCopy會做參數比較: 70 if ( option == ExtendedCopyOption. INTERRUPTIBLE ) { 71 interruptible = true ; 72 continue ; 73 } 然而在PowerMock環境中,卻無法如預期判別;於是我直接印出ExtendedCopyOption的classLoader: System . out . println ( ExtendedCopyOption. class . getClassLoader ( ) ) ; 最後發現在PowerMock環境中為MockClassLoader,而非PowerMock環境則為null(null...

Mock Class Filed

Problem Mock一個method已能駕輕就熟了,但如果是一個類別中的欄位呢? 也許你可以替它寫一個get method再去mock就好。但如果今天是一個Constants Intercace,就不行了吧? public interface Constants { double PI = 3.14159 ; double PLANCK_CONSTANT = 6.62606896e - 34 ; } How to solve? 在這我介紹兩種做法,透過Mock Field的方式解決Final修飾宣告的問題。在有宣告Final情況下,不允許使用WhiteBox的方式設置。也許有方法,只是我還沒發現。 WhiteBox 首先看看範例類別,包含一個static與非static的欄位。 public class TestClass { static String field1 ; String field2 ; } 要mock只需要透過Whitebox.setInternalState。如果是static欄位,使用TestClass.class;非static欄位就需要傳instance給第一個參數。其餘的就是欄位名稱與內容值。 Whitebox. setInternalState ( TestClass. class , "field1" , "test1" ) ;   TestClass tc = new TestClass ( ) ; Whitebox. setInternalState ( tc, "field2" , "test2" ) ;   PowerMock. replayAll ( ) ;   System . out . println ( TestClass. field1 ) ; System . out . println ( tc. field2 ) ; Mock Field 假如我們用WhiteBox去mock,在Problem中所宣告的Constants,就會收到無法存取final修飾字欄位的例外。 Field field = PowerMock. field ( Constants. cla...

無法預期的輸入

Problem 在寫測試案例時,我想不是每一個method都會有預期的輸入吧?像是時間或是執行期間產生的物件都是有可能的。舉例來說,我有一個MessageSender的類別提供了send method替我發送訊息。對caller而言,address、account、password是讀取資料庫而來,message是由user輸入但會夾雜timestamp。這樣我在寫的時候不就沒辦法先設定好expect的message嗎? 也許可以把它拆成純粹的message再加上一個date參數,但還是要面對一樣的問題。 public class MessageSender { public static void send ( String address, String account, String password, String message ) {   } } How to resolve? EasyMock提供了anyBoolean、anyByte、anyChar、isA甚至anyObject的函式替你解決這個問題。以上述的問題來說,我們可以這樣寫: @Test public void testSendMessage ( ) { PowerMock. mockStaticPartial ( MessageSender. class , "send" ) ; MessageSender. send ( EasyMock. eq ( "192.168.1.110" ) , EasyMock. eq ( "user" ) , EasyMock. eq ( "password" ) , EasyMock. isA ( String . class ) ) ;   PowerMock. expectLastCall ( ) . once ( ) ; PowerMock. replay ( MessageSender. class ) ; } 前三個參數很確切的知道內容是什麼,所以我透過EasyMock.eq將內容交給EasyMock的Matcher物件。而第四個參數我不確定到底是什麼字串,所以我透過isA確保它是...

不同的Expected Result

Problem 為了讓程式夠強健,在發生能夠處理的例外情況可能會做retry。在 搞笑談軟工 有提到retry好的做法,也可以透過spring的RetryTemplate去實做retry(很大包..)。 但在測試的時候,要怎麼確認你的retry是否可以work呢? 讓我們來看看範例。這個範例在執行時,如果有例外產生的時候,會使用替代方案執行,如果超過重試次數就會拋出一個RuntimeException。 package org.tonylin.powermock ;   public class RetryExample { public User readUser ( ) { int attempt = 0 ; int maxAttempt = 2 ; boolean retry = false ; User user = null ; do { try { retry = false ; if ( attempt == 0 ) user = DaoUtil. readUserFromDB ( ) ; else user = DaoUtil. readUserFromFile ( ) ; } catch ( Exception e ) { attempt ++; retry = true ; if ( attempt > maxAttempt ) throw new RuntimeException ( e ) ; } } while ( attempt <= maxAttempt && retry ) ; return user ; } } 測試案例可以這樣設計: 第一次執行成功。 第一次失敗,改由readUserFromFile執行成功。 第一次執行失敗,第二次執行readUserFromFile失敗,第三次才成功。 執行皆失敗。 該如何透過PowerMock去實做這些測試案例呢? How to resolve? 第一次執行成功這個案例較基本,我就不特別說明。 ...

PowerMock and Exceptions

將曾經遇過的問題與解決方式整理在這。 java.security.NoSuchAlgorithmException 在使用SSL操作時會需要KeyManager,而產生KeyManager的instance則會透過KeyManagerFactory。然而在使用PowerMock時,由於PowerMock已載入Algorithm類別,造成Factory比較SuperClass失敗。 KeyManagerFactory. getInstance ( KeyManagerFactory. getDefaultAlgorithm ( ) ) ; 參考 Issue 288 修改方式,使用@PowerMockIgnore({“javax.net.ssl.*”}): @RunWith ( PowerMockRunner. class ) @PrepareForTest ( { Example. class } ) @PowerMockIgnore ( { "javax.net.ssl.*" } ) public class TestExample { ... java.lang.ClassCastException 這種例外算佔大多數。套用Spring一定會遇到,解法可參考這篇 文章 。後來我還有愈到的是com.sun.crypto.provider.HmacSHA1 cannot be cast to javax.crypto.MacSpi,面對這種問題大都可以使用@PowerMockIgnore解決,至於要Ignore哪些package或class可能要自行嘗試一下。 @RunWith ( PowerMockRunner. class ) @PrepareForTest ( { Example. class } ) @PowerMockIgnore ( { "com.sun.crypto.*" , "javax.crypto.*" } ) public class TestExample { ... 友藏內心的獨白: PowerMock是很方便,但要去Trace要Ignore的地方也花不少時間阿!

Static Block

 Problem 我想測試getWindowsMajorVersion method,但由於使用了static block導致我無法同時測到在Windows與Linux的情況: public class PlatformUtil { private final static String mOSName = System . getProperty ( "os.name" ) ; private final static boolean IS_WINDOWS ;   static { IS_WINDOWS = mOSName. toUpperCase ( ) . indexOf ( "WINDOWS" ) != - 1 ; }   public static boolean isWindows ( ) { return IS_WINDOWS ; }   public static int getWindowsMajorVersion ( ) { if ( ! isWindows ( ) ) { return - 1 ; }   String osVersion = System . getProperty ( "os.version" ) ; int dotIndex = osVersion. indexOf ( "." ) ; String majorVersionStr = osVersion ; if ( dotIndex != - 1 ) { majorVersionStr = osVersion. substring ( 0 , dotIndex ) ; } try { return Integer . valueOf ( majorVersionStr ) ; } catch ( NumberFormatException e ) { return - 1 ; } } } Solution Suppress sta...