使用 JUnit 测试 Java 应用程序 课堂笔记(二)

 

目录

一.JWebUnit 框架测试

二.测试 EJB 和 Servlet

三.JMeter 测

四.DBUnit 数据库测试


一.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.总结

发布了29 篇原创文章 · 获赞 21 · 访问量 1661

猜你喜欢

转载自blog.csdn.net/Lyrelion/article/details/104782431
今日推荐