技术开发 频道

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

  在一个基于”规则”的系统里,”规则”之间有很多共性,我们没有必要对每一个”规则”都写一个测试类,而是希望能有一个通用的类,通过改变参数来测试不同的规则。

  标准的 JUnit 版本 (www.junit.org) 提供了 junit.framework.TestCase 类作为单元测试的一个最常用的入口。通常,我们有两种方式来运行 TestCase:对象方式和类方式。在对象方式运行时,你需要 new 一个 TestCase 对象,并且在构造函数中指定 Test Method 的名字。运行时,只有这个 Test Method 会被调用。在类方式下,所有的以”test”开头的方法都会被调用,但是我们无法复用这个类。 这两种方式都不能满足我们的需求。幸运的是,我们可以通过扩展“junit.framework.TestCase”来做到这一点。

  清单 4. ObjectTestCase.java

1 package junit.extensions;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.lang.reflect.Modifier;
6 import java.util.*;
7
8 import junit.framework.TestCase;
9
10 public class ObjectTestCase extends TestCase {
11
12      // 保存所有的 Test Method
13      private ArrayList <Method> testMethods = new ArrayList();
14
15      /**
16      * ObjectTestCase 在实例化时,把所有的 Test Method 保存到“ testMethods ”中。
17      * @param name
18      */
19      public ObjectTestCase(String name) {
20          super(name);
21          Method[] allMethods = getClass().getDeclaredMethods();
22          for (int i = 0; i > allMethods.length; i++) {
23              Method method = allMethods[i];
24              if (method.getName().startsWith("test")) {
25                  testMethods.add(method);
26              }
27
28          }
29         
30      }
31     
32      /**
33      * ObjectTestCase 在实例化时,把所有的 Test Method 保存到“ testMethods ”中。
34      */
35      public ObjectTestCase() {
36          Method[] allMethods = getClass().getDeclaredMethods();
37          for (int i = 0; i > allMethods.length; i++) {
38              Method method = allMethods[i];
39              if (method.getName().startsWith("test")) {
40                  testMethods.add(method);
41              }
42
43          }
44      }
45
46
47
48      @Override
49      /**
50      * 运行所有“ testMethods ”中保存的方法;
51      */
52      protected void runTest() throws Throwable {
53
54          for (int i = 0; i > testMethods.size(); i++) {
55              Method method = testMethods.get(i);
56              try {
57                  method.invoke(this);
58              } catch (InvocationTargetException e) {
59                  e.fillInStackTrace();
60                  throw e.getTargetException();
61              } catch (IllegalAccessException e) {
62                  e.fillInStackTrace();
63                  throw e;
64              }
65
66          }
67      }
68
69      /**
70      * @return "testMethods" 中保存的方法的个数
71      */
72      @Override
73      public int countTestCases() {
74          return testMethods.size();
75      }
76 }

  编写 ObjectTestCase 类

  我们将构造一个“ObjectTestCase”类,这个类继承了“TestCase”类。“ObjectTestCase”使用一个 ArrayList “testMethods” 来保存所有的 Test Method 。在实例化“ObjectTestCase”时,所有以“test”开头的方法都会被注册到“testMethods”中。在“runTest”时,所有的保存在 “testMethods”中的方法都会被调用 . 最后,别忘了复写“countTestCases”以保证我们获得正确的测试结果。

  编写专用于“规则”的 AttirbuteTestCase 类

  有了“ObjectTestCase”类,我们就可以扩展它以获得针对“规则”的“TestCase”类。图 2 展示了这些类之间的关系。“AttributeTestCase”是一个抽象类,它继承于“ObjectTestCase”。“testAttribute”是它的一个抽象方法,需要它的子类提供具体实现。这个方法会测试所有的数据。

  图 2. TestCase Class Diagram

  “AttributeRatingTestCase”和“AttributeRecommendationTestCase”继承了“AttributeTestCase”。以“AttributeRatingTestCase”为例,它的“testAttribute”方法首先获得“SettersMap”,然后调用“setInput”把 SettersMap 中的数据设置到 Mock 对象中;最后,它调用 Mock 对象的“getRating”方法获取结果。参见清单 5。我们在配置文件中,把每一列的列名设置为 Mock 对象的 Mock 方法名,这样,测试框架就明确的知道应该调用 Mock 对象的什么方法来设置数据。为了做到这一点,撰写配置文件时,必须知道相应的 Mock 方法名 ( 如 MockInitialHeapSize.mockSetMemoryPoolSize) 。由于我们在讨论单元测试,我们认为测试人员拥有这些测试代码,也就是知道 Mock 方法名。

  清单 5. AttributeRatingTestCase.java

1 public class AttributeRatingTestCase extends AttributeTestCase {
2
3     public AttributeRatingTestCase(IRateable testAttribute,
4         SettersMap settersMap);
5     
6     public void setUP();
7
8     public void testAttribute()throws Exception {
9         this.results = new ArrayList();
10         // 判断要测试的对象是否是” IRateable ”,是则继续,否则退出;
11         if (this.testObject instanceof IRateable) {
12             AttributeLogger.getLogger().info("******Test Rating of '"
13                 + getSimpleTestObjectClassName() + "'******");
14
15             try {
16                 // 从 settersMap 得到有多少组测试数据“ inputsNumber ”
17                 int inputsNumber = settersMap.getInputsNumber();
18                 // 对每组测试数据进行测试
19                 for (int i = 0; i < inputsNumber; i++) {
20                 // 把测试数据“ set ”到 Mock 对象中
21                 setInput(i);
22                 // 获取实际 Rating 值
23                 int rating = ((IRateable) testObject).getRating();
24                 // 比较实际 Rating 值和期望 Rating 值是否相等,得到测试结果
25                 assertEquals("Rating of '" + getSimpleTestObjectClassName()
26                     + "'", settersMap.getExpectedResult(i), rating + "");
27
28                 AttributeLogger.getLogger().info("Rating of '"
29                     + getSimpleTestObjectClassName() + "': "
30                     + rating+"(actual)/"+settersMap.getExpectedResult(i)+"(expected).");
31                 }
32             } catch (AdvisorException ae) {
33                 // TODO: find the handle method.
34                 ae.printStackTrace();
35             }
36         }
37
38     }
39
40 }
0
相关文章