●JUnit 的框架
到目前为止,我们只是介绍了断言本身,很显然我们不能只是简单的把断言方法写完,就希望测试可以运行起来。我们需要一个框架来辅助完成这些,那么我们就要做多一些工作了。很幸运的是,我们不用多做太多。
在 JUnit 4 提供了@Before和@After,在每个测试函数调用之前/后都会调用。
@Before: Method annotated with @Before executes before every test. 每个测试方法开始前执行的方法
@After: Method annotated with @After executes after every test. 每个测试方法执行后再执行的方法
如果在测试之前有些工作我们只想做一次,用不着每个函数之前都做一次。比如读一个很大的文件。那就用下面两个来标注:
@BeforeClass: 测试类初始化的时候,执行的方法
@AfterClass: 测试类销毁的时候,执行的方法
注意:
1.@Before/@After 可以执行多次; @BeforeClass/@AfterClass 只能执行一次
2.如果我们预计有Exception,那就给@Test加参数:@Test(expected = XXXException.class)
3.如果出现死循环怎么办?这时timeout参数就有用了:@Test(timeout = 1000)
4.如果我们暂时不用测试一个用例,我们不需要删除或都注释掉。只要改成:@Ignore ,你也可以说明一下原因@Ignore("something happens")
示例代码:下面的代码代表了单元测试用例的基本框架
public class JUnitDemoTest { @Before public void setUp(){ //TODO: 测试预置条件,测试安装 } @After public void tearDown(){ //TODO: 测试清理,测试卸载 } @Test public void test01(){ //TODO: test01 脚本 } @Test public void test02(){ //TODO: test02 脚本 } @Test public void test03(){ //TODO: test03 脚本 } } |
单元测试框架的过程如下:
测试过程.png
JUnit 需要注意的事项:
1.每个 @Test 都是一个测试用例,一个类可以写多个 @Test
2.每个 @Test 执行之前 都会执行 @Before,执行之后都会运行 @After
3.每个 @Test,@After,@Before 都必须是 public void, 参数为空
4.@After / @Before 也可以是多个,并且有执行顺序。在每个 @Test 前后执行多次。
@Before 多个名字长度一致,z -> a, 长度不一致,会先执行名字短的。
@After / @Test 多个名字长度一致,a -> z, 长度不一致,会后执行名字短的。
5.@AfterClass / @BeforeClass 也可以是多个,并且有执行顺序。只会在测试类的实例化前后各执行一次。
@BeforeClass 多个名字长度一致,z -> a, 长度不一致,会先执行名字短的。
@AfterClass 多个名字长度一致,a -> z, 长度不一致,会后执行名字短的。
@AfterClass / @BeforeClass 都必须是 public static void, 参数为空
测试结果有 通过、不通过和错误 三种。
●JUnit 的测试运行
这一小节,我们来介绍一下 JUnit 4 中的新的测试运行器(Test Runner)。如果我们刚开始编写测试,那么我们需要尽可能快捷的运行这些测试,这样我们才能够将测试融合到开发循环中去。
编码 → 运行 → 测试 → 编码……
其中,JUnit 就可以让我们构建和运行测试。我们可以按照组合测试Suite 以及参数化测试分别来运行测试。
●组合测试Suite
测试集 (Suite 或者 test suite)一组测试。测试集是一种把多个相关测试归入一组的便捷测试方式。可以在一个测试集中,定义需要打包测试的类,并一次性运行所有包含的测试;也可以分别定义多个测试集,然后在一个主测试集中运行多个相关的测试集,打包相关的测试的类,并一次性运行所有包含的测试。
示例代码如下:
@RunWith(value = Suite.class) @Suite.SuiteClasses(value = HelloWorldTests.class) public class HelloWorldTestRunner { } |
●参数化测试
参数化测试(Parameterized)是测试运行器允许使用不同的参数多次运行同一个测试。参数化测试的代码如下:
@RunWith(value = Parameterized.class) public class ParameterizedHelloWorldTests { @Parameterized.Parameters public static Collection getTestParameters() { int[] listToTest1 = {10, 80, 100, -96}; int[] listToTest2 = {-10, -80, -100, -6}; int[] listToTest3 = {10, -80, -100, -96}; int[] listToTest4 = {10, -80, 100, -96}; int[] listToTest5 = {10, 80, -100, -96}; return Arrays.asList(new Object[][]{ {100, listToTest1}, {-6, listToTest2}, {10, listToTest3}, {100, listToTest4}, {80, listToTest5}}); } @Parameterized.Parameter public int expected; @Parameterized.Parameter(value = 1) public int[] listToTest; @Test public void testGetLargestElementByParameters() { Assert.assertEquals("获取最大元素错误!", expected, new HelloWorld().getLargestElement(listToTest)); } } |
对于参数化测试的运行器来运行测试类,那么必须满足以下要求:
1.测试类必须使用@RunWith(value = Parameterized.class)注解
2.必须声明测试中所使用的实例变量
3提供一个用@Parameterized.Parameters的注解方法,这里用的是getTestParameters(),同时此方法的签名必须是public static Collection
4.为测试指定构造方法,或者一个个全局变量的成员进行赋值
5.所有的测试方法以@Test注解,实例化被测试的程序,同时在断言中使用我们提供的全局变量作为参数
●1.4 [探讨]按业务价值导向进行单元测试设计
练习:测试的结果是否正确
如果测试代码能够运行正确,我们要怎么才能知道它是正确的呢?
如何应对测试数据量比较大的时候,我们的测试代码如何编写?
练习:测试的边界条件
寻找边界条件是单元测试中最有价值的工作之一,一般来说Bug出现在边界上的概率比较大。那么我们都需要考虑什么样的边界条件呢?
练习:强制产生错误条件
关于产生错误的条件,请列出一个详细的清单来。
分析:测试作为设计工具
第一节【专题】中,我们有讨论设计潜力的曲线,其中第二条方案强调了测试作为设计的工具。那么我们想就两个方面来讨论这个测试设计的问题。
TDD,测试驱动开发
BDD,行为驱动开发
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。