容器外的JSP页面测试技术

Jsp测试技术

开发web应用程序最恼人的一点就是想要测试的话你就必须向将其部署好。当然,并不是所有部分都这样。如果你是经过了精心的设计的话,你可以在Java程序中测试业务逻辑。你可以在应用服务器不运行的情况下测试数据访问、接口以及存储过程。不过如果是测试GUI的话(由Jsp所产生的HTMl),你就必须向将其部署,然后才可能测试。

很多的团队求助于SelleniumMercury或是其他的一些工具通过web server来测试GUI。然而,即使是页面的内容不变但样式变了得情况也会让测试变得脆弱不堪。其他的团队使用Cactus解决这种脆弱性,或是用HtmlUnitHttpUnit这样原始的工具来监测web应用程序所生成的HTML。对于这些问题,我会在另一系列的blog之中来谈论。

本文之中我会介绍一种简单易行的技术,它使用JUnit或是HtmlUnit来测试Jsp页面,并且完全脱离容器。这项技术的优势也在此。

你不必一定保持容器的运行,甚至存在。你可以在选择特定的webserver之前就测试你的Jsp

你不必在每次修改后重新部署,因而编辑/编译/测试的过程会更迅速。

你可以使用测试优先开发的方式来持续的构建Jsp

容器外测试Jsp技术之所以并不盛行是因为Jsp在设计上就运行于容器内的。设计者从未过多的想过容器外运行的可能。因此由Jsp编译器的所生成代码往往依赖于容器所提供的诸多组件。即使是生成Jsp代码的工具也假定了你已经有一个成功部署的web应用程序在运行。因此,为了在容器外运行,你就要开发出相应的这些工具和组件。

依赖管理的抱怨

为什么这么多框架和工具的设计者们总期望你生活在他们提供的狭小世界中?为什么我必须先构建出完整的web应用才能编译Jsp?为什么这些东西一定要运行在容器中?信息隐藏早在10年前就已经是优秀软件设计的基本信条了。我们这个行业何时才能认真对待它?

编译Jsp

测试Jsp的第一步是将其编译为servlet。实现这一步,我们还需要先将Jsp转换成Java格式。Apache提供了一个叫做Jasper的工具,我们调用JasperMyPage.jsp创建一个Java格式的源文件MyPage_jsp.java。然后,你就可以使用你最喜欢的IDE编译这个文件成Servlet

可惜Jasper并非是设计用在命令行中使用的,或者说并不是完全这样设计的。但Jasper确有一个main函数用来处理命令行参数,而且通过调用java org.apache.jasper.JspC就能够轻易调用它了。不过,Jasper期望它所运行的环境与容器环境是保持一致的。你要确保classpath中有了很多apacheJar文件,而且它要能找到web应用程序的web.xml。它还需要能够找到包含web应用程序Jar以及TLD文件等的WEB-INF目录。简而言之,Jasper需要能找到一个完整的web应用程序。

如果事情更糟的话,除非是与TOMCAT的调用方式保持完全一致,否则某些特定的Jasper版本(我用的是tomcat 5.5.20)存在一些bug,它生成的代码会有一些错误。

第一点要做的虽然繁琐但还算简单,你需要创建好正确的目录以及文件结构,然后在AntClasspath更容易控制)中调用Jasper。第二点就需要一定的研究和测试才能让它跑起来。以下就是能成功运行的ant文件。JspC的调用出现在最后一个任务中。

