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確保它是個字串就可以了。這裡需要注意的是: 「使用了EasyMock Matcher,要確保所有參數都在Matcher的控管內,否則你會收到混搭的例外訊息」有的時候我比較懶,就會直接使用anyObject去處理這個問題。
這時候也許你會想: 「雖然這個值不是一開始能決定的,但我總能去Assert某些字串吧?」是的! 你可以透過之前曾提過PowerMock提供的IAnswer類別。
class SendMessageAnswer implements IAnswer<Void> { private String mMessage = null; @Override public Void answer() throws Throwable { mMessage = (String)EasyMock.getCurrentArguments()[3]; return null; } public String getMessage(){ return mMessage; } } @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)); SendMessageAnswer answer = new SendMessageAnswer(); PowerMock.expectLastCall().once().andAnswer(answer); PowerMock.replay(MessageSender.class); // Do something... Assert.assertTrue( "Should contain xxxx",answer.getMessage().contains("xxxx") ); }
原本IAnswer是拿來根據input去決定回傳值使用,但我想也可以透過它去保存那些**非預期*的內容,以最後要做什麼處理用。
友藏內心獨白: PowerMock、EasyMock也是用了不少DesignPattern阿!
留言
張貼留言