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

目录

一.JWebUnit 框架测试

1.测试 JSP应用程序

2.测试 JSP标记库的标记 

二.测试 EJB 和 Servlet

1.测试 EJB应用程序

2.测试 Servlet、过滤器

三.JMeter 测试

四.DBUnit 数据库测试


一.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对象:
  1. 会话bean:执行特殊任务 ,包含执行任务的方法
  2. 实体bean:数据库中存在的对象的实体
  3. 消息驱动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 对 过滤器 进行单元测试

  • 过滤器:客户端请求服务器之前将其拦截
  • 过滤器实现过程:
  1. public void doFilter (ServletRequest, ServletResponse, FilterChain)
  2. public void init(FilterConfig filterConfig)
  3. 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 (系统找不到指定的文件。)解决方案:
  1. 找到 apache-jmeter-5.2.1\bin\jmeter.properties 文件夹
  2. 修改 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 测试过程:
  1. JMeter 生成请求并模拟一组用户
  2. 服务器作出响应,并且响应将被保存
  3. 计算所需的统计数据
  4. 以表和图形的形式生成并显示报告
  • 完整测试包含内容:
  1. 线程组:指定 Web 应用程序被测试时的负载
  2. 控制器:配置请求、向服务器发送请求
  3. 侦听器:显示通过在 JMeter 中运行测试计划所收集的信息
  4. 计时器:指定向服务器发送的连续请求之间的延迟
  5. 断言:测试从服务器接收到的响应
  6. 配置元素:配置要在测试中使用的某些变量和默认值
  7. 吞吐量:服务器可处理的每秒请求数
  • 建立JMeter 测试的步骤:
  • JMeter 中创建测试计划,并创建线程组:
  • 创建样本 HTTP 请求在样本 HTTP 请求中,指定要测试的网站的服务器名称 / IP:
  • 添加摘要报告以查看测试结果:
  • 创建表格报告元素查看个别请求信息:
  • 区分:
  • 对本地网站进行负载测试:
  1. 创建线程组和 HTTP 请求采样器
  2. server path / IP:将服务器名称指定为 localhost,将端口号指定为 8080
  3. 指定 Web 应用程序的路径
  • 对现场网站进行负载测试:
  1. 直接输入网址路径 path 即可
  •   

四.DBUnit 数据库测试

1.执行数据库测试目的

  • 测试包含多任务的应用程序时,需要将数据与测试逻辑分离,数据驱动测试解决
  • 优点:独立测试脚本、降低冗余、可管理记录
  • 缺点:需了解脚本语言、维护大型应用的数据文件、使用测试计划中的变更更新数据文件、结构化数据文件

2.数据库测试原理

  • 数据驱动的测试中,输入值 和 期望值 作为 不同 的数据文件传递
  • 测试时,测试数据 和 输出值 是从数据文件中读取的,而不是使用同样的硬编码值(即写在测试文件里的数据)
  • 数据文件存储格式:
  1. Excel文件(电子表格)
  2. XML文件(用户定义标记以逻辑格式存储的数据)
  3. CSV文件(用 逗号 分隔值的文件)
  4. 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类 是抽象类,需重写以下方法:
  1. protected abstract IDatabaseConnection getConnection() throws Exception
  2. 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>
  • 运行测试
  • 验证结果
  • 生成报告

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyrelion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值