<project name="Library" default="compile" basedir=".">

  <property environment="env"/>

  <property name="build.home" value="${basedir}/build"/>

  <property name="build.war.home" value="${build.home}/war"/>

  <property name="build.classes.home" value="${build.home}/classes"/>

  <property name="build.jar.home" value="${build.home}/jars"/>

  <property name="catalina.home" value="${env.CATALINA_HOME}"/>

  <property name="dist.home" value="${basedir}/dist"/>

  <property name="web.home" value="${basedir}/web"/>

 

  <path id="compile.classpath">

    <fileset dir="lib">

      <include name="*.jar"/>

    </fileset>

    <pathelement location="${catalina.home}/common/classes"/>

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

      <include name="*.jar"/>

    </fileset>

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

      <include name="*.jar"/>

    </fileset>

    <pathelement location="${catalina.home}/shared/classes"/>

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

      <include name="*.jar"/>

    </fileset>

  </path>

 

  <target name="clean">

    <delete dir="${build.home}"/>

    <delete dir="${dist.home}"/>

  </target>

 

  <target name="compile">

    <mkdir dir="${build.classes.home}"/>

    <javac srcdir="${src.home}" destdir="${build.classes.home}" excludes="**/*Test.java">

      <classpath refid="compile.classpath"/>

    </javac>

  </target>

 

  <target name="jar" depends="compile">

    <mkdir dir="${build.jar.home}"/>

    <jar jarfile="${build.jar.home}/application.jar" basedir="${build.classes.home}" includes="**/application/**/*.class" />

  </target>

 

  <target name="dist" depends="jar">

    <copy todir="${build.war.home}">

      <fileset dir="${web.home}"/>

    </copy>

 

    <copy todir="${build.war.home}/WEB-INF/lib">

      <fileset dir="${build.jar.home}" includes="*.jar"/>

    </copy>

 

    <mkdir dir="${dist.home}"/>

    <jar jarfile="${dist.home}/${app.name}.war" basedir="${build.war.home}"/>

  </target>

 

  <target name="jsp" depends="dist">

    <delete dir="${basedir}/testjsp"/>

    <java classname="org.apache.jasper.JspC" fork="true">

      <arg line="-v -d ${basedir}/testjsp -p com.objectmentor.library.jsp -mapped -compile -webapp ${build.war.home}"/>

      <arg line="WEB-INF/pages/patrons/books/loanRecords.jsp"/>

      <classpath>

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

          <include name="*.jar"/>

        </fileset>

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

          <include name="*.jar"/>

        </fileset>

        <fileset dir="${catalina.home}/bin">

          <include name="*.jar"/>

        </fileset>

        <fileset dir="${build.war.home}/WEB-INF/lib">

          <include name="*.jar"/>

        </fileset>

        <pathelement location="/Developer/Java/Ant/lib/ant.jar"/>

      </classpath>

    </java>

    <jar jarfile="${build.jar.home}/jsp.jar" basedir="${basedir}/testjsp"

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

      />

  </target>

</project>

当然,你要让所有标准文件以及目录都在${build.war.home}之下以确保工作。如果你在你的Jsp之中使用了自定义tag的话,还要确保所有相应的TLD文件都在你的TLD目录之中。

要注意的是,在ant文件中调用Jspc的命令行,而不是使用Tomcat所提供的JspCAnt 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) {

%>

<table class="list" id="loanRecords">

  <tr>

    <th>ID</th>

    <th>Title</th>

    <th>Due date</th>

    <th>Fine</th>

  </tr>

  <%

    for (int i = 0; i < loanRecords.size(); i++) {

      LoanRecord loanRecord = (LoanRecord) loanRecords.get(i);

  %>

  <tr class="<%=i%2==0?"even":"odd"%>">

    <td><%=loanRecord.id%>

    </td>

    <td><%=loanRecord.title%>

    </td>

    <td><%=DateUtil.dateToString(loanRecord.dueDate)%>

    </td>

    <td><%=loanRecord.fine.toString()%>

    </td>

  </tr>

  <%

    }

  %>

</table>

<%

  }

%>

下面则是Jasper所生成的代码。

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

 

import javax.servlet.*;

import 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;

 

