一个最基本的JUnit
测试需要三样东西:
下面是一个最简单的例子,
MessageUtil.java
可以打印用户传递给它的消息。下面的代码中包含了一条bug。系统希望printMessage()
直接打印出给入的原始信息。但这里程序员不小心多打了个Hi
.
/**
* 文件路径:/Users/Wei/JavaCode/src/com/ciaoshen/junit/MessageUtil.java
*/
package com.ciaoshen.junit;
public class MessageUtil {
private String message;
public MessageUtil(String message){
this.message = "Hi " + message; // Bug! 原先希望直接打印出message。
}
public String printMessage(){
System.out.println(message);
return message;
}
}
MessageUtilTest.java
是测试的主体。这里需要导入org.junit.Test
库,因为代码里用到了@Test
注释,它告诉JUnit哪个方法是需要执行测试方法。在JUnit 4之前,用户需要遵守一个重要的人为的约定: 所有测试方法必须以test
开头,JUnit才会运行它们。. 从JUnit 4开始,可以用 @Test
注释来标注。这是JUnit非常重要的一个特性。它实现的原理,是基于在运行时可见的注释,以及动态代理技术。这里先不展开。
另外一个org.junit.Assert.assertEquals
库属于断言系统。说白了,JUnit就是用断言来判断,实际得到的结果,是否和正确结果一致。所以断言系统在JUnit里占有很重要的位置。下面的代码中,assertEquals()
函数会验证它的两个参数(即目标结果和实际结果)是否一致。
/**
* 文件路径:/Users/Wei/JavaCode/Test/com/ciaoshen/junit/MessageUtilTest.java
*/
package com.ciaoshen.junit;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MessageUtilTest {
private String message = "Hello Ronald";
MessageUtil mu = new MessageUtil(message);
@Test
public void testPrintMessage() {
assertEquals(message, mu.printMessage());
}
}
我们当然可以自己运行测试类MessageUtilTest.java
。比如像这样,
/**
* 文件路径:/Users/Wei/JavaCode/Test/com/ciaoshen/junit/MessageUtilTest.java
*/
package com.ciaoshen.junit;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MessageUtilTest {
private String message = "Hello Ronald";
MessageUtil mu = new MessageUtil(message);
@Test
public void testPrintMessage() {
assertEquals(message, mu.printMessage());
}
public static void main(String[] args) { // 自己运行测试
MessageUtilTest test = new MessageUtilTest();
test.testPrintMessage();
}
}
正常的时候assertEquals()
的返回值是void
,所以没什么反应。一旦被测试代码出错,比如printMessage()
的输出和用户的输入不一致了,assertEquals()
函数会抛出异常org.junit.ComparisonFailure
。
MacBook-Pro-de-Wei:JavaCode Wei$ sh run.sh MessageUtil
HiHello Ronald
Exception in thread "main" org.junit.ComparisonFailure: expected:<H[]ello Ronald> but was:<H[iH]ello Ronald>
at org.junit.Assert.assertEquals(Assert.java:115)
at org.junit.Assert.assertEquals(Assert.java:144)
at com.ciaoshen.junit.MessageUtilTest.testPrintMessage(MessageUtilTest.java:15)
at com.ciaoshen.junit.MessageUtilTest.main(MessageUtilTest.java:19)
实际测试中,经常会有很多错误一起出现。我们就需要自己捕获,并收集异常,然后以比较容易阅读的方式打印出来。这些JUnit都可以帮我们做。
JUnitCore.runClasses()
函数帮我们运行主测试类,把测试结果收集到一个Result
类实例中。可以用Result#getFailures()
函数解析出异常并打印。
下面代码中的三个import
库,分别为JUnitCore
,Result
和Failure
组件。
/**
* 文件路径:/Users/Wei/JavaCode/Test/com/ciaoshen/junit/MessageUtilTestRunner.java
*/
package com.ciaoshen.junit;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
public class MessageUtilTestRunner {
public static void main(String[] args) {
//JUnitCore.runClasses()函数运行主测试类
Result result = JUnitCore.runClasses(MessageUtilTest.class); //把测试结果收集到一个`Result`类实例中
for (Failure failure : result.getFailures()) { // getFailures()函数从Result实例中解析出异常
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
Demo根目录下的文件结构如下,
把被测试代码和测试类放在同一个包,但分放在不同的文件夹下,是一个良好的实践。
.
├── bin // class文件
│ └── com
│ └── ciaoshen
│ └── leetcode
│ ├── MessageUtil.class
│ ├── MessageUtilTest.class
│ └── MessageUtilTestRunner.class // class文件可以放在一起
├── lib // JUnit库包
│ ├── hamcrest-core-1.3.jar
│ └── junit-4.12.jar
├── src // 我的正常代码存放位置
│ └── com
│ └── ciaoshen
│ └── leetcode
│ └── MessageUtil.java // 被测试代码
└── test // 测试代码和业务代码在同一个包,但被放在两个不同的文件夹下。
└── com
└── ciaoshen
└── leetcode
├── MessageUtilTest.java // 单元测试代码
└── MessageUtilTestRunner.java // 运行单元测试代码
下面是编译用的bash
文件。这样,第一个JUnit的程序就可以跑起来了。
# Base Path
BASE_DIR="/Users/Wei/JavaCode"
CLASS_PATH="$BASE_DIR/bin"
SOURCE_PATH="$BASE_DIR/src"
LIB_PATH="$BASE_DIR/lib"
TEST_PATH="$BASE_DIR/test"
# JUnit Library
JUNIT="$LIB_PATH/junit-4.12.jar"
HAMCREST="$LIB_PATH/hamcrest-core-1.3.jar"
### JUnit sub dir
JUNIT_PACK="com/ciaoshen/junit"
JUNIT_SRC="$SOURCE_PATH/$JUNIT_PACK"
JUNIT_TEST="$TEST_PATH/$JUNIT_PACK"
##################################
# Compile & Run JUnit Demo
##################################
javac -cp $CLASS_PATH:$JUNIT:$HAMCREST -d $CLASS_PATH $JUNIT_SRC/$1.java $JUNIT_TEST/$1Test.java $JUNIT_TEST/$1TestRunner.java
java -cp $CLASS_PATH:$JUNIT:$HAMCREST com.ciaoshen.junit.$1TestRunner
下面是运行结果,Runner
把结果显示地很有条理。
MacBook-Pro-de-Wei:JavaCode Wei$ sh run.sh MessageUtil
HiHello Ronald
testPrintMessage(com.ciaoshen.junit.MessageUtilTest): expected:<H[]ello Ronald> but was:<H[iH]ello Ronald>
false