無法預期的輸入

在寫測試案例時,我想不是每一個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){
 
	}
}

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阿!