java单元测试JUnit框架原理与用法实例教程
本篇章节讲解java单元测试JUnit框架原理与用法。分享给大家供大家参考,具体如下: 1 简介 JUnit是一个Java语言的单元测试框架,它由 Kent Beck 和 Erich Gamma 建立,逐渐成为 xUnit 家族中最为成功的一个。 JUnit有它自己的JUnit扩展生态圈,多数Java的开发环境都已经集成了JUnit作为单元测试的工具。在这里,一个单元可以是一个方法、类、包或者子系统。因此,单元测试是指对代码中的最小可测试单元进行检查和验证,以便确保它们正常工作。例如,我们可以给予一定的输入测试输出是否是所希望得到的结果。在本篇博客中,作者将着重介绍 JUnit 4.X 版本的特性,这也是我们在日常开发中使用最多的版本。 2 特点 JUnit提供了注释以及确定的测试方法; 3 内容 3.1 注解 @Test :该注释表示,用其附着的公共无效方法(即用public修饰的void类型的方法 )可以作为一个测试用例; /** * JUnit 注解示例 */ @Test public void testYeepay(){ Syetem.out.println("用@Test标示测试方法!"); } @AfterClass public static void paylus(){ Syetem.out.println("用@AfterClass标示的方法在测试用例类执行完之后!"); } 3.2 断言 在这里,作者将介绍一些断言方法,所有这些方法都来自 org.junit.Assert 类,其扩展了 java.lang.Object 类并为它们提供编写测试,以便检测故障。简而言之,我们就是通过断言方法来判断实际结果与我们预期的结果是否相同,如果相同,则测试成功,反之,则测试失败。 void assertEquals([String message],expected value,actual value) :断言两个值相等,值的类型可以为int、short、long、byte、char 或者 4 JUnit 3.X 和 JUnit 4.X 的区别 4.1 JUnit 3.X (1)使用 JUnit 3.X 版本进行单元测试时,测试类必须要继承于 TestCase 父类; ① public的; (3)不同的测试用例之间一定要保持完全的独立性,不能有任何的关联; /** * 用 JUnit 3.X 进行测试 */ import junit.framework.Assert; import junit.framework.TestCase; public class TestOperation extends TestCase { private Operation operation; public TestOperation(String name) { // 构造函数 super(name); } @Override public void setUp() throws Exception { // 在每个测试方法执行 [之前] 都会被调用,多用于初始化 System.out.println("欢迎使用Junit进行单元测试..."); operation = new Operation(); } @Override public void tearDown() throws Exception { // 在每个测试方法执行 [之后] 都会被调用,多用于释放资源 System.out.println("Junit单元测试结束..."); } public void testDivideByZero() { Throwable te = null; try { operation.divide(6,0); Assert.fail("测试失败"); //断言失败 } catch (Exception e) { e.printStackTrace(); te = e; } Assert.assertEquals(Exception.class,te.getClass()); Assert.assertEquals("除数不能为 0 ",te.getMessage()); } } 4.2 JUnit 4.X (1)使用 JUnit 4.X 版本进行单元测试时,不用测试类继承TestCase父类; @Before 注解:与JUnit 3.X 中的 setUp() 方法功能一样,在每个测试方法之前执行,多用于初始化; 此外,我们可以通过阅读上面的第二部分“2 注解”了解更多的注解。 /** * 用 JUnit 4.X 进行测试 */ import static org.junit.Assert.*; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class TestOperation { private Operation operation; @BeforeClass public static void globalInit() { // 在所有方法执行之前执行 System.out.println("@BeforeClass标注的方法,在所有方法执行之前执行..."); } @AfterClass public static void globalDestory() { // 在所有方法执行之后执行 System.out.println("@AfterClass标注的方法,在所有方法执行之后执行..."); } @Before public void setUp() { // 在每个测试方法之前执行 System.out.println("@Before标注的方法,在每个测试方法之前执行..."); operation = new Operation(); } @After public void tearDown() { // 在每个测试方法之后执行 System.out.println("@After标注的方法,在每个测试方法之后执行..."); } @Test(timeout=600) public void testAdd() { // 设置限定测试方法的运行时间 如果超出则返回错误 System.out.println("测试 add 方法..."); int result = operation.add(2,3); assertEquals(5,result); } @Test public void testSubtract() { System.out.println("测试 subtract 方法..."); int result = operation.subtract(1,2); assertEquals(-1,result); } @Test public void testMultiply() { System.out.println("测试 multiply 方法..."); int result = operation.multiply(2,3); assertEquals(6,result); } @Test public void testDivide() { System.out.println("测试 divide 方法..."); int result = 0; try { result = operation.divide(6,2); } catch (Exception e) { fail(); } assertEquals(3,result); } @Test(expected = Exception.class) public void testDivideAgain() throws Exception { System.out.println("测试 divide 方法,除数为 0 的情况..."); operation.divide(6,0); fail("test Error"); } public static void main(String[] args) { } } 4.3 特别提醒 通过以上两个例子,我们已经可以大致知道 JUnit 3.X 和 JUnit 4.X 两个版本的区别啦!首先,如果我们使用 JUnit 3.X,那么在我们写的测试类的时候,一定要继承 TestCase 类,但是如果我们使用 JUnit 4.X,则不需继承 TestCase 类,直接使用注解就可以啦!在 JUnit 3.X 中,还强制要求测试方法的命名为“ testXxxx ”这种格式;在 JUnit 4.X 中,则不要求测试方法的命名格式,但作者还是建议测试方法统一命名为“ testXxxx ”这种格式,简洁明了。 此外,在上面的两个示例中,我们只给出了测试类,但是在这之前,还应该有一个被测试类,也就是我们真正要实现功能的类。现在,作者将给出上面示例中被测试的类,即 Operation 类: /** * 定义了加减乘除的法则 */ public class Operation { public static void main(String[] args) { System.out.println("a + b = " + add(1,2)); System.out.println("a - b = " + subtract(1,2)); System.out.println("a * b = " + multiply(1,2)); System.out.println("a / b = " + divide(4,2)); System.out.println("a / b = " + divide(1,0)); } public static int add(int a,int b) { return a + b; } public static int subtract(int a,int b) { return a - b; } public static int multiply(int a,int b) { return a * b; } public static int divide(int a,int b) { return a / b; } } 5 测试示例 5.1 示例一:简单的 JUnit 3.X 测试 import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import java.util.ArrayList; import java.util.Collection; /** * 1、创建一个测试类,继承TestCase类 */ public class SimpleTestDemo extends TestCase { public SimpleTestDemo(String name) { super(name); } /** * 2、写一个测试方法,断言期望的结果 */ public void testEmptyCollection(){ Collection collection = new ArrayList(); assertTrue(collection.isEmpty()); } /** * 3、写一个suite()方法,它会使用反射动态的创建一个包含所有的testXxxx方法的测试套件 */ public static Test suit(){ return new TestSuite(SimpleTestDemo.class); } /** * 4、写一个main()方法,以文本运行器的方式方便的运行测试 */ public static void main(String[] args) { junit.textui.TestRunner.run(suit()); } } 5.2 示例二:套件测试 首先,介绍一下套件测试,简单来讲,测试套件是指:一些测试不同类的用例,可以使用 @RunWith 和 @Suite 注解把所有的测试类套在一起,从而形成测试套件。如果有很多测试类,想让它们都运行在同一时间,而不是单一地运行每个测试,套件测试是非常有用的。当一个类被注解为 @RunWith, JUnit 将调用其中的注解,以便运行测试类,而不使用内置的 JUnit 运行方法。 /** * 待测试类 */ import java.util.Arrays; public class GotoWork { public String[] prepareSkills() { String[] skill = { "Java","MySQL","JSP" }; System.out.println("My skills include : " + Arrays.toString(skill)); return skill; } public String[] addSkills() { String[] skill = { "Java","JSP","JUnit" }; System.out.println("Look,my skills include : " + Arrays.toString(skill)); return skill; } } /** * 测试类 1 */ import org.junit.Test; import static org.junit.Assert.*; public class PrepareSkillsTest { GotoWork gotoWork = new GotoWork(); String[] skill = { "Java","JSP" }; @Test public void testPrepareSkills() { System.out.println("Inside testPrepareSkills()"); assertArrayEquals(skill,gotoWork.prepareSkills()); } } /** * 测试类 2 */ import org.junit.Test; import static org.junit.Assert.*; public class AddSkillsTest { GotoWork gotoWork = new GotoWork(); String[] skill = { "Java","JUnit" }; @Test public void testAddSkills() { System.out.println("Inside testAddPencils()"); assertArrayEquals(skill,gotoWork.addSkills()); } } /** * 套件测试 */ import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ PrepareSkillsTest.class,AddSkillsTest.class }) public class SuitTest { } 使用 @Suite.SuiteClasses 注解,可以定义测试类,将被列入执行,并且执行的顺序就是在 @Suite.SuiteClasses 注解中定义的顺序。 5.3 示例三:参数化测试 首先介绍一下参数化测试,一个测试类可以被看作是一个参数化测试类,当其满足下列所有要求: ① 该类被注解为 @RunWith(Parameterized.class); /** * 待测试类 */ public class Calculate { public int sum(int var1,int var2) { System.out.println("此方法的参数值分别为 : " + var1 + " + " + var2); return var1 + var2; } } /** * 参数化测试类 */ import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class CalculateTest { private int expected; private int first; private int second; public CalculateTest(int expectedResult,int firstNumber,int secondNumber) { this.expected = expectedResult; this.first = firstNumber; this.second = secondNumber; } @Parameters public static Collection addedNumbers() { return Arrays.asList(new Integer[][] { { 3,1,2 },{ 5,2,3 },{ 7,3,4 },{ 9,4,5 },}); } @Test public void testSum() { Calculate add = new Calculate(); System.out.println("Addition with parameters : " + first + " and " + second); assertEquals(expected,add.sum(first,second)); } } 观察 CalculateTest 类,它满足上述所有的要求,因此它就可以称为一个参数化测试类。addedNumbers 方法使用注释 @Parameters 返回数组的集合,每个数组包括每个测试执行输入和输出数字,每个数组中的元素数必须相同好与构造参数的个数相匹配。所以,在这种特定的情况下,每个数组包括三个元素,即表示要加入的两个元素和一个结果元素。 6 个人建议 有些童鞋可能会有一些误解,认为写测试代码没有用,而且还会增大自己的压力,浪费时间。但事实上,写测试代码与否,还是有很大区别的,如果是在小的项目中,或许这种区别还不太明显,但如果在大型项目中,一旦出现错误或异常,用人力去排查的话,那将会浪费很多时间,而且还不一定排查的出来,但是如果用测试代码的话,JUnit 就是自动帮我们判断一些代码的结果正确与否,从而节省的时间将会远远超过你写测试代码的时间。 因此,个人建议:要养成编写测试代码的习惯,码一点、测一点;再码一点,再测一点,如此循环。在我们不断编写与测试代码的过程中,我们将会对类的行为有一个更为深入的了解,从而可以有效的提高我们的工作效率。下面,作者就给出一些具体的编写测试代码的技巧和较好的实践方法: 1. 不要用 TestCase 的构造函数初始化 Fixture,而要用 setUp() 和 tearDown() 方法; 事实上,在 Junit 中使用 try catch 来捕获异常是没有必要的,因为 Junit 会自动捕获异常,那些没有被捕获的异常就会被当成错误处理啦! 更多关于java相关内容感兴趣的读者可查看本站专题:《Java数据结构与算法教程》、《Java字符与字符串操作技巧总结》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》 希望本文所述对大家java程序设计有所帮助。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |