软件测试
___________________________________________________________________________
Unit Test是由程序员本身来编写的。
以下介绍Junit单元测试框架:官网地址www.junit.org
JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。
Junit3.x
___________________________________________________________________________
Junit3.x中使用包junit.framework.*
1. 必须继承TestCase类
public class CalculatorTest extends TestCase {
private Calculator cal;
public CalculatorTest() {
}
public CalculatorTest(String name) {
super(name);
}
public void setUp() {
System.out.println("...........setUp..............");
cal = new Calculator();
}
public void testAdd() {
int result = cal.add(1, 2);
Assert.assertEquals("计算添加失败", 3, result);
}
public void tearDown() {
System.out.println("........tearDown........");
}
}
2. 测试用例(Test Case)是单元测试的一个非常重要的方面。
3. 单元测试主要是用来判断程序的执行结果与自己期望的结果是否一致。
4. 在Junit3.x中,测试方法规则定义如下:
1) public
2) void
3) 无参数的
4) 测试方法名以test开头
5. Test Case之间一定要保持完全的独立性,不允许出现任何的依赖关系。
6. 我们不能依赖于测试方法的执行顺序。
7. 关于setUp与tearDown方法的执行顺序:
1) setUp
2) testAdd
3) tearDown
8. Junit两种类型错误,Failure 和 Error
Failure:指预期结果与实际结果不同,例如当你使用assertEquals或者assertXXX方法断言失败时,或者调用fail方法,就会报出Failure,这时要检查测试方法逻辑设计是否有误。
public void testDevide() {
System.out.println(".........testDevide()........");
int expected = 0;
int actual = 0;
try {
actual = cal.devide(1, 3);
} catch (Exception e) {
Assert.fail("测试失败"); //不应该执行这段.
}
Assert.assertEquals(expected, actual);
}
Error:指程序在断言执行之前,程序就因为某种错误而引发异常,导致程序终止,例如测试方法中因抛出某个异常,使得测试方法无法正确执行到断言就结束,这时你要检查测试的方法是否有未考虑到的情况而引起流程突然中断。
也就是说代码中抛出了异常等影响代码正常执行的情况,比如ArrayIndexOfBoundsException、NullPointException,也可能是磁盘已满、网络中断等等外部环境失败所带来的影响。
首先处理Error,然后在处理Failure.
9. 运行测试用例
1) IDE中,如Eclipse工具已经内置了Junit,所以可以直接在测试类中鼠标右键Run--Junit Test运行。
2) 使用junit.textui.TestRunner类运行测试类.
public static void main(String[] args) {
junit.textui.TestRunner.run(MyStackTest.class);
junit.textui.TestRunner.run(new CalculatorTest("testAdd"));
}
3) 使用TestSuite
a) 一次可以运行多个测试类进行测试
public class TestAll {
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(OOOTest.class);
suite.addTestSuite(XXXTest.class);
suite.addTestSuite(YYYTest.class);
return suite;
}
public static void main(String[] args) {
TestRunner.run(suite());
}
}
b) 通过IDE自动发现suite()方法,必须继承TestCase
public class TestAll extends TestCase {
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(OOOTest.class);
suite.addTestSuite(XXXTest.class);
suite.addTestSuite(YYYTest.class);
return suite;
}
}
c) 组合模式,组合方式多元化
……
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(new XXXTest("testABC")); // 执行testABC()方法
suite.addTest(YYYTest.suite()); // suite() 传回TestSuite实例
suite.addTestSuite(OOOTest.class);//自动查找OOOTest类中testXXX方法
return suite;
}
10.测试之前是什么状态,在测试执行完成后就应该是什么状态,而不应该由于测试执行的原因到导致了状态发生了变化。
Junit4.x
___________________________________________________________________________
Junit4.x开始支持Annotation注解技术,在编写测试用例时简化不少动作.
Junit4.x中使用的包org.junit.*
Junit4.x是兼容以前版本
Eclipse中自带了Junit4,版本为junit4.3.1. BC-EC工程中使用的版本为Junit4.4,目前最新版本4.11
1. 无需继承TestCase类,所有被@Test注解所修饰的public,void,无参数的方法都是测试用例,Junit自动查找注解方法并执行测试。
@Test
public void testAdd() {
int result = cal.add(1, 2);
Assert.assertEquals("计算添加失败", 3, result);
}
2. 虽然Junit4.x中测试类中的方法名称可以随便取,但是建议跟junit3.x中测试类方法命名约定一致,统一方法名以test开头。
3. 使用@Before注解所修饰的方法同junit3.x中的setUp方法的功能,使用@After注解所修改的方法同junit3.x测试类中的tearDown方法的功能。 @Before和@After可以在多次指定.
@Before
public void init() {
System.out.println("...........setUp..............");
cal = new Calculator();
}
@After
public void destroy() {
System.out.println("........tearDown........");
}
4. 通过@BeforeClass和@AfterClass注解标注public,static,void,无参数的类方法。在所有测试方法执行之前和之后执行。
@BeforeClass
public static void setUpBeforeClass() {
...
}
@AfterClass
public static void tearDownAfterClass() {
...
}
5. 使用@Ignore注解所修饰的方法(可以表示尚未编写完该用例或者想禁用该用例),运行器会忽略该方法的测试;当修饰类时,运行器会忽略掉所有测试方法。
@Test
@Ignore("尚未完成")
public void testMultiply() {
… …
}
Eclipse中Junit执行结果中会提示如下:
6. 预期异常:
也可以使用在junit3.x中提到的fail()来测试预期抛出异常的情况。
public void testDevideByZero() {
Throwable tx = null;
try {
cal.devide(1, 0);
Assert.fail("应该按预期抛出异常,测试失败");
} catch (Exception e) {
tx = e;
}
Assert.assertNotNull(tx.getMessage());
Assert.assertEquals(ArithmeticException.class, tx.getClass());
Assert.assertEquals("除数不能为0!", tx.getMessage());
}
junit4中使用Test中的expected属性达到相同的功能,代码量小很多.
@Test(expected = ArithmeticException.class)
public void testDevideByZero() throws Exception {
cal.devide(1, 0); // 应该抛出异常
}
7. 使用@Test(timeout = 2000) 注解预期某些操作应该在指定时间内完成,否则测试失败。 单位是毫秒。
8. 测试运行器:可以使用@RunWith注解使用的runner.
Junit4中内置的运行器有:
a) 附带兼容junit3.x运行器
org.junit.internal.runners.Junit38ClassRunner
b) 参数化运行器,可设定一组参数,每次运行测试时自动在指定位置给予不同的参数。
org.junit.runners.Parameterized
c) Suite运行器,如同Junit3.x中的TestSuite, 用于任意组合测试.
org.junit.runner.Suite
9. 参数化运行器:
a) 使用注解@RunWith(value = Parameterized.class) 指定参数化运行器,
b) 定义好一个方法,返回一组参数数据,使用注解@Parameterized.Parameters
c) 测试类构造方法中为各个参数赋值(构造方法是由Junit调用的)
d) 方法必须是public,static,void,no-arg,返回一个Collection。
e) 方法中每个元素必须是一个一维数组,数组中第一个为预期值,之后参数一,参数二等。
@RunWith(value = Parameterized.class)
public class ParamCalculatorTest {
private Calculator cal;
private int expected;
private int para1;
private int para2;
@Parameterized.Parameters
public static Collection<Integer[]> getParamData() {
Integer[][] data = new Integer[][] { { 5, 3, 2 }, { 3, 1, 2 }, { 2, 1, 1 } };
return Arrays.asList(data);
}
@Before
public void init() {
cal = new Calculator();
}
public ParamCalculatorTest(int expected, int para1, int para2) {
this.expected = expected;
this.para1 = para1;
this.para2 = para2;
}
@Test
public void testAdd() {
int result = cal.add(para1, para2);
Assert.assertEquals(expected, result);
}
@After
public void destory() {
}
}
10.Suite运行器:在Junit4中,如果想同时运行多个测试,需要使用两个注解:
@RunWith(value = Suite.class)
@SuiteClasses
使用以上两个注解会通过Suite运行器来执行测试,在SuiteClasses中指定测试类,也可以继续指定Suite,这样Junit会在去查找里面的测试类并执行。
@RunWith(value = Suite.class)
@SuiteClasses( { CalculatorTest.class, MyStackTest.class })
public class SuiteCalculatorMyStackTest {
… …
}