public final class loanRecords_jsp extends 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 request, HttpServletResponse response)

        throws java.io.IOException, ServletException {

 

    JspFactory _jspxFactory = null;

    PageContext pageContext = null;

    HttpSession session = null;

    ServletContext application = null;

    ServletConfig config = null;

    JspWriter out = null;

    Object page = this;

    JspWriter _jspx_out = null;

    PageContext _jspx_page_context = null;

 

    try {

      _jspxFactory = JspFactory.getDefaultFactory();

      response.setContentType("text/html");

      pageContext = _jspxFactory.getPageContext(this, request, response,

                  null, true, 8192, true);

      _jspx_page_context = pageContext;

      application = pageContext.getServletContext();

      config = pageContext.getServletConfig();

      session = pageContext.getSession();

      out = pageContext.getOut();

      _jspx_out = out;

 

      out.write('/n');

      out.write('/n');

      out.write('/n');

 

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

  if (loanRecords.size() > 0) {

 

      out.write("/n");

      out.write("<table class=/"list/" id=/"loanRecords/">/n");

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

      out.write("    <th>ID</th>/n");

      out.write("    <th>Title</th>/n");

      out.write("    <th>Due date</th>/n");

      out.write("    <th>Fine</th>/n");

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

      out.write("  ");

 

    for (int i = 0; i < loanRecords.size(); i++) {

      LoanRecord loanRecord = (LoanRecord) loanRecords.get(i);

 

      out.write("/n");

      out.write("  <tr class=/"");

      out.print(i%2==0?"even":"odd");

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

      out.write("    <td>");

      out.print(loanRecord.id);

      out.write("/n");

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

      out.write("    <td>");

      out.print(loanRecord.title);

      out.write("/n");

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

      out.write("    <td>");

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

      out.write("/n");

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

      out.write("    <td>");

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

      out.write("/n");

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

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

      out.write("  ");

 

    }

 

      out.write("/n");

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

 

  }

 

    } catch (Throwable t) {

      if (!(t instanceof SkipPageException)){

        out = _jspx_out;

        if (out != null && out.getBufferSize() != 0)

          out.clearBuffer();

        if (_jspx_page_context != 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中获得的。如果我们能够创建一个JspWritermock 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) {

    }

 

    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<arg2; 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类非常有帮助。它能够自动化的构建方法原型,并为那些接口或是抽象类所需要实现的方法给出退化的实现。

同样的用类似方法创建出MockPageContextMockHttpServletRequest以及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;

  }

  // lots of degenerate functions elided.

}

 

MockHttpServletRequest

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

 

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.security.Principal;

import java.util.*;

 

public class MockHttpServletRequest implements HttpServletRequest {

 

  private String method;

  private String contextPath;

  private String requestURI;

  private HttpSession session = new MockHttpSession();

  private Map parameters = new HashMap();

  private Map attributes = new HashMap();

 

  public MockHttpServletRequest(String method, String contextPath,

                                String requestURI) {

    super();

    this.method = method;

    this.contextPath = contextPath;

    this.requestURI = requestURI;

  }

 

  public MockHttpServletRequest() {

    this("GET");

  }

 

  public MockHttpServletRequest(String method) {

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

  }

 

  public String getContextPath() {

    return contextPath;

  }

 

  public String getMethod() {

    return method;

  }

 

  public String getRequestURI() {

    return requestURI;

  }

 

  public String getServletPath() {

    return requestURI.substring(getContextPath().length());

  }

 

  public HttpSession getSession() {

    return session;

  }

 

  public HttpSession getSession(boolean arg0) {

    return session;

  }

 

  public Object getAttribute(String arg0) {

    return attributes.get(arg0);

  }

 

  public String getParameter(String arg0) {

    return (String) parameters.get(arg0);

  }

 

  public Map getParameterMap() {

    return parameters;

  }

 

  public Enumeration getParameterNames() {

    return null;

  }

 

  public void setSession(HttpSession session) {

    this.session = session;

  }

 

  public void setParameter(String s, String s1) {

    parameters.put(s, s1);

  }

 

  public void setAttribute(String name, Object value) {

    attributes.put(name, value);

  }

 

  // Lots of degenerate methods elided.

}

 

MockHttpServletResponse

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

 

import javax.servlet.ServletOutputStream;

import javax.servlet.http.*;

import java.io.*;

import java.util.Locale;

 

public class MockHttpServletResponse implements HttpServletResponse {

  // all functions are implemented to be degenerate.

}

有了这些mock对象,现在我就可以创建一个loanRecords_jspservlet实例并且开始调用它!我的头一个测试用例就像下面这样:

  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();
    
    

    
    
     
      
    
    
    jspPage._jspInit();
    
    
    jspPage._jspService(request, response);
    
    

    
    
     
      
    
    
    assertEquals("", jspWriter.getContent());
    
    
  }

  
  

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

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

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

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

package com.objectmentor.library.jspTest.books.patrons.books;
    
    

    
    
     
      
    
    
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;
    
    

    
    
     
      
    
    
import javax.servlet.*;
    
    
import javax.servlet.http.*;
    
    
import javax.servlet.jsp.*;
    
    
import java.util.*;
    
    

    
    
     
      
    
    
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;
    
    

    
    
     
      
    
    
  protected void setUp() throws Exception {
    
    
    jspWriter = new MockJspWriter();
    
    
    pageContext = new MockPageContext(jspWriter);
    
    
    mockFactory = new MockJspFactory(pageContext);
    
    

    
    
     
      
    
    
    JspFactory.setDefaultFactory(mockFactory);
    
    
    response = new MockHttpServletResponse();
    
    
    request = new MockHttpServletRequest();
    
    
    webClient = new WebClient();
    
    
    webClient.setJavaScriptEnabled(false);
    
    
    dummyWindow = new TopLevelWindow("", webClient);
    
    
  }
    
    

    
    
     
      
    
    
  public void testLoanRecordsPageGeneratesAppropriateTableRows() throws Exception {
    
    
    HttpJspBase jspPage = new loanRecords_jsp();
    
    
    jspPage._jspInit();
    
    

    
    
     
      
    
    
    List<LoanRecord> loanRecords = new ArrayList<LoanRecord>();
    
    
    addLoanRecord(loanRecords,
    
    
                  "99",
    
    
                  "Empire",
    
    
                  DateUtil.dateFromString("2/11/2007"),
    
    
                  new Money(4200));
    
    
    addLoanRecord(loanRecords,
    
    
                  "98",
    
    
                  "Orbitsville",
    
    
                  DateUtil.dateFromString("2/12/2007"),
    
    
                  new Money(5200));
    
    

    
    
     
      
    
    
    request.setAttribute("loanRecords", loanRecords);
    
    

    
    
     
      
    
    
    jspPage._jspService(request, response);
    
    

    
    
     
      
    
    
    StringWebResponse stringWebResponse = new StringWebResponse(jspWriter.getContent());
    
    
    HtmlPage page = HTMLParser.parse(stringWebResponse, dummyWindow);
    
    
    HtmlElement html = page.getDocumentElement();
    
    

    
    
     
      
    
    
    HtmlTable table = (HtmlTable) html.getHtmlElementById("loanRecords");
    
    
    List<HtmlTableRow> rows = table.getHtmlElementsByTagName("tr");
    
    
    assertEquals(3, rows.size());
    
    

    
    
     
      
    
    
    assertEquals("even", classOfElement(rows.get(1)));
    
    
    assertEquals("odd", classOfElement(rows.get(2)));
    
    

    
    
     
      
    
    
    List<HtmlTableDataCell> firstRowCells = rows.get(1).getCells();
    
    
    assertEquals(4, firstRowCells.size());
    
    

    
    
     
      
    
    
    List<HtmlTableDataCell> secondRowCells = rows.get(2).getCells();
    
    
    assertEquals(4, secondRowCells.size());
    
    

    
    
     
      
    
    
    assertLoanRecordRowEquals("99", "Empire", "02/11/2007", "$42.00", firstRowCells);
    
    
    assertLoanRecordRowEquals("98", "Orbitsville", "02/12/2007", "$52.00", secondRowCells);
    
    
  }
    
    

    
    
     
      
    
    
  private String classOfElement(HtmlTableRow firstDataRow) {return firstDataRow.getAttributeValue("class");}
    
    

    
    
     
      
    
    
  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());
    
    
  }
    
    

    
    
     
      
    
    
  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;
    
    

    
    
     
      
    
    
    loanRecords.add(loanRecord);
    
    
  }
    
    

    
    
     
      
    
    
  private class MockJspFactory extends JspFactory {
    
    
    private PageContext pageContext;
    
    
    public MockJspFactory(PageContext pageContext) {
    
    
      this.pageContext = pageContext;
    
    
    }
    
    

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

    
    
     
      
    
    
    public void releasePageContext(PageContext pageContext) {
    
    
    }
    
    

    
    
     
      
    
    
    public JspEngineInfo getEngineInfo() {
    
    
      return null;
    
    
    }
    
    
  }
    
    
}

   
   

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

