目錄
Jsp測試技術" >Jsp測試技術
依賴管理的抱怨" >依賴管理的抱怨
編譯Jsp" >編譯Jsp
结论" >结论
首頁 Java java教程 如何測試容器外的JSP頁面

如何測試容器外的JSP頁面

Aug 10, 2017 pm 03:12 PM
javascript 容器 頁面

Jsp測試技術

開發web應用程式最惱人的一點就是想要測​​試的話你就必須向將其部署好。當然,並不是所有部分都這樣。如果你是經過了精心的設計的話,你可以在Java程式中測試業務邏輯。你可以在應用程式伺服器不運行的情況下測試資料存取、介面以及預存程序。不過如果是測試GUI的話(Jsp所產生的HTMl),你就必須部署,然後才可能測試。

很多的團隊求助於Sellenium,Mercury或是其他的一些工具透過web server來測試GUI。然而,即使是頁面的內容不變但樣式變了得情況也會讓測試變得脆弱不堪。其他的團隊使用Cactus解決這種脆弱性,或是用HtmlUnit、HttpUnit這樣原始的工具來監測web應用程式所產生的HTML。對於這些問題,我會在另一系列的blog之中來談論。

本文之中我會介紹一種簡單易行的技術,它使用JUnit或是HtmlUnit來測試Jsp頁面,並且完全脫離容器。這項技術的優勢也在此。

你不必一定保持容器的運行,甚至存在。你可以在選擇特定的webserver之前測試你的Jsp。

你不必在每次修改後重新部署,因而編輯/編譯/測試的過程會更迅速。

你可以使用測試優先開發的方式來持續的建立Jsp。

容器外測試Jsp技術之所以並不盛行是因為Jsp在設計上就運行於容器內的。設計者從未過多的想過容器外運作的可能。因此由Jsp編譯器的所產生程式碼往往依賴容器所提供的諸多元件。即使是產生Jsp程式碼的工具也假定了你已經有一個成功部署的web應用程式在運行。因此,為了在容器外運行,你就要開發出相應的這些工具和元件。

依賴管理的抱怨

#為什麼這麼多框架和工具的設計者總期望你生活在他們提供的狹小世界?為什麼我必須先建置出完整的web應用程式才能編譯Jsp?為什麼這些東西一定要運行在容器裡?資訊隱藏早在10年前就已經是優秀軟體設計的基本信條了。我們這個行業何時才能認真看待它?

編譯Jsp

#測試Jsp的第一步是將其編譯為servlet。要實現這一步,我們還需要先將Jsp轉換成Java格式。 Apache提供了一個叫做Jasper的工具,我們呼叫Jasper為MyPage.jsp建立一個Java格式的原始檔MyPage_jsp.java。然後,你就可以用你最喜歡的IDE編譯這個檔案成Servlet。

可惜Jasper並非是設計用在命令列中使用的,或者說並不是完全這樣設計的。但Jasper確有一個main函式用來處理命令列參數,而且透過呼叫java org.apache.jasper.JspC就能夠輕易地呼叫它了。不過,Jasper期望它所運作的環境與容器環境是保持一致的。你要確保classpath有了很多apache的Jar文件,而且它要能找到web應用程式的web.xml。它還需要能夠找到包含web應用程式Jar以及TLD檔案等的WEB-INF目錄。簡而言之,Jasper需要能找到一個完整的web應用程式。

如果事情更糟的話,除非是與TOMCAT的呼叫方式保持完全一致,否則某些特定的Jasper版本(我用的是tomcat 5.5.20)存在一些bug,它產生的程式碼會有一些錯誤。

第一點要做的雖然繁瑣但還算簡單,你需要建立好正確的目錄以及檔案結構,然後在Ant(Classpath比較容易控制)中呼叫Jasper。第二點就需要一定的研究和測試才能讓它跑起來。以下就是能成功運作的ant檔。 JspC的呼叫出現在最後一個任務。

 

 

#

 

 

  <屬性名稱=“build.jar.home”值=“${build.home}/jars”/>

 

## 

## 

#   

#     

##   

   

    ;

##

   

   

     

   

   

   

