使用规则_使用 JUnit 对规则进行单元测试

本文介绍了如何针对基于规则的系统进行单元测试,特别是面对复杂的输入参数和条件。通过创建Mock类来模拟运行时环境,使用配置文件存储测试数据以提高维护性和复用性,以及编写可复用的测试用例,可以有效地管理测试过程。文章以一个性能调优工具的Java虚拟机初始堆大小评级算法为例,详细阐述了这些方法的应用。
摘要由CSDN通过智能技术生成

`引言

一个例子

我们先看一个例子,以了解对”规则”做单元测试的特点。我们有一个性能调优工具 WPA, 它能够将与性能相关的参数的值进行评估并推荐最优值。它的评估和推荐最优值算法都是基于”规则”的。

Java 虚拟机的初始堆大小(JVM initial heap size)是一个影响 JVM 的性能的关键参数。性能调优工具 WPA 有一套规则对“ JVM initial heap size ”的值进行评估(参见清单 1)。评估的结果有 5 个级别。级别“ 1 ”表示设置良好,可提高性能;级别“ 5 ”表示设置很差,会降低性能。

清单 1. JVM initial heap size rating algorithm

Rating3UpperBounds = 1024  Rating3LowerBounds = 48  Rating5UpperBounds = 1536  Rating5LowerBounds = 32  Rating3Multiplier = 4  Rating5Multiplier = 3   absoluteMaximumValue= Math.min(currentMemoryPoolSize, overallMemoryOnPartition)  / Rating3Multiplier  if (initialHeapSize > absoluteMaximumValue) {  return 4;  }  if ((initialHeapSize < Rating5LowerBounds) ||  (initialHeapSize > Rating5UpperBounds)) {  rating = severe problem (5)  }  else if ((initialHeapSize < Rating3LowerBounds) ||  (initialHeapSize > Rating3UpperBounds)) {  rating = probable problem (3)  } …… }  if (initialHeapSize * Rating5Multiplier > currentMemoryPoolSize)  {  return severe problem (5)  }  else if(initialHeapSize*Rating3Multiplier > currentMemoryPoolSize)  {  return max(rating, 3)  }  else if(initialHeapSize*Rating2Multiplier > currentMemoryPoolSize)  else {  return max(rating, 1)  }

在这一套规则中,有三个输入参数:“initialHeapSize”(“JVM initial heap size”的值),“currentMemoryPoolSize” ( 内存池的值 ) 和“overallMemoryOnPartition”(物理内存的值)。为了得到这些值,我们需要使用 Application Server 和 OS 提供的 API 。在使用这些 API 的时候,我们必须构造出 API 所需的运行环境。

在这一套规则中,包含很多不同的条件(见“ IF-ELSE ”语句)。在测试时(单元测试和功能测试),我们需要至少 24 组测试数据以覆盖所有的阀值(threshold value)和等价类(equivalent class)。参见表 1

对”规则”做单元测试

从“JVM initial heap size rating algorithm”以及 WPA 中其他基于“规则”的性能调优算法,我们总结出对“规则”做单元测试的特点有:

一、为了覆盖所有的阀值 (threshold value )和等价类 (equivalent class ),我们需要大量测试数据。单元测试的通常做法是,把所有的测试数据写入测试代码中。对比以格式化的形式(XML,Excel 等)来保存测试数据,这样做使得这些数据不容易维护和复用。

二、由于对”规则”的测试涉及到变量,这些变量来自运行时的输入,我们在单元测试之前就需要构建运行时环境,这种工作可能非常复杂。如果一套”规则”中包含更多的条件和输入参数,以上两个问题会更加严重

三、在一个基于”规则”的系统里,”规则”之间有很多共性,我们没有必要对每一个”规则”都写一个测试类。

本文将给出解决以上问题的一种做法。本文的组织结构如下:

  • 编写 Mock 类:利用 Mock 对象来代替实时运行环境;
  • 将测试数据保存到配置文件中:利用格式化文档实现测试数据的复用性和可维护性;
  • 编写 SettersMap 类:这个类保存了配置文件中的数据并提供了获取这些数据的接口;
  • 编写可复用的 TestCase 类:创建 JUnit 的扩展类以适应对“规则”做单元测试的需求;
  • 用 TestSuite 组织测试用例:用 TestSuite 把测试用例组织起来;
  • 在以下内容中,我们将拿“ JVM initial heap size rating algorithm ”做例子。

编写 Mock 类

为了测试” JVM initial heap size rating algorithm ”,我们需要获得三个输入参数。然而,获取这三个参数并不是那么容易。

为了简化测试环境,我们利用 Mock 对象来设置这些参数。

Mock 对象是单元测试经常用到的一种技术,Mock 对象能模拟实际对象的行为,并且提供了额外的行为控制接口。还有一个常用到的词是 Dummy 对象。 Mock 和 Dummy 的含义经常被混淆。在这里,我们认为 Dummy 对象没有提供额外的行为控制接口。

对于” JVM initial heap size rating algorithm ”,我们需要一个 Mock 类,它的行为与“ InitialHeapSize.java ”相同(“ InitialHeapSize.java ”是 “ JVM initial heap size rating algorithm ”的 Java 代码)。我们把这个 Mock 类命名为“ MockInitialHeapSize.java ”。一个 Client 类可以把“ initialHeapSize ” , “ currentMemoryPoolSize ” , 和“ overallMemoryOnPartition ” 直接设置到“ MockInitialHeapSize ”对象中。参见清单 2

清单 2. MockInitialHeapSize.java

public class MockInitialHeapSize extends InitialHeapSize {  // 设置 InitialHeapSize  public void setInitialValue(String initialValue){  this.initialValue = initialValue;  }  // 设置 MemoryPoolSize  public void mockSetMemoryPoolSize(String size) {  try{  this.currentSettingOfMemoryPoolSize=Float.parseFloat(size);  }catch(NumberFormatException ne){  Advisor.getLogger().severe("size: "+size+" are not an float value.");  }  }  // 设置 OverallMemory  public void mockSetOverallMemory(String size) {  try{  this.overallMemoryOnPartition=Float.parseFloat(size);  }catch(NumberFormatException ne){  Advisor.getLogger().severe("size: "+size+" are not an float value.");  }  }  …… }

将测试数据保存到配置文件中

正如我们在文章开头提到的,我们希望把测试数据保存成格式化的形式,以便对这些数据进行维护和复用。表 1展示了用一个 Excel 文件 “ MockInitialHeapSize_rating.xls ” 保存所有的测试数据的例子。 这个文件完全可以用于功能测试的文档编写。

表 1. JVM initial heap size 测试数据

setInitialValuemockSetOverallMemorymockSetMemoryPoolSizeresult31929253112312353112412453295955321271273321281283471401405471871873471881883481431435481911913481921921491461465491951953491961961102430713071510244095409531024409640961102530743074510254009400931025410041003153746104610515376147614751537614861485

表 1中,每一行都代表了一组测试数据,包括输入参数和期望结果。三个输入参数“initialHeapSize”,“currentMemoryPoolSize”,“overallMemoryOnPartition”分别保存到了三列中:“setInitialValue”,“mockSetOverallMemory ”和“mockSetMemoryPoolSize”。期望结果保存到了“result”列 ,测试代码将从这个文件中获取测试数据。

配置文件的格式是可以变化的,只需要提供相应的 SettersMap 和 SettersMapFactory 类就可以了。参看下文。

编写 SettersMap 类

有了配置文件,我们需要编写代码从配置文件中读取测试数据。我们用一个接口类“SettersMap”来代表一个配置文件。参见图 1。附件“rule_test.zip”中的 BaseSettersMap.java 是 SettersMap 接口的一个实现。

图 1. SettersMap.java

10d421f1f20e5510863c7b28207c22f2.png

我们提供了一个工厂接口 SettersMapFactory 来构造 SettersMap 。这里采用了抽象工厂(Abstract Factory)的设计模式。

清单 3. SettersMapFactory.java

/* * Created on 2008-3-13 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package attributetest.binding.spi;  import java.io.File;  /** * @author jsl * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */ public interface SettersMapFactory {   /**  *  * @return Factory 的名字 */  String getName();   /**  * 从配置文件创建 SettersMap ; * @param file 配置文件对应的 File 对象; * @return 根据配置文件创建的 SettersMap  */  SettersMap createSettersMap(File file);   /**  *  * @return 配置文件的扩展名,如 ".xls
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值