How to vertify the System.out.print?

假如我們有一隻main程式,它在執行完某些工作後會dump資訊到console上,我們要怎麼做verify? 假設程式如下,我們的測試程式所期望的輸出就是HelloWorld。

public class Example {
	public static void main(String[] args) {
		System.out.println("Hello World");
	}
}

是否能夠透過PowerMock達到目的呢? 答案是可以的,但非常麻煩。你必須先mock system.out物件,接著針對可能會呼叫到的println或print做去set expectResult。我提供另外一種方法,可以讓你直接針對結果做assertion,步驟如下:

  1. 建立mock的PrintStream類別且override write method,System.out的print method最後都會呼叫write method。
  2. 建立mock的OutputStream類別,用來設置給PrintStream使用。而此mock OutputStream物件需要override write method,將寫入的int轉放入StrinBuffer中,好讓最後我們可以直接透過此StringBuffer做驗證。
  3. 在測試案例中,將mock的PrintStream物件設至System.out中。
  4. 驗證mock PrintStream所儲存起來的資訊。

我們來看看實做。

Implement Mock PrintStream

在mock PrintStream建構子的地方,super讓它使用原本的System.out就好,這並不是很重要。重要的是我們要將client用來驗證的OutputStream給設成member,而write的地方要將傳入值給帶給OutputStream物件中,好讓它最後可以透過我們假的OutputStream做寫入動作。

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
 
public class TheTestingPrintStream extends PrintStream{
	private OutputStream mOutputStream;
 
	public TheTestingPrintStream(OutputStream aOutputStream) {
		super(System.out);
		System.out.println();
		mOutputStream=aOutputStream;
	}
 
	@Override
	public void write(int aB) {
		super.write(aB);
		try {
			mOutputStream.write(aB);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
 
	@Override
	public void write(byte[] aBuf, int aOff, int aLen) {
		super.write(aBuf, aOff, aLen);
		try {
			mOutputStream.write(aBuf,aOff, aLen);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

Implement Mock OutputStream

而Mock OutputStream的職責就是將要寫入的字元透過StringBuffer存起來即可。

import java.io.IOException;
import java.io.OutputStream;
 
public class StringBufferOutputStream extends OutputStream {
 
	private StringBuffer mStringBuffer;
 
	public StringBufferOutputStream(StringBuffer aSb) {
		mStringBuffer = aSb;
	}
 
	@Override
	public void write(int aB) throws IOException {
		mStringBuffer.append((char) aB);
	}
 
	public String toString() {
		return mStringBuffer.toString();
	}
 
	public void clear() {
		mStringBuffer.delete(0, mStringBuffer.length());
	}
 
}

Implement Testing Code

在Setup的時候,我們透過System.setOut將PrintStream給偷天換日掉。這樣就可以在呼叫完Example.main後,從mSB中取得結果去做Assertion。

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
 
public class ExampleTest {
	private StringBuffer mSB = new StringBuffer();
	private StringBufferOutputStream mSBOutputStream =  new StringBufferOutputStream(mSB);
	@Before
	public void setUp() throws Exception {
		System.setOut(new TheTestingPrintStream(mSBOutputStream));
		mSBOutputStream.clear();
	}
 
	@Test
	public void testMain(){
		Example.main(new String[0]);
		Assert.assertEquals("Hello World"+System.getProperty("line.separator"), mSB.toString());
	}
}

若懶得寫可以直接使用opensource: link