<檔案集目錄=「${catalina.home}/shared/lib」>

     

<包含名稱=「*.jar」/ >

   

< /檔集>

###  ##### ############################################################################################################# #####  ############### ###    ##### #############    ########## ########    ###

   

     

     

     

       < ;fileset dir="${catalina.home}/common/lib">< ;fileset dir="${catalina.home}/common/lib">

############          #### ##############        ############        ###
######fileset>#### #############        ###################     ############ #####        ###################################################################################### ##### #############          ###### ############     ###### #########

       

#       

       

     

#     classpath>

   

##   

        

includes="**/jsp/** /*.class"

     

/>

## 

#

#當然,你要讓所有標準文件以及目錄都在${build.war.home}之下以確保工作。如果你在你的Jsp之中使用了自訂tag的話,也要確保所有對應的TLD檔案都在你的TLD目錄之中。

要注意的是,在ant檔案中呼叫Jspc的命令列,而不是使用Tomcat所提供的JspC的Ant Task。因為我發現當你有自訂tag的時候它無法正確運行。也許我犯了糊塗,或者JspC確實有bug。不過我所發現的唯一能讓Jasper產生正確程式碼的方式是從命令列呼叫它,並明確的傳遞Jsp檔案路徑作為命令列的參數!如果你依靠它的Ant Task或是使用命令列來搜尋所有web應用程式中的Jsp進行編譯的話,它就會產生錯誤的程式碼。 (請參閱這篇blog)

現在我們有了Java文件,讓我們來分析一下它。首先,請看下面的Jsp檔。

<%@ page import="com.objectmentor.library.utils.DateUtil" %>

<%@ page import="com.objectmentor.library.web.controller.patrons.LoanRecord" %>

#<% @ page import="java.util.List" %>

#<%

 

List loanRecords = (List) request.getAttribute("loanRecords");

if (loanRecords.size() > 0) {

#%>

 

#   

   

#   

######################################################## ###    ##################  ################################################### ###  ###<%###############    ###for (int i = 0; i < loanRecords.size(); i++) {#### ###########      ###LoanRecord loanRecord = (LoanRecord) loanRecords.get(i);################  ###%>#################Ġ ####

 

">

   

   

   

 

## 

##   

}

## 

%>表>

}%>

#則下面是Jasper所產生的程式碼。

#package com.objectmentor.library.jsp.WEB_002dINF.pages.patrons.books ;

#導入javax.servlet.*;

導入javax.servlet.http .*;

import javax. servlet.jsp.*;

import com.objectmentor.library.utils.DateUtil;

import com.objectmentor.library.web. controller.patrons.LoanRecord;

import java.util.List;

」公共最終類loanRecords_jsp擴展org.apache.jasper.runtime.HttpJspBase

#   

