Cactus 擴展了 JUnit 3.x,被設計來輔助您進行In-Container測試,從API實作的角度來說,Cactus擴 充了JUnit 3.x一些類別,並重新定義了一些方法,Cactus可以用嵌入式(Embedded)的方式運行起一個容器,從中獲取測試時所必要的物件或資源,也可以 透過獨立架設的容器,線上執行測試。
以嵌入式的方式來說,可以使用Jetty作為嵌入式的容器,實際來看測試案例撰寫的方式:
package test.cc.openhome;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;
import org.apache.cactus.extension.jetty.Jetty5xTestSetup;
import org.apache.cactus.extension.jetty.Jetty6xTestSetup;
import cc.openhome.LoginServlet;
class TestForLoginServlet extends LoginServlet {
public void doTest(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
}
class DummyHttpServletRequest extends HttpServletRequestWrapper {
private String forwardToPage;
private boolean isForwarded;
public DummyHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public RequestDispatcher getRequestDispatcher(String path) {
forwardToPage = path;
return new RequestDispatcher() {
public void forward(ServletRequest req, ServletResponse resp)
throws ServletException, IOException {
isForwarded = true;
}
public void include(ServletRequest req, ServletResponse resp)
throws ServletException, IOException {
}
};
}
public String getForwardToPage() {
return forwardToPage;
}
public boolean isForwarded() {
return isForwarded;
}
}
public class LoginServletTest extends ServletTestCase {
static {
System.setProperty(
"cactus.contextURL", "http://localhost:8080/example");
}
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(LoginServletTest.class);
return new Jetty5xTestSetup(suite);
}
public void beginLoginSuccess(WebRequest request) {
request.addParameter("user", "justin");
request.addParameter("passwd", "1234");
}
public void testLoginSuccess() throws Throwable {
DummyHttpServletRequest dummyRequest =
new DummyHttpServletRequest(request);
new TestForLoginServlet().doTest(dummyRequest, response);
assertTrue(dummyRequest.isForwarded());
assertEquals("success.html", dummyRequest.getForwardToPage());
}
public void beginLoginFail(WebRequest request) {
request.addParameter("user", "someone");
request.addParameter("passwd", "1234");
}
public void testLoginFail() throws Throwable {
DummyHttpServletRequest dummyRequest =
new DummyHttpServletRequest(request);
new TestForLoginServlet().doTest(dummyRequest, response);
assertTrue(dummyRequest.isForwarded());
assertEquals("login.html", dummyRequest.getForwardToPage());
}
}
首先要注意的是,在撰寫文件的此時,Cactus擴充的對象是JUnit 3.x, 注意到套件部份使用的是junit.framework,ServletTestCase擴充了TestCase,你撰寫 Servlet測試時時,也是以繼承方式擴充ServletTestCase。
ServletTestCase 中可以定義beginXXX()方法,這會在對應的testXXX()方法之前執行,beginXXX()方法會傳入WebRequest,你可以用它來 作一些請求參數等的設定。
如果要以嵌入式方式來運行Jetty容器,撰寫文件的此時,可以使用Jetty5xTestSetup, 這可用來包裹TestSuite, 傳回Jetty5xTestSetup物 件給JUnit 3.x的TestRunner, 將會以嵌入式方運行Jetty容器。
要注意的是,Cactus要求一定要設定的屬性是cactus.contextURL, 用來設定HTTP請求時容器的網址,如果你有多個測試類別要共用這個設定的話,也可以於執 行時指定一個.properties檔案的所在(預設會在Classpath中尋找),.properties中設定相關屬性。例如:
java -Dcactus.config=cactus.properties ...
為何要設定請求網址?Cactus的測試執行分作客戶端、代理轉發與伺服端執行測試三個階段,了解這些階段,對於使用Cactus是很重要的:
- 當測試開始時,客戶端TestRunner會產生ServletTestCase實例,執行beginXXX()方法,在這個方法中您可以準備一些HTTP 相關的參數,例如加入使用者名稱、密碼等參數。
- 客戶端TestRunner使 用HTTP與伺服器上的Redirector Proxy進行溝通,Redirector Proxy會接收請求並產生相關的物件,像是HttpServletRequest、 HttpServletResponse等。
- Redirector Proxy會再產生ServletTestCase實 例,之前Redirector Proxy所保留的HttpServletRequest、 HttpServletResponse等,會設定給此時所產生的ServletTestCase實 例,程式中可以使用request、response等名稱來取得。
- Redirector Proxy開 始執行上面的setUp ()、testXXX()、tearDown()等方法,收集測試結果, 這部份與單純的JUnit 是相同的。
- ServletTestCase與 所使用到的類別實例互動。
- Redirector Proxy執行ServletTestCase完
成,取得測試結果。
- Redirector Proxy將測試結果以HTTP傳回給客戶端TestRunner。
- 客戶端TestRunner執行endXXX()方法(接受WebResponse物件), 可以在這邊分析HTTP傳回的訊息,並顯示測試結果。
如果使用嵌入的方式來運作,你無需接觸Redirector Proxy的細節,實際上這是由一個ServletTestRedirector來負責,後面的文件還會看到 ServletTestRedirector的設定。