目录
一.JWebUnit 框架测试
- JWebUnit 框架:用于测试 Web 应用程序 网页间导航
- 核心 JWebUnit API 取决于:包含 Assert 类各种方法的 JUnit API
- JWebUnit 框架包含 插件,例如:HtmlUnit 插件、Selenium、WebDriver 插件
1.测试 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测试用例 提供了两个方法:静态导入、委托
1.1 使用静态导入
- 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"); }
1.2 使用委托
- net.sourceforge.jwebunit.junit.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"); }
2.测试 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!"); }
二.测试 EJB 和 Servlet
1.测试 EJB应用程序
- EJB应用程序(运行在容器内的组件)包含三个 bean对象:
- 会话bean:执行特殊任务 ,包含执行任务的方法
- 实体bean:数据库中存在的对象的实体
- 消息驱动bean:在应用程序中实现 Java消息服务(JMS)
- 在 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; }
2.测试 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>
2.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"); }
2.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"); }}
三.JMeter 测试
- 模拟使用流量,可使用 Apache 提供的 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
存储方式:测试计划使用 .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>
- JMeter 测试过程:
- JMeter 生成请求并模拟一组用户
- 服务器作出响应,并且响应将被保存
- 计算所需的统计数据
- 以表和图形的形式生成并显示报告
- 完整测试包含内容:
- 线程组:指定 Web 应用程序被测试时的负载
- 控制器:配置请求、向服务器发送请求
- 侦听器:显示通过在 JMeter 中运行测试计划所收集的信息
- 计时器:指定向服务器发送的连续请求之间的延迟
- 断言:测试从服务器接收到的响应
- 配置元素:配置要在测试中使用的某些变量和默认值
- 吞吐量:服务器可处理的每秒请求数
- 建立JMeter 测试的步骤:
- 在 JMeter 中创建测试计划,并创建线程组:
- 创建样本 HTTP 请求,在样本 HTTP 请求中,指定要测试的网站的服务器名称 / IP:
- 添加摘要报告以查看测试结果:
- 创建表格报告元素查看个别请求信息:
- 区分:
- 对本地网站进行负载测试:
- 创建线程组和 HTTP 请求采样器
- server path / IP:将服务器名称指定为 localhost,将端口号指定为 8080
- 指定 Web 应用程序的路径
- 对现场网站进行负载测试:
- 直接输入网址路径 path 即可
四.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>
- 运行测试
- 验证结果
- 生成报告