• JUnit4单元测试基础篇


    引言

     JUnit 作为Java语言的测试框架,在测试驱动开发(TDD)下扮演重要的角色。众所周知,无论开发大型项目还是一般的小型项目,

     单元测试都至关重要。单元测试为软件可发测试维护提供了很大的便利。JUnit 4 作为最新版本,增添了许多新的特性,

     结合Hamcrest,可以写出很多灵活的测试。从JUnit 4 开始 jar包放在org.junit包下。代码已经托管在GitHub上。

     为了以后测试方便,自定义了一个JUnit的类库,把几个重要的jar包导在一个类库,

     这样,以后的项目工程需要写单元测试,直接导入这个类库,不需要重新导入各种jar包。

     自定义类库结构如下:

      

     关于如何自定义类库,Build Path -> Configure Build Path... -> Add Library... -> User Library... -> Next ......,

     常规的单元测试,是在工程下新建一个Source Folder和src目录下相同的包名结构,测试A类,就在相对应的包下

     创建一个测试类ATest,测试fun方法,就相应的创建测试方法testFun()。本文单纯学习JUnit特性,没有采用这种方式。 

     ok,废话到此为止,开始测试。文章略长,按需阅读。

    断言 - Assertions

     在JUnit 之前版本,经常会使用assertTrue,assertEquals,assertNull等断言,根据字面意思就很容易知道函数的用途。

     JUnit 4 结合Hamcrest推出更强大的断言AssertThat(),相比于之前的assertXXX,assertThat代码风格将变得统一,更容易维护;

     配合Hamcrest的Matchers, 可以写出很多灵活的测试,下面会提到;另一个优点更倾向于英语语法,不像"谓宾主"

     (如:assertEquals(9, x)) 语法模式拗口,assertThat使用类型"主谓宾"的语法模式(如:assertThat(x, is(9)),可能是受母语的影响,

     这条优点感触不是很深刻; 下面要说的这个优点比较实用,AssertThat测试失败会提供一些可读的描述性错误信息,

     而assertXXX不会(当然可以手动去写),assertThat会举例说明; Last but not the least(v~v高考残留的英语记忆),

     可发人员可以实现Matcher接口,自定义匹配符,这个功能很强大。

     注意:测试的时候需要引入一些类,有些类(Assert, Matchers)比较特殊,采用静态引入的方式,

     好处在于可以直接使用这些类的静态方法(assertTure, assertThat),而不用使用类名.方法名的方式。

    assertXXX 

    import static org.junit.Assert.*;
    
    import org.junit.Test;
    
    /**
     * AssertXXX测试
     * @author michael
     */
    public class AssertTests {
    
        @Test
        public void testAssertArrayEquals() {
            byte[] expected = "trial".getBytes();
            byte[] actual = "trial".getBytes();
            assertArrayEquals("failure - byte arrays not same", expected, actual);
        }
        
        @Test
        public void testEquals() {
            assertEquals("failure - strings are not equal", "text", "text");
        }
    
        @Test
        public void testAssertTrue() {
            assertTrue("failure - should be true", true);
        }
        
        @Test
        public void testFalse() {
            assertFalse("failure - should be false", false);
        }
        
        @Test
        public void testAssertNotNull() {
            assertNotNull("should not be null", new Object());
        }
        
        @Test
        public void testAssetNull() {
            assertNull("should be null", null);
        }
        
        @Test
        public void testAssertNotSame() {
            assertNotSame("should not be same Object", new Object(), new Object());
        }
        
        @Test
        public void testAssertSame() {
            Integer aNumber = Integer.valueOf(985);
            assertSame("should be same", aNumber, aNumber);
        }
        
    }

    assertThat

    import static org.junit.Assert.*;
    import static org.hamcrest.Matchers.*;
    
    import java.util.Arrays;
    
    import org.junit.Test;
    
    public class AssertThatTests {
        
    
        //JUnit Matchers assertThat
        @Test
        public void testAssertThatBothContainsString() {
            assertThat("albumen", both(containsString("a")).and(containsString("b")));
        }
        
        @Test
        public void testAssertThathasItemsContainsString() {
            assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
        }
        
        @Test
        public void testAssertThatEveryItemContainsString() {
            assertThat(Arrays.asList(new String[] {"fun", "ban", "net"}), everyItem(containsString("n")));
        } 
        
        /**
         * Core Hamcrest Matchers with assertThat
         * 组合使用多种匹配符
         */
        @Test
        public void testAssertThatHamcrestCoreMatchers() {
            assertThat("good", allOf(equalTo("good"), startsWith("good")));
            assertThat("good", not(allOf(equalTo("good"), equalTo("bad"))));
            assertThat("good", anyOf(equalTo("good"), equalTo("bad")));
            assertThat(3, not(either(equalTo(6)).or(equalTo(9))));
            assertThat(new Object(), not(sameInstance(new Object())));
        }
        
        /**
         * Readable failure message
         * assertThat会提供可读性的错误信息,assertTrue不会
         */
        @Test
        public void testFailureMessage() {
            String s = "coour";
            //assertTrue(s.contains("color") || s.contains("colour"));
            assertThat(s, anyOf(containsString("color"), containsString("colour")));
        }
    
    }

    套件测试 - Aggregating tests in Suites

     如果你写了一系列的测试类(十几个甚至几十个),你不可能一个一个的去测试,此时,套件测试就会派上用场。

     创建一个空类,不需要为这个类定义任何东西,只需要在这个类的头部添加注解@RunWith(Suite.class)。

     @Suite.SuiteClasses。在SuiteClasses里添加需要测试的类。创建的这个类只是作为一个载体,承载上面的注解。

    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;
    
    import assertions.AssertTests;
    import assertions.AssertThatTests;
    
    @RunWith(Suite.class)
    @Suite.SuiteClasses({
        //添加需要测试的类
        AssertTests.class,
        AssertThatTests.class
        
        // more test classes
    })
    
    public class FutureTestSuite {
        
        /**
         * the class remains empty,
         * used only a holder for the above annotations.
         */
    }

    What's the difference between failure and error in JUnit

    顺序测试 - Test Execution Order

     顺序测试的应用场景不是很多,当你想让你写的一系列测试方法按照一定的顺序执行才会用到。

     添加@FixMethodOrder注解,有三种常用的运行顺序定义在枚举类MethodSorters内。

     DEFAULT、JVM、NAME_ASCENDING。

     DEFAULT:默认,比较方法名的hashCode值。

     JVM:依赖JVM的实现,不同机器上可能有所不同。

     NAME_ASCENDING:按照测试函数方法名升序。 

    import org.junit.FixMethodOrder;
    import org.junit.Test;
    import org.junit.runners.MethodSorters;
    
    @FixMethodOrder(MethodSorters.NAME_ASCENDING)
    //@FixMethodOrder(MethodSorters.DEFAULT)
    //@FixMethodOrder(MethodSorters.JVM)
    public class TestMethodOrder {
        
        @Test
        public void testA() {
            System.out.println("first");
        }
        
        @Test
        public void testB() {
            System.out.println("second");
        }
        
        @Test
        public void testC() {
            System.out.println("third");
        }
    
    }

    异常测试 - Exception Testing

     开发者在编写程序时,有些代码可能会抛出异常。

     如何测试这些代码是否按照我们预想的抛出异常,此时就需要用到异常测试。

     @Test注解有一个可选择的参数expected,用于指定可能抛出的异常。例如:

     Expected Exceptions

    import java.util.ArrayList;
    import org.junit.Test;
    
    public class TestException {
        
        @SuppressWarnings("unused")
        private double result;
        
        @Test(expected = ArithmeticException.class)
        public void divide() {
            result = 1/0;
        }
        
        /**
         * The expected parameter should be used with care. 
         * The test will pass if any code in the method throws IndexOutOfBoundsException
         */
        @Test(expected = IndexOutOfBoundsException.class)
        public void empty() {
            new ArrayList<Object>().get(0);
        }
    
    }

     不过,expected参数应当谨慎使用,因为,如果测试方法任意一处代码抛出expected指定的异常,

     测试都会通过,无法准确定位哪处代码抛出的异常。长远考虑,推荐使用ExpectedException rule。

     关于Rule会在另一篇文章中介绍。

     ExceptedException rule

     上面的测试方法对于测试简单的例子比较适用,但它存在一定的限制,比如开发者无法测试异常信息。

     当然JUnit3.x 提供了Try/Catch Idiom可以预测异常和异常信息。JUnit4则提供了ExpectedException rule。

     不同的是,JUnit4 expectedMessage除了可以预测可能抛出的异常信息,还可以与Hamcrest的Matchars配合 使用,

     编写出更加灵活的测试。例如: 

    import static org.hamcrest.Matchers.containsString;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.ExpectedException;
    
    public class TestExceptionRule {
        
        //Expected Exception Rule
        @Rule
        public ExpectedException thrown =ExpectedException.none();
        
        /**
         * This rule lets you indicate not only what exception you are expecting, 
         * but also the exception message you are expecting:
         */
        @Test
        public void shouldTestExceptionMessage() {
            List<Object> list = new ArrayList<Object>();
            
            thrown.expect(IndexOutOfBoundsException.class);
            thrown.expectMessage("Index: 0, Size: 0");
            thrown.expectMessage(containsString("Size: 0"));
            
            list.get(0);
        }
    
    }

    测试忽略 - Ignore tests

     如果在某次测试中想要跳过某个测试方法,就使用@Ignore注解。

     当然直接注释掉@Test注解同样可以跳过该测试方法,不同的是,

     注释掉在测试结果中不会显示,而使用@Ignore会显示该方法被忽略。

    import org.junit.Ignore;
    import org.junit.Test;
    
    public class TestIgnore {
        
        @Test
        public void testA() {
            System.out.println("executed");
        }
        
        @Ignore
        @Test
        public void testB() {
            System.out.println("ignored");
        }
    
    }

    超时测试 - Timeout for tests

     超时测试,顾名思义,测试方法超过指定的时间就会报Errors,注意不是Failures。

      Why is JUnit timeout an Error Not Failure 

     开发人员可以为单独某个方法设置超时时间,也可以为整个测试类设置统一超时时间。

     单独为一个方法设置超时时间,单位毫秒 

    import org.junit.Test;
    
    public class TestTimeout {
        
        @Test(timeout=100)
        public void testWithTimeOut() {
            
            for( ; ;) {
                
            }
            
        }
        
    }

     为整个类设置超时时间,单位毫秒

    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.Timeout;
    
    public class TestTimeoutRule {
        
        public static int n;
        
        @Rule
        public Timeout globalTimeout = new Timeout(100);
        
        @Test
        public void testInfiniteLoop1() {
            n++;
            for( ; ;) {
                
            }
        }
        
        @Test
        public void testInfiniteLoop2() {
            n++;
            for( ; ;) {
                
            }
        }
    
    }

    结束语

        Keeps the bar green to keep the code clean.


    作者:VictorWong
    出处:http://www.cnblogs.com/jwongo
    github:https://github.com/jwongo
    本文版权归作者和博客园共有,欢迎转载。水平有限,恳请读者批评指正。如果觉得对您有点用,就点个赞支持一下吧。

  • 相关阅读:
    TreeView 读取 xml 显示节点
    网上流传的数据操作类
    文件上传, 打开, 删除操作
    Reflector8.5 .net反编译工具 破解教程
    asp.net c# 打开新页面或页面跳转
    UGUI 要点总结
    EventSystem
    拖拽事件
    Unity Transform
    鼠标跟随(坐标转换)
  • 原文地址:https://www.cnblogs.com/jwongo/p/junit4-basics.html
Copyright © 2020-2023  润新知