目录
1、 JUnit 是一个开源的 Java 语言的单元测试框架
二、使用@BeforeCss 和@ AfterClass 静态方法(过程)
三、@ Before 、@ After 、@ BeforeClass 、@ AfterClass 使用场景
一、什么是单元测试
- 单元测试是针对最小的功能单元编写测试代码- Java 程序最小的功能单元是方法
- 单元测试就是针对单个 Java 方法的测试
二、为什么要使用单元测试
1、使用main方法测试的缺点
- 只能有一个 main ()方法,不能把测试代码分离;
- 没有打印出测试结果和希望结果;
- 需要一种测试框架,帮我们编写测试。
2、单元测试的好处
- 确保单个方法运行正常;
- 如果修改了代码,只需要确保其对应的单元测试通过。
- 测试代码本身就可以作为实例代码;
- 可以自动化运行所有测试并获得报告。
三、 JUnit 单元测试框架介绍
1、 JUnit 是一个开源的 Java 语言的单元测试框架
- 专门针对 Java 语言设计,使用广泛;
- JUnit 是事实上的标准单元测试框架。
2、 JUnit 的特点
- 使用断言( Assertion )测试期望结果;﹣可以方便地组织和运行测试
- 可以方便地查看测试结果
- 常用 IDE (例如 IntelliJ ldea 、 eclipse )都集成了 JUnit
- 可以方便地集成到 Maven 。
四、JUnit 设计
- TestCase :一个 TestCase 表示一个测试。
- TestSuite :一个 TestSuite 包含一组 TestCase ,表示一组测试。
- TestFixture :一个 TestFixture 表示一个测试环境。
- TestResult :用于收集测试结果。
- TestRunner :用于运行测试。
- Testlistener :用于监听测试过程,手机测试数据;
- Assert :用于断言测试结果是否正确。
1、Assert 断言常用方法:
- 断言相等: assertEquals (100, X );
- 断言数组相等: assertArrayEquals ((1,2,3, X );
- 浮点数断言相等:0 assertEquals (3.1416, X , 0.0001);
- 断言为 nul:assertNull ( X );
- 断言为 true / false:assertTrue ( x >0 ) / assertFalse ( x < 0 )
- 其它:assertNotEquals / assertNotNull
五、使用JUnit测试方法
1、示例程序
public class Demo01 { public String sum(String d,int...arg){ int ss=Integer.parseInt(d.trim()); int sum=0; for(int i=0;i<arg.length;i++){ sum=sum+arg[i]; } return sum+""+ss; } }
对于以上这个用于测试的程序而言,我们要测试它,最土的方法就是使用main函数,如下图所示:
下面我们讨论一下使用JUnit进行测试的方法:(使用eclipse)
1、首先要建立一个JUnit的测试包
2、建立测试项目:
3、选择存放目录和确定文件名
4、选择要进行测试的方法
5、使用断言和更改测试的方法的参数
6、运行结果
7、使用多个@Test测试
@Test public void testSum() { assertEquals("125", new Demo01().sum("5", 2,4,6)); assertEquals("127", new Demo01().sum("7", 2,4,6)); } @Test public void test02() { assertEquals("127", new Demo01().sum(" 7", 2,4,6)); }
8、修改主方法
package com.dw.service public class Demo01 { public String sum(String d, int...arg){ int ss = Integer.parseInt(d.trim()); int sum = 0; for(int i = 0; i < arg.length; i++){ sum = sum + arg[i]; } return sum + "" + ss; } }
六、总结
- 一个 TestCase 包含一组相关的测试方法;
- 使用断言测试结果(注意浮点数 assertEquals 要指定 delta )
- 每个测试方法必须完全独立;
- 测试代码必须非常简单;
- 不能为测试代码再编写测试
- 测试需要覆盖各种输入条件,特别是边界条件。
JUnit 单元测试实战
一、使用@Before和@After
同一个单元测试内的多个测试方法:测试前都需要初始化某些对象,测试后可能需要清理资源; Junit 使用@Before和@After可以实现,在@Before方法中初始化测试资源,在@After方法中释放测试资源。
JUnit 对每个@Test 方法:(执行过程)
- 实例化测试用例;
- 执行@ Before 方法;
- 执行@ Test 方法;
- 执行@ After 方法。
注意:使用@Before 和@After 可以保证:
- 单个@Test 方法执行前会创建新的 XxxTest 实例;实例变量的状态不会传递给下一个@ Test 方法。
- 单个@Test 方法执行前后会执行@ Before和@ After方法。
示例代码
import org.junit.After; import org.junit.Before; import org.junit.Test; public class demoTest { public DemoTest() { System.out.println("Demotest对象创建"); } @Before public void before() { System.out.println("beforeTest方法执行"); } @Test public void test01() { System.out.println("test01方法执行......"); } @Test public void test02() { System.out.println("test02方法执行......"); } @Test public void test03() { System.out.println("test03方法执行......"); } @After public void after() { System.out.println("after方法执行......"); } }
运行结果:
我们可以用@Before创建对象demo,那么每一次@test的执行都会执行@Before下的对象的创建。每一个test都是一个独立的单元,然后再使用@After销毁对象,释放资源。
二、使用@BeforeCss 和@AfterClass 静态方法(过程)
- 在执行所有@Test 方法前执行@Beforedass 标注的静态方法;
- 执行所有测试
- 在执行所有@Test 方法后执行@AfterClass 静态方法。
注意:@BeforeClass 静态方法初始化的对象只能存放在静态字段中,静态字段的状态会影
响到所有的@Test 。
示例代码
import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class DemoTest02 { public DemoTest02() { System.out.println("Demotest对象创建"); } @BeforeClass public static void beforeClass() { System.out.println("beforeClass方法执行"); } @Test public void test01() { System.out.println("test01方法执行......"); } @Test public void test02() { System.out.println("test02方法执行......"); } @Test public void test03() { System.out.println("test03方法执行......"); } @AfterClass public static void afterclass() { System.out.println("afterclass方法执行......"); } }
执行结果:
beforeClass方法执行 Demotest对象创建 test01方法执行...... Demotest对象创建 test02方法执行...... Demotest对象创建 test03方法执行...... afterclass方法执行......
在这个结果中,@Beforeclass和@Afterclass就只执行了一次,且@Beforeclass的执行早于@Demotest对象创建的执行。类中所有的方法或对象共享。而@Beforet和 @After是成员对象所独享的。所有@test都会共享@Beforeclass里面的内容。我们可以在@Beforeclass中申明一个对象,这个对象可以在各@Test中使用。
三、@Before 、@After 、@BeforeClass 、@AfterClass 使用场景
- @Before:初始化测试对象;例如: input = new FilelnputStream();
- @After:销毁@ Before 创建的测试对象;例如: input .Close();
- @BeforeClass:初始化非常耗时的资源,例如:创建数据库。
- @AfterClass:清理@ BeforeClass 创建的资源,例如:删除数据库。
四、总结
理解 JUnit 执行测试的生命周期
- @Before 用于初始化测试对象,测试对象以实例变量存放;
- @After 用于清理@ Before 创建的对象;
- @BeforeClass 用于初始化耗时资源,以静态变量存放;
- @AfterClass 用于清理@ BeforeClass 创建的资源。
异常测试
1、对可能抛出的异常进行测试:
异常本身是方法签名的一部分。
public static int parselnt() throws NumberFormatException
测试错误的输入是否导致特定的异常。
Integer.parselnt ( null ); Integer.parselnt (“”); Integer.parselnt (“ xyz”);
2、如何测试异常
我们可以通过 try - catch测试,但这样会写出很多这样的语句,从而使其变得复杂起
来;更好的方法就是使用 expected 测试异常,具体操作如下:
@Test( expected = NumberFormatException.class ) public void testNumberFormat (){ Integer . parseInt ( null); //如果测试过程中抛出了以上异常,则测试通过。 }
总结
- 测试异常可以使用@Test ( expected = Exception. class )
- 对可能发生的每种类型的异常进行测试。
示例程序一
public class Demo03 { public void testNumberFormat(String num){ Integer.parseInt(num); } public void testExtends() { Object msg=null; System.out.println(msg.toString()); } }
测试程序二
import static org.junit.jupiter.api.Assertions.*; import org.junit.Before; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; import org.junit.Before; import org.junit.jupiter.api.Test; public class Demo03Test { Demo03 d2; @Before public void before() { d2=new Demo03(); } @Test( expected = NumberFormatException.class ) public void testTestNumberFormat() { d2.testNumberFormat("abc"); } //如果出现运行通过,表示出现异常。如果输入12,则运行不通过,表示没有出现异常。 @Test( expected = NumberFormatException.class ) public void testTestExtends() { d2.testExtends(); } //空指针异常 }
超时测试
可以为 JUnit 的单个测试设置超时
超时设置为1秒:@Test ( timeout =1000)【 timeout 单位是毫秒】
强调:如果测试方法执行时间小于超时设置的时间,测试通过,反之不通过。
注意:超时测试不能取代性能测试和压力测试。
程序示例一
public class Demo04 { public int culate() { int b=100; int sum=0; for(int i=1;i<=100;i++) { sum=sum+i; } return sum; } public int culate02(){ int b=500; int sum=0; for(int i=1;i<=100;i++) { sum=sum+i; } try { Thread.sleep(1000); } catch(InterruptedException e) { //TODD Auto-generated catch block e.printStackTrace(); } return sum; } }
测试程序二
import static org.junit.jupiter.api.Assertions.*; import org.junit.Before; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class Demo04Test { Demo04 d4; @Before public void before() { d4=new Demo04(); } @Test @Timeout(600) public void testCulate(){ d4.culate(); } @Test @Timeout(600) public void testCulate02() { d4.culate02(); } }
参数化测试
1、参数化测试
如果待测试的输入和输出是一组数据,可以把测试数据组织起来,用不同的测试数据调用相同的测试方法。
2、参数化测试要求
- 参数必须有静态方法 data()返回,返回类型为 Collection <?> ,静态方法必须标记为
- @Parameters ;
- 测试类必须标记为:@RunWith ( Parameterized.class );
- 构造方法参数必须和测试参数对应。
示例程序
@RunWith(Parameterized.class)/ public class Demo05Test{ @Parameters public static Collection<?> data() { return Arrays.asList(new Object[][]{{0,0},{1,1}, {-1,1}}); } //这是一种固定的结构,我们要做的就是改变其中的数组的参数值。其它的部分//都不能改变 int input; int expected; // 参数必须要对应; public Demo05Test(int input, int expected) { this.input = input; this.expected = expected; } @Test public void Test() { int r=Math.abs(this.input);//绝对值 assertEquals(this.expected, r); } //对应这三组数据会执行三次 }