结论

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

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

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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 要创建 4 个 JSP 页面,您需要遵循以下步骤: 1. 安装并配置 Java 运行环境和 Web 服务器(如 Apache Tomcat)。 2. 在 Web 服务器的 Web 根目录下创建一个文件夹,该文件夹将存储您的 JSP 文件。 3. 使用文本编辑器(例如 Notepad++ 或 Sublime Text)打开一个新文件,并将其保存为 .jsp 文件。 4. 在 JSP 文件中编写 HTML 和 Java 代码。 5. 将 JSP 文件保存到 Web 服务器的 Web 根目录下的文件夹中。 6. 重复步骤 3 到 5,直到创建所有 4 个 JSP 文件。 7. 启动 Web 服务器,并通过浏览器访问您的 JSP 文件。 注意:具体的 JSP 编写方式取决于您希望实现的功能。此,请确保您对 Java 和 Web 开发有一定的了解,以编写有效的 JSP 代码。 ### 回答2: 要创建4个JSP页面,可以按照以下步骤进行操作: 步骤一:新建文件夹 首先,创建一个新的文件夹,用于存放JSP页面和其他相关文件。 步骤二:创建JSP文件 在新建的文件夹中,创建4个后缀为.jsp的文件。可以使用文本编辑器(如Notepad++、Sublime Text等)来创建这些文件。 步骤三:编写JSP代码 在每个JSP文件中,编写相应的JSP代码。你可以根据具体需求来编写这些代码,例如显示页面内容、处理表单数据、展示数据库数据等。 步骤四:保存和测试 保存好每个JSP文件,并确保文件名和扩展名正确。然后,将这些文件部署到一个支持JSP的Web容器中(如Apache Tomcat)。 步骤五:访问JSP页面 启动Web容器后,在浏览器中输入相应的URL来访问这些JSP页面。例如,如果你的Web容器运行在本地的8080端口,而且某个JSP文件命名为index.jsp,则可以在浏览器中输入"http://localhost:8080/文件夹名字/index.jsp"来访问该页面。 综上所述,要创建4个JSP页面,你需要新建一个文件夹并在其中创建4个后缀为.jsp的文件。然后,编写相应的JSP代码,保存文件并部署到Web容器中。最后,通过浏览器来访问这些页面。 ### 回答3: 要创建4个jsp页面,可以按照以下步骤进行操作: 第一步是创建一个新的Dynamic Web Project(动态Web项目)。 可以使用Eclipse或者其他IDE来创建一个新的Web项目。选择Dynamic Web Project,并按照提示进行项目的设置。 第二步是创建jsp文件。 在项目的WebContent目录下创建一个新的目录,可以命名为jsp。在该目录下创建4个jsp文件,可以分别命名为page1.jsp、page2.jsp、page3.jsp和page4.jsp。这些jsp文件将被用于显示网页的内容。 第三步是编辑jsp文件。 打开每个jsp文件,并添加所需的HTML和JSP代码。根据页面的设计和功能需求,可以添加文本、图片、表单、链接等等。可以使用JSP标签和表达式来嵌入Java代码。 第四步是在web.xml文件中配置jsp页面。 在WebContent/WEB-INF目录下找到web.xml文件,并打开该文件。在文件中添加对jsp页面的配置,以便可以在浏览器中正确访问这些页面。可以使用如下配置示例: <servlet> <servlet-name>page1</servlet-name> <jsp-file>/jsp/page1.jsp</jsp-file> </servlet> <servlet-mapping> <servlet-name>page1</servlet-name> <url-pattern>/page1</url-pattern> </servlet-mapping> 将上述配置重复4次,分别为4个jsp页面。 第五步是运行项目。 在IDE中启动项目,并通过浏览器访问4个jsp页面,确保它们能正确显示。 通过以上步骤,就可以创建4个jsp页面并成功在浏览器中访问了。根据项目的需要,可以继续修改和完善这些页面的功能和样式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值