implements org.apache.jasper.runtime.JspSourceDependent {

## 

private static java.util.List _jspx_dependants;

public Object getDependants() {

#return _jspx_dependants;

 

}

##  public void _jspService( HttpServletRequest 請求,HttpServletResponse 響應)

       

拋出java.io.IOException, ServletException {

##    JspFactory _jspxFactory = null;

#    PageContext pageContext = null;

###########################################################################################################################################nn\ ######    ###HttpSession session = null;################    ###ServletContext application = null;############ ##    ###ServletConfig config = null;###############    ###JspWriter out = null;## #############   ####################白o# #物件頁面= this;######

##    JspWriter _jspx_out = null;

##    PageContext _jspx_page_context = null;

    try {

      _jspxFactory = JspFactory.getDefaultFactory();y();

#      response.setContentType("text/html");

      pageContext = _jspxFactory.getPageContext (這PageContext ,請求,回應,

##                 

null, true, 8192, true); _jspx_page_context = pageContext;##     

application = pageContext.getServletContext();

# pageContext.getServletConfig();

##      session = pageContext.getSession();

# 。

      out.write('/n');

#      out.write('/n');

      out.write('/n');

 

List LoanRecords = (List) request.getAttribute("loanRecords");

##  if (loanRecords.size() > 0) {

      out.write("/n");

#     

out.write("
IDTitleDue date Fine

   

    td>

   

##   

   

/n");

#      out.write(" 

/n");

#      out.write( "#out.write("    

/n");

      out.write("    

標題/n");

      out.write("#   

#/n ");

##      out.write("   

/n") ;

##      out.write("

/n");

      out.write("  ");

##   

for (int i = 0; i #      LoanRecord LoanRecord = (LoanRecord) LoanRecords.get(i );

##############。 ######################      ###out.write("/n");######

      out.write("

#     

#     

#     

#out.print(i%2==0?"偶":"奇");

#     

out.write("/">/n " );

#     

out.write("

   

/n");

     

## out.write("

   

/n");

      out.write("

   

/n");

##      out.write("   

/n");     

out.write (" 

/n");

#      out.write("

 

");

#   

}

      out.write("/n");

      out.write("表>/n" );

  }

    ###} catch (Throwable t) {##############      ###if (!(t instanceof SkipPageException) )){# ##############        ####out = _jspx_out;###############        ###if ( ) != 0)##############          ###out.clearBuffer();################    != null) _jspx_page_context.handlePageException(t);######

      }

    } finally {

#      if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);

    }

##    }

==

##好的

}

}

最後的抱怨

這個類別為什麼要宣告為final呢?如果我想建立一個測試的stub衍生類別呢?為什麼有人會覺得生成類別如此不可冒犯以至於我都無法覆寫它。

仔細閱讀這段程式碼你就會發現,要使用這個servlet的實例我們需要HttpServletRequest以及HttpServletResponse的實例。

更仔細研讀我們就會發現servlet將所有的HTML寫到JspWriter的實例中,而JspWriter是從PageContext中得到的。如果我們能夠建立一個JspWriter的mock up的版本來保存所有的這些HTML,再為PageContext建立一個mock up的版本來派送mock JspWriter,那麼我們就能在我們的測試中存取這些HTML了。

幸運的是,Tomcat的設計人員把JspWriter的創建放入到了JspFactory的工廠類別中。而這個工廠類別是可以被覆寫的!這意味著我們可以在servlet之中取得我們自己的JspWriter類別而不用改變servlet。需要的就是下面這段程式碼。

class MockJspFactory extends JspFactory {

    public PageContext getPageContext(Servlet servlet, ServletRequest servletRequest, ServletResponse servletResponse, String string, boolean b, int i, boolean b1) {

##      return new MockPageContext(new MockJspWriter());

    }

#    }

##    public void releasePageContext(PageContext pageContext) {

    }

#of

#    public JspEngineInfo getEngineInfo() {

      return null;

    }

}

#現在,我們需要的是mock Jspwriter。為了方便展示,我用了下面的:

MockJspWriter

# package com.objectmentor.library.web.framework.mocks;

import javax.servlet.jsp.JspWriter;

import java.io.IOException;

#####################public class MockJspWriter extends JspWriter {###### ######################  ###private StringBuffer submittedContent;############################### ######  ###public MockJspWriter(int bufferSize, boolean autoFlush) {###############    ###super(bufferSize, autoFlush);##############################################################################################################################################################################################################################' #########    ###submittedContent = new StringBuffer();###############  ###}################################################################# ######

  public String getContent() {

    return submittedContent.toString();

}

public void print(String arg0) throws IOException {

    submittedContent.append(arg0);

  }

public void write(char[] arg0, int arg1, int arg2) throws IOException {

#    for (int i=0; i

      submittedContent.append(String.valueOf (arg0[arg1++]));

  }

##  public void write(String content) throws IOException {

    submittedContent.append(content);

  }

// lots of uninteresting methods elided.  I just gave them

  // degenerate implementations.  (e.g. {})

#}

#不需要關心那些我省略掉的未實作方法,我認為只需要關心那些足夠使得我的測試得以運作的方法即可。對於剩下的,我只會使用其退化實現。

我的IDE對於建立這些mock類別非常有幫助。它能夠自動化的建構方法原型,並為那些介面或是抽象類別所需實現的方法給予退化的實作。

同樣的用類似方法建立出MockPageContext,MockHttpServletRequest以及MockHttpServletResponse類別。

MockPageContext

package com.objectmentor.library.web.framework .mocks;

import javax.servlet.*;

import javax.servlet .http.*;

import javax.servlet.jsp.*;

import java.io.IOException;

import java.util.Enumeration;

public class MockPageContext extends PageContext {

private final JspWriter out;

##  private HttpServletRequest request ;

  public MockPageContext(JspWriter out) {

#    this.out = out;

#    request = new MockHttpServletRequest();

########## ##  ###}############################  ###public JspWriter getOut() {######### #######    ###return out;################  ###}######################################################################################## #########  ###public ServletRequest getRequest() {###############    ###return request;######

##  }

##  // 省略了許多簡併函數。 }

MockHttpServletRequest

#MockHttpServletRequest

套件com.objectmentor.library.web.framework。 ;

匯入javax.servlet.*;

匯入javax.servlet。 .*;

導入java.io.*;

#導入java.security.Principal;

##import java.util.*;

#公共類別MockHttpServletRequest 實作HttpServletRequest {

## 

私有字串方法;

私有字串contextPath;

 

private String requestURI;## 

private HttpSession session = new MockHttpSession();

##  私有對應參數= new HashMap();

私人對應屬性= new HashMap();

##  public MockHttpServletRequest(String method, String contextPath,

#                               ,String##==   this.method = 方法;

    this.contextPath = contextPath;

##    this .requestURI = requestURI;

##  }

# #  public MockHttpServletRequest() {

    this("GET");

##  }

public MockHttpServletRequest(字串方法){

    this(方法, "/Library", "/Library/foo/bar.jsp");

}

public String getContextPath() {

##    return contextPath;

##  }

  public String getMethod() {

#############    ###回傳方法;######## #########  ###} ############################ ###public String getRequestURI() {# ############# #    ###返回請求URI;##############  ###}############################################################### ############# ###  ###public String getServletPath() {###############    ###return requestURI.substring(getContextPath( ).length());#######

##  }

##  public HttpSession getSession() {

    回傳會話;

  }

## 

public HttpSession getSession(boolean arg0) {

#    return session;

}

public Object getAttribute (字串arg0) {

   

return attribute.get(arg0);

}

public String getParameter(String arg0) {

  return(字串)parameters.get(arg0);

## 

}

  public Map getParameterMap() {

##   

回傳參數;

##  }

公開枚舉getParameterNames() {

##    return null;

}

  public void setSession(HttpSession session) {

##    this.session = 會話;

}

##  public void setParameter(String s, String s1) {

   

#parameters.put(s, s1);

## 

}

#public void setAttribute(字串名稱, 物件值) {

##    attributes.put(name, 值);

## 

}# ###########################  ###// 省略了許多簡併方法。 ############} #########################MockHttpServletResponse############# package com.objectmentor.library.web.framework.mocks; ########################導入javax.servlet.ServletOutputStream;####### ######導入javax.servlet.http。 .Locale;########## ##############public class MockHttpServletResponse Implements HttpServletResponse {##############  #####  ##### #// 所有功能實現為退化。 #有了這些mock對象,現在我就可以建立一個LoanRecords_jsp 的 servlet 實例並開始呼叫它!
<br/>
登入後複製
登入後複製
登入後複製
  public void testSimpleTest() throws Exception {
登入後複製
    MockJspWriter jspWriter = new MockJspWriter();
登入後複製
    MockPageContext pageContext = new MockPageContext(jspWriter);
登入後複製
    JspFactory.setDefaultFactory(new MockJspFactory(pageContext));
登入後複製
    HttpJspBase jspPage = new loanRecords_jsp();
登入後複製
登入後複製
    HttpServletRequest request = new MockHttpServletRequest();
登入後複製
    HttpServletResponse response = new MockHttpServletResponse();
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    jspPage._jspInit();
登入後複製
登入後複製
    jspPage._jspService(request, response);
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    assertEquals("", jspWriter.getContent());
登入後複製
  }
登入後複製
登入後複製
<br/>
登入後複製
登入後複製
登入後複製

就像预期的一样,测试失败了。这是因为还有些内容还没补充上,不过所剩无多。如果你仔细的看过Jsp文件,你就会发现它调用了request.getAttribute(“loanRecords”)并且期望返回一个List。但因为目前的测试并未为这样的属性赋值,从而导致了代码抛出了异常。

要想成功让servlet输出HTML,我们还需要加载这个属性。然后,我们就可以使用HtmlUnit来解析此HTML并且编写相应的单元测试。

HtmlUnit非常的容易使用,尤其是在测试所产生的像是本例这样的web pages上。我这里还有篇文章详细的介绍了它。

下面就是最终测试加载属性的测试,它通过htmlunit来检测HTML,并且做出正确的判断:

<br/>
登入後複製
登入後複製
登入後複製
package com.objectmentor.library.jspTest.books.patrons.books;
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
import com.gargoylesoftware.htmlunit.*;
登入後複製
import com.gargoylesoftware.htmlunit.html.*;
登入後複製
import com.objectmentor.library.jsp.WEB_002dINF.pages.patrons.books.loanRecords_jsp;
登入後複製
import com.objectmentor.library.utils.*;
登入後複製
import com.objectmentor.library.web.controller.patrons.LoanRecord;
登入後複製
import com.objectmentor.library.web.framework.mocks.*;
登入後複製
import junit.framework.TestCase;
登入後複製
import org.apache.jasper.runtime.HttpJspBase;
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
import javax.servlet.*;
登入後複製
import javax.servlet.http.*;
登入後複製
import javax.servlet.jsp.*;
登入後複製
import java.util.*;
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
public class LoanRecordsJspTest extends TestCase {
登入後複製
  private MockPageContext pageContext;
登入後複製
  private MockJspWriter jspWriter;
登入後複製
  private JspFactory mockFactory;
登入後複製
  private MockHttpServletResponse response;
登入後複製
  private MockHttpServletRequest request;
登入後複製
  private WebClient webClient;
登入後複製
  private TopLevelWindow dummyWindow;
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  protected void setUp() throws Exception {
登入後複製
    jspWriter = new MockJspWriter();
登入後複製
    pageContext = new MockPageContext(jspWriter);
登入後複製
    mockFactory = new MockJspFactory(pageContext);
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    JspFactory.setDefaultFactory(mockFactory);
登入後複製
    response = new MockHttpServletResponse();
登入後複製
    request = new MockHttpServletRequest();
登入後複製
    webClient = new WebClient();
登入後複製
    webClient.setJavaScriptEnabled(false);
登入後複製
    dummyWindow = new TopLevelWindow("", webClient);
登入後複製
  }
登入後複製
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  public void testLoanRecordsPageGeneratesAppropriateTableRows() throws Exception {
登入後複製
    HttpJspBase jspPage = new loanRecords_jsp();
登入後複製
登入後複製
    jspPage._jspInit();
登入後複製
登入後複製
 <br/>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    List<LoanRecord> loanRecords = new ArrayList<LoanRecord>();<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">    addLoanRecord(loanRecords,
登入後複製
登入後複製
                  "99",
登入後複製
                  "Empire",
登入後複製
                  DateUtil.dateFromString("2/11/2007"),
登入後複製
                  new Money(4200));
登入後複製
    addLoanRecord(loanRecords,
登入後複製
                  "98",
登入後複製
                  "Orbitsville",
登入後複製
                  DateUtil.dateFromString("2/12/2007"),
登入後複製
                  new Money(5200));
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    request.setAttribute("loanRecords", loanRecords);
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    jspPage._jspService(request, response);
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    StringWebResponse stringWebResponse = new StringWebResponse(jspWriter.getContent());
登入後複製
    HtmlPage page = HTMLParser.parse(stringWebResponse, dummyWindow);
登入後複製
    HtmlElement html = page.getDocumentElement();
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    HtmlTable table = (HtmlTable) html.getHtmlElementById("loanRecords");
登入後複製
    List<HtmlTableRow> rows = table.getHtmlElementsByTagName("tr");
登入後複製
    assertEquals(3, rows.size());
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    assertEquals("even", classOfElement(rows.get(1)));
登入後複製
    assertEquals("odd", classOfElement(rows.get(2)));
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    List<HtmlTableDataCell> firstRowCells = rows.get(1).getCells();
登入後複製
    assertEquals(4, firstRowCells.size());
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    List<HtmlTableDataCell> secondRowCells = rows.get(2).getCells();
登入後複製
    assertEquals(4, secondRowCells.size());
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    assertLoanRecordRowEquals("99", "Empire", "02/11/2007", "$42.00", firstRowCells);
登入後複製
    assertLoanRecordRowEquals("98", "Orbitsville", "02/12/2007", "$52.00", secondRowCells);
登入後複製
  }
登入後複製
登入後複製
登入後複製
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  private String classOfElement(HtmlTableRow firstDataRow) {return firstDataRow.getAttributeValue("class");}
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  private void assertLoanRecordRowEquals(String id, String title, String dueDate, String fine, List<HtmlTableDataCell> rowCells) {
登入後複製
    assertEquals(id, rowCells.get(0).asText());
登入後複製
    assertEquals(title, rowCells.get(1).asText());
登入後複製
    assertEquals(dueDate, rowCells.get(2).asText());
登入後複製
    assertEquals(fine, rowCells.get(3).asText());
登入後複製
  }
登入後複製
登入後複製
登入後複製
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  private void addLoanRecord(List<LoanRecord> loanRecords, String id, String title, Date dueDate, Money fine) {
登入後複製
    LoanRecord loanRecord = new LoanRecord();
登入後複製
    loanRecord.id = id;
登入後複製
    loanRecord.title = title;
登入後複製
    loanRecord.dueDate = dueDate;
登入後複製
    loanRecord.fine = fine;
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    loanRecords.add(loanRecord);
登入後複製
  }
登入後複製
登入後複製
登入後複製
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  private class MockJspFactory extends JspFactory {
登入後複製
    private PageContext pageContext;
登入後複製
    public MockJspFactory(PageContext pageContext) {
登入後複製
      this.pageContext = pageContext;
登入後複製
    }
登入後複製
登入後複製
登入後複製
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    public PageContext getPageContext(Servlet servlet, ServletRequest servletRequest, ServletResponse servletResponse, String string, boolean b, int i, boolean b1) {
登入後複製
      return pageContext;
登入後複製
    }
登入後複製
登入後複製
登入後複製
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    public void releasePageContext(PageContext pageContext) {
登入後複製
    }
登入後複製
登入後複製
登入後複製
登入後複製
 <br>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
    public JspEngineInfo getEngineInfo() {
登入後複製
      return null;
登入後複製
    }
登入後複製
登入後複製
登入後複製
登入後複製
  }
登入後複製
登入後複製
登入後複製
登入後複製
}
登入後複製

<br>
登入後複製

<span style="font-size: 9pt;">上述的测试确保了所生成的HTML中表格中的每一行都具有正确的内容。这项测试确实能够测出是否存在这样的表格,并且判断出是否表格的每一行是按照正确的顺序来展现的。同时,它也确保了每一行的相应style。测试忽略了此外的表单以及语法部分。</span>

结论

这篇发表在此的技术能够用来测试几乎所有目前我们所见过的web页面,并且脱离容器,也无需web server的运行。相对来说,它也比较容易去设置,并且非常易于扩展。有了它,你就可以快速的进行编辑、编译、测试的周期性迭代,并且你也能遵循测试驱动开发的原则了。<br>

(原文链接网址: http://blog.objectmentor.com/articles/category/testing-guis; Robert C. Martin的英文blog网址: http://blog.objectmentor.com/ 

作者简介:Robert C. Martin是Object Mentor公司总裁,面向对象设计、模式、UML、敏捷方法学和极限编程领域内的资深顾问。他不仅是Jolt获奖图书《敏捷软件开发:原则、模式与实践》(中文版)(《敏捷软件开发》(英文影印版))的作者,还是畅销书Designing Object-Oriented C++ Applications Using the Booch Method的作者。Martin是Pattern Languages of Program Design 3和More C++ Gems的主编,并与James Newkirk合著了XP in Practice。他是国际程序员大会上著名的发言人,并在C++ Report杂志担任过4年的编辑。

ID 很好
");       out.print(loanRecord.id);

#      out.write("/n");

##      out.write("   

");      out.print(loanRecord.title);

##      out.write("/n");

#      out.write("

");#      out.print(DateUtil.dateToString(loanRecord.dueDate));

##     

##      out .

     

out .    write("/n");

      out.write("   

");

#     

     

out.print(loanRecord.fine.toString());

#      out.write("/n");

      out.write("

   

以上是如何測試容器外的JSP頁面的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何在Word複製頁面 如何在Word複製頁面 Feb 20, 2024 am 10:09 AM

是否要複製MicrosoftWord中的頁面,並保持格式不變?這是一個聰明的想法,因為當您想要建立特定文件佈局或格式的多個副本時,在Word中複製頁面可能是一種有用的節省時間的技術。本指南將逐步引導您在Word中複製頁面的過程,無論是建立範本還是複製文件中的特定頁面。這些簡單的說明旨在幫助您輕鬆地重新製作頁面,省去從頭開始的麻煩。為什麼要在MicrosoftWord中複製頁面?在Word中複製頁面非常有益的原因有以下幾點:當您有一個具有特定佈局或格式的文件要複製時。與從頭開始重新建立整個頁面不同

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何快速刷新網頁? 如何快速刷新網頁? Feb 18, 2024 pm 01:14 PM

頁面刷新在我們日常的網路使用中非常常見,當我們訪問一個網頁後,有時會遇到一些問題,例如網頁加載不出來或顯示不正常等。這時候我們通常會選擇刷新頁面來解決問題,那麼要如何快速刷新頁面呢?下面我們就來探討頁面刷新的快捷鍵。頁面刷新快捷鍵是一種透過鍵盤操作來快速刷新目前網頁的方法。在不同的作業系統和瀏覽器中,頁面刷新的快捷鍵可能有所不同。下面我們以常見的W

華為、浪潮等單位合作創建的開源容器鏡像中心,AtomHub,宣布正式開放公測,可穩定下載國內服務 華為、浪潮等單位合作創建的開源容器鏡像中心,AtomHub,宣布正式開放公測,可穩定下載國內服務 Jan 02, 2024 pm 03:54 PM

華為官方消息顯示,開放原子開發者大會以「一切為了開發者」為主題,在無錫舉辦了兩天,時間為12月16日至17日會上,由開放原子開源基金會主導,華為、浪潮、DaoCloud、諧雲、青雲、颶風引擎以及OpenSDV開源聯盟、openEuler社群、OpenCloudOS社群等成員單位共同發起建置的AtomHub可信任鏡像中心正式開放公測。 AtomHub秉承共建、共治、共享的理念,旨在為開源組織和開發者提供中立、開放共建的可信開源容器鏡像中心。鑑於DockerHub等鏡像倉庫的不穩定性和不可控性,以及一些

3秒跳轉頁面實作方法:PHP程式指南 3秒跳轉頁面實作方法:PHP程式指南 Mar 25, 2024 am 10:42 AM

標題:3秒跳轉頁面實作方法:PHP程式設計指南在網頁開發中,頁面跳轉是常見的操作,一般情況下我們使用HTML中的meta標籤或JavaScript的方法進行頁面跳轉。不過,在某些特定的情況下,我們需要在伺服器端進行頁面跳轉。本文將介紹如何使用PHP程式實作一個在3秒內自動跳到指定頁面的功能,同時會給出具體的程式碼範例。 PHP實現頁面跳躍的基本原理PHP是一種在

處理Laravel頁面無法正確顯示CSS的方法 處理Laravel頁面無法正確顯示CSS的方法 Mar 10, 2024 am 11:33 AM

《處理Laravel頁面無法正確顯示CSS的方法,需要具體程式碼範例》在使用Laravel框架開發Web應用程式時,有時候會遇到頁面無法正確顯示CSS樣式的問題,這可能會導致頁面呈現不正常的樣式,影響使用者體驗。本文將介紹一些處理Laravel頁面無法正確顯示CSS的方法,並提供具體的程式碼範例,幫助開發者解決這個常見問題。一、檢查檔案路徑首先要檢查CSS檔案的路徑是

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

See all articles