目录
一.JWebUnit 框架测试
1.简介
JWebUnit 框架提供 API,用于测试 Web 应用程序中 网页之间的导航。
如:主页 → 登录页{ 欢迎登录,登录失败 }
核心 JWebUnit API 取决于包含 Assert 类各种方法的 JUnit API。
JWebUnit 框架包含插件,例如 HtmlUnit 插件和 Selenium/ WebDriver 插件。
2.测试 JSP应用程序
如果要测试 JSP Maven应用程序,需要添加 JWebUnit 依赖(在 pom.xml 文件中)
<dependency> <groupId>net.sourceforge.jwebunit</groupId> <artifactId>jwebunit-htmlunit-plugin</artifactId> <version>3.2</version> <scope>test</scope> </dependency>
JWebUnit 为创建 JUnit测试用例 提供了两个方法:静态导入、委托。
Example:
方法一:使用静态导入
(net.sourceforge.jwebunit.junit.JWebUnit 类的静态方法。)
① 测试用户 Web 应用程序功能 的测试用例:
@Before public void setUp() { setBaseUrl("http://localhost:8080/JWebUnitDemo_StaticImport/"); }
② 用于 欢迎界面 的测试用例:
@Test public void loginSuccess() { beginAt("/index.html"); clickLinkWithExactText("Login"); assertTitleEquals("Login"); setTextField("username", "user"); setTextField("password", "password@123"); submit(); assertTitleEquals("Welcome"); }
③ 用于 错误页面 的测试用例:
@Test public void loginFail() { beginAt("/index.html"); clickLinkWithExactText("Login"); assertTitleEquals("Login"); setTextField("username", "test"); setTextField("password", "test123"); submit(); assertTitleEquals("Error"); }
方法二:使用委托
(使用 net.sourceforge.jwebunit.junit.WebTester 类及其方法。)
(与JWebUnit 类的方法同名,但非静止方法,需要创建WebTester 类的实例调用方法。)
① 测试用户 Web 应用程序功能 的测试用例:
private WebTester testObj; //实例化 webtester 类对象 @Before public void setUp() { testObj = new WebTester(); testObj.setBaseUrl("http://localhost:8080/JWebUnitDemo_Delegation/"); }
② 用于 欢迎界面 的测试用例:
@Test public void loginSuccess() { testObj.beginAt("/index.html"); testObj.clickLinkWithExactText("Login"); testObj.assertTitleEquals("Login"); testObj.setTextField("username", "user"); testObj.setTextField("password", "password@123"); testObj.submit(); testObj.assertTitleEquals("Welcome"); }
③ 用于 错误页面 的测试用例:
@Test public void loginFail() { testObj.beginAt("/index.html"); testObj.clickLinkWithExactText("Login"); testObj.assertTitleEquals("Login"); testObj.setTextField("username", "test"); testObj.setTextField("password", "test123"); testObj.submit(); testObj.assertTitleEquals("Error"); }
3.测试 JSP标记库的标记
① 用 JWebUnit 类的方法测试 index.jsp 文件中 JSP标记库的标记。public class TagLibTest { ........ @Before public void setUp() { setBaseUrl("http://localhost:8080/TagLibTesting/"); } @Test public void dataAccessTest() { beginAt("/index.jsp"); submit(); assertTablePresent("Details"); assertTableRowCountEquals("Details", 3); assertTextInTable("Details", "Andrew"); assertTextInTable("Details", "California"); assertTextInTable("Details", "Nancy"); assertTextInTable("Details", "Texas"); } }
② 用于测试 自定义标记输出的 测试用例。
@Before public void setUp() { setBaseUrl("http://localhost:8080/CustomJSTL/"); } @Test public void greetingTest() { beginAt("/index.jsp"); //assertTextPresent() 方法用于测试 index.jsp 页面上是否提供了自定义标记的期望输出。 assertTextPresent("Welcome to the World of Wonders!"); }
4.总结
二.测试 EJB 和 Servlet
1.EJB应用程序简介
EJB应用程序(运行在容器内的组件)包含三个主要类型的 bean 对象。
会话bean:执行特殊任务 ,包含执行任务的方法。
实体bean:数据库中存在的对象的实体。
消息驱动bean:在应用程序中实现 Java消息服务(JMS)
2.测试 EJB应用程序
在 pom.xml 文件中添加服务器属性和依赖
<properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 下面这行是添加服务器属性 --> <glassfish.embedded-static-shell.jar>C:/Program Files/glassfish-4.1.1/glassfish/lib/embedded/glassfish-embedded-staticshell.jar</glassfish.embedded-static-shell.jar> </properties> <!-- 下面是添加服务器依赖 --> <dependency> <groupId>org.glassfish.extras</groupId> <artifactId>glassfish-embedded-static-shell</artifactId> <version>4.1</version> <scope>system</scope> <systemPath>${glassfish.embedded-static-shell.jar}</systemPath> </dependency>
测试会话bean:
@Test public void testInsertandRetrieve() throws Exception{ // 创建容器对象createEJBContainer 通过设个测试EJB EJBContainer container = javax.ejb.embeddable.EJBContainer.createEJBContainer(); // 获取上下文getContext() 通过上下文查找注册的bean EmployeeManager instance = (EmployeeManager)container.getContext().lookup("java:global/classes/EmployeeManager"); // 通过实例调用参数方法 Employee emp = new Employee("EMP001","Andrew","California","eLearning",5000.00); Employee emp1 = new Employee("EMP002","Nancy","Texas","Human Resources",8000.00); instance.insertEmployee(emp); instance.insertEmployee(emp1); int res = instance.retrieveEmployee(); Assert.assertTrue(res == 2); System.out.println("Successfully finished test!"); }
测试实体bean:
public class PhoneBook implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private String name; private String number; public PhoneBook() {} public PhoneBook(String name) { this.name = name; } public PhoneBook(String name, String number) { this.name = name; this.number=number; }
3.为 Servlet 和过滤器实现测试用例
对 servlet 进行单元测试时,需要模拟 从用户接收到的请求 以及 从 Servlet 生成的响应。
需要模拟 HttpServletRequest、HttpServletResponse 和 HttpSession 接口以及 ServletOutputStream 类。
对 servlet 进行单元测试,可以使用 JUnit 以及 Mockito框架(用于模仿上面三个类)。
要测试 Maven 应用程序中的 Servlet,需要添加服务器属性和依赖 以及 Mockito框架 的依赖。
<!-- 添加服务器属性、依赖 见上面有写 --> <!-- 下面是添加 mockito框架依赖 --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency>
3.1 对 servlet 进行单元测试:
- //模拟实例 xxxHttpServletRequest(Response) 是通过mock()方法实现的
- //下面定义了 testSuccessProcessRequest() 方法,此方法用when指定条件:
- //当请求对象把 jspuid 作为参数调用getParameter()方法时, 这个参数的值应该返回admin
- //响应对象上调用 getWriter()方法,应返回 PrintWriter实例pw
- //pw 是通过StringWriter类的 sw实例创建的
- //创建ValidationServlet实例 来调用processRequest()方法
- //verify():将检索和显示存储的详细联系方式
- //响应对象上是否使用 WelcomeAdminPage.jsp 这个参数调用sendRedirect()方法
public class ValidationServletTest { HttpServletRequest mockHttpServletRequest = mock( HttpServletRequest.class); HttpServletResponse mockHttpServletResponse = mock( HttpServletResponse.class); HttpSession mockHttpSession = mock(HttpSession.class); public ValidationServletTest() { } ... @Test public void testSuccessAdmin() throws IOException, ServletException{ //模拟实例 xxxHttpServletRequest(Response) 是通过mock()方法实现的 //下面定义了 testSuccessProcessRequest() 方法,此方法用when指定条件: //当请求对象把 jspuid 作为参数调用getParameter()方法时, 这个参数的值应该返回admin when(mockHttpServletRequest.getParameter("jspuid")).thenReturn("admin"); when(mockHttpServletRequest.getParameter("jsppwd")).thenReturn("password@123"); when(mockHttpServletRequest.getParameter("r1")).thenReturn("administrator"); when(mockHttpServletRequest.getSession()).thenReturn(mockHttpSession); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); //响应对象上调用 getWriter()方法,应返回 PrintWriter实例pw //pw 是通过StringWriter类的 sw实例创建的 when(mockHttpServletResponse.getWriter()).thenReturn(pw); //创建ValidationServlet实例 来调用processRequest()方法 ValidationServlet validationServlet = new ValidationServlet (); validationServlet.processRequest(mockHttpServletRequest,mockHttpServletResponse); //verify():将检索和显示存储的详细联系方式 //响应对象上是否使用 WelcomeAdminPage.jsp 这个参数调用sendRedirect()方法 verify(mockHttpServletResponse).sendRedirect("WelcomeAdminPage.jsp"); }
3.2 对 过滤器 进行单元测试:
过滤器:客户端请求服务器之前将其拦截。
过滤器实现过程:
- public void doFilter (ServletRequest, ServletResponse, FilterChain)
- public void init(FilterConfig filterConfig)
- public void destroy()
因此,测试过滤器,需要模拟 ServletRequest, ServletResponse, FilterChain 这三个接口。
@Test public void testDoFilterMethod() throws Exception { //创建 HttpServletRequest、HttpServletResponse 和 FilterChain 的模拟对象。 HttpServletRequest mockHttpServletRequest = mock(HttpServletRequest.class); HttpServletResponse mockHttpServletResponse = mock(HttpServletResponse.class); FilterChain mockFilterChain = mock(FilterChain.class); //when() 方法:请求对象调用 getRequestURI() 方法时,是否应该返回URI/ServletFilterUnitTest_Mockito/Products.jsp。 when(mockHttpServletRequest.getRequestURI()).thenReturn("/ServletFilterUnitTest_Mockito/Products.jsp"); //创建过滤器类的实例,调用doFilter()方法模拟。 MyFilter myFilter = new MyFilter(); myFilter.doFilter(mockHttpServletRequest, mockHttpServletResponse, mockFilterChain); //verify() 方法:确保 getRequestURI() 方法调用了至少一次。 verify(mockHttpServletRequest, atLeast(1)).getRequestURI(); //verify() 方法:确保用户被重定向到 login.jsp 页面。 verify(mockHttpServletResponse).sendRedirect("/ServletFilterUnitTest_Mockito/login.jsp"); }}
4.总结
三.JMeter 测试
1.JMeter 目的
用于模拟使用流量,确保应用程序在多个用户同时访问它的情况下高效执行。
可以使用 Apache 提供的 JMeter 对 本地和现场网站 进行负载测试。
2.JMeter 安装
启动 jmeter-server.bat: java.io.FileNotFoundException: rmi_keystore.jks (系统找不到指定的文件。)解决方案:
- 找到 apache-jmeter-5.2.1\bin\jmeter.properties 文件夹
- 修改 server.rmi.ssl.disable=true (记得去除server.rmi.ssl.disable=true前的#),重新启动jmeter-server.bat
3.JMeter 测试计划
3.1 存储方式:测试计划使用 .jmx 格式保存,.jmx文件用XML格式存储测试。
<?xml version="1.0" encoding="UTF-8"?> <jmeterTestPlan version="1.2" properties="2.6" jmeter="2.11 r1554548"> <hashTree> <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true"> <stringProp name="TestPlan.comments"></stringProp> <boolProp name="TestPlan.functional_mode">false</boolProp> <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="TestPlan.user_define_classpath"></stringProp> </TestPlan> <hashTree/> </hashTree> </jmeterTestPlan>
3.2 JMeter 测试过程:
- JMeter 生成请求并模拟一组用户。
- 服务器作出响应,并且响应将被保存。
- 计算所需的统计数据。
- 以表和图形的形式生成并显示报告。
3.3 完整测试包含内容
- 线程组:用于指定 Web 应用程序被测试时的负载。
- 控制器:用于配置请求以及向服务器发送请求。
- 侦听器:用于显示通过在 JMeter 中运行测试计划所收集的信息。
- 计时器:用于指定向服务器发送的连续请求之间的延迟。
- 断言:用于测试从服务器接收到的响应。
- 配置元素:用于配置要在测试中使用的某些变量和默认值。
- 吞吐量:服务器可处理的每秒请求数。
3.4 建立JMeter 测试的步骤
- 在 JMeter 中创建测试计划。
- 创建线程组。
- 创建样本 HTTP 请求。
- 在样本 HTTP 请求中,指定要测试的网站的服务器名称 / IP。
- 添加摘要报告以查看测试结果。
- 还可以创建表格报告元素查看个别请求信息。
4.对本地网站进行负载测试
- 创建线程组和 HTTP 请求采样器。
- server path / IP:将服务器名称指定为 localhost,将端口号指定为 8080。
- 指定 Web 应用程序的路径。
5.对现场网站进行负载测试
直接输入网址路径 path 即可。
6.总结
四.DBUnit 数据库测试
1.执行数据库测试目的
测试包含多任务的应用程序时 需要将数据与测试逻辑分离 用数据驱动测试解决这个问题。
优点:独立测试脚本、降低测试用例中的冗余、可管理记录。
缺点:需要了解脚本语言、维护大型应用的数据文件、使用测试计划中的变更更新数据文件、结构化数据文件。
2.数据库测试原理
数据驱动的测试中,输入值 和 期望值可以作为 不同 的数据文件传递。
测试运行时,测试数据 和 输出值 是从数据文件中读取的,而不是使用同样的硬编码值(即写在测试文件里的数据)。
数据文件存储格式:
- Excel文件(电子表格)、
- XML文件(用户定义标记以逻辑格式存储的数据)、
- CSV文件(用逗号分隔值的文件)、
- Database(外部数据库)
3.数据库测试过程
- 设置环境:① 添加依赖:
<dependency> <groupId>org.dbunit</groupId> <artifactId>dbunit</artifactId> <version>2.5.0</version> <scope>test</scope> <type>jar</type> </dependency> <dependency> <groupId>mysql</groupId> <artifactId> mysql-connector-java </artifactId> <version>5.1.6</version> </dependency>
- ② DatabaseTestCase 类是一个抽象类,需要重写以下两个方法的实现:
▪ protected abstract IDatabaseConnection getConnection() throws Exception ▪ protected abstract IDataSet getDataSet() throws Exception
- 数据库测试文件:
public class StudentTest extends DBTestCase{ public StudentTest() { //配置数据库环境变量: System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS,"com.mysql.cj.jdbc.Driver"); System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, "jdbc:mysql://localhost:3306/dbdemo?characterEncoding=UTF-8&serverTimezone=UTC"); System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME,"root"); System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD,"aaaaaa"); } @Override //覆盖函数 继承业务逻辑 调用函数获取输入数据集 将自己准备的输入文件写进来 protected IDataSet getDataSet() throws Exception { IDataSet myInput = new FlatXmlDataSetBuilder().build(new FileInputStream("datas/studentinfo/input.xml")); return myInput; } //不加 @Test 是因为沿用junit3的习惯 public void testCheckLoginDataLoaded() throws Exception{ //业务逻辑代码 空 //获取 数据库 当前结果集 IDataSet databaseDataSet = getConnection().createDataSet(); ITable actualTable = databaseDataSet.getTable("studentinfo"); //获取 期望 结果集 自己准备的数据文件 IDataSet expectedDataSet = new FlatXmlDataSetBuilder().build(new File("datas/studentinfo/expected.xml")); ITable expectedTable = expectedDataSet.getTable("studentinfo"); //期望的数据集和 实际数据库结果集 进行比较,不是比较输入集 Assertion.assertEquals(expectedTable, actualTable); } @Override //把 输入集 读进来后 要执行的操作 protected DatabaseOperation getSetUpOperation() throws Exception{ return DatabaseOperation.CLEAN_INSERT; } @Override //执行完测试之后 要执行的操作 protected DatabaseOperation getTearDownOperation() throws Exception{ return DatabaseOperation.NONE; } }
- 数据库测试中的 输入数据文件 期待数据文件:
<!-- input.xml --> <?xml version="1.0" encoding="UTF-8"?> <!-- 这是输入数据集 --> <dataset> <!-- 标签名字是表名 属性的名字是列名 属性大小写不敏感 但是要大写都大写 --> <studentinfo StudentID="001" StudentName="John Hodge" studentmarks="85" /> <studentinfo StudentID="002" StudentName="Sarah Williams" studentmarks="95" /> <studentinfo StudentID="003" StudentName="Roger Halls" studentmarks="90" /> </dataset> <!-- expected.xml --> <?xml version="1.0" encoding="UTF-8"?> <!-- 这是期望数据集 --> <dataset> <studentinfo StudentID="001" studentname="John Hodge" studentmarks="85" /> <studentinfo StudentID="002" studentname="Sarah Williams" studentmarks="95" /> <studentinfo StudentID="003" studentname="Roger Halls" studentmarks="90" /> </dataset>
- 运行测试
- 验证结果
- 生成报告
4.总结