• Junit使用


      单元测试作用保障软甲质量和节省开发精力的重要手段,是每个码农必备的技能。

    JUNIT是JAVA中最常用的单元测试框架,下面介绍其中的重要知识点。

    1,Junit 核心概念

    Assert   断言用来判断具体测试条件是否满足,满足则静默,反之抛出异常。
     Test

     一个测试方法,用注解@Test定义,JUnit跑测试方法时,先创建Test Class的instance,

    再调用@Test注解的测试方法。有几个测试方法,Test Class的instance就会被创建几遍。

    Test Class   A test class is the container for @Test method. 
    Suite  The Suite allows to group test classes together.
    Runner  The Runner class runs tests.

    2、断言

    assertArrayEquals(expecteds, actuals) 查看两个数组是否相等。
    assertEquals(expected, actual) 查看两个对象是否相等。类似于字符串比较使用的equals()方法
    assertNotEquals(first, second) 查看两个对象是否不相等。
    assertNull(object) 查看对象是否为空。
    assertNotNull(object) 查看对象是否不为空。
    assertSame(expected, actual) 查看两个对象的引用是否相等。类似于使用“==”比较两个对象
    assertNotSame(unexpected, actual) 查看两个对象的引用是否不相等。类似于使用“!=”比较两个对象
    assertTrue(condition) 查看运行结果是否为true。
    assertFalse(condition) 查看运行结果是否为false。
    assertThat(actual, matcher) 查看实际值是否满足指定的条件
    fail() 让测试失败

    3、注解

    @Before 初始化方法
    @After 释放资源
    @Test 测试方法,在这里可以测试期望异常和超时时间
    @Ignore 忽略的测试方法
    @BeforeClass 针对所有测试,只执行一次,且必须为static void
    @AfterClass 针对所有测试,只执行一次,且必须为static void
    @RunWith 指定测试类使用某个运行器
    @Parameters 指定测试类的测试数据集合
    @Rule 允许灵活添加或重新定义测试类中的每个测试方法的行为
    @FixMethodOrder 指定测试方法的执行顺序

    这里插一句,@FixMethodOrder的参数为 MethodSorters是个Enum,有三个值:

    • MethodSorters.JVM
    • MethodSorters.DEFAULT
    • MethodSorters.NAME_ASCENDING

    JVM和DEFAULT都没法自定义顺序,只有NAME_ASCENDING是有意义的,

    但是一般需要修改test方法名来适应NAME_ASCENDING排序,个人感觉基本也没啥意义。

    测试执行顺序

    一个测试类单元测试的执行顺序为:

      @BeforeClass –> @Before –> @Test –> @After –> @AfterClass

    每一个测试方法的调用顺序为:

      @Before –> @Test –> @After

    需要注意的是,实际打印到console中的顺序可能并不是上面的顺序,在打印信息中加入时间信息,就能修正这种错误的印象。

     1 public class SumCalculator {
     2 
     3 
     4     public Integer sum(List<Integer> intMembers) {
     5         if (null == intMembers) {
     6             return 0;
     7         } else {
     8             return intMembers.stream().reduce(0, (e0, e1) -> e0 + e1);
     9         }
    10     }
    11 }
    import java.time.LocalTime;
    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class SumCalculatorTest {
    
        private SumCalculator calculator = new SumCalculator();
        public SumCalculatorTest() {
            System.out.println("constructor called!");
        }
    
        @BeforeClass
        public static void setUpClass() throws Exception {
            System.out.println(LocalTime.now() + ": BeforeClass");
        }
    
        @AfterClass
        public static void tearDownClass() throws Exception {
            System.out.println(LocalTime.now() + ": AfterClass");
        }
    
        @Before
        public void setUp() throws Exception {
            System.out.println(LocalTime.now() + ": Before");
        }
    
        @After
        public void tearDown() throws Exception {
            System.out.println(LocalTime.now() + ": After");
        }
    
    //    @Ignore
        @Test
        public void testSumOfNull() throws Exception {
            System.out.println(LocalTime.now() + ": test testSumOfNull about to run");
            int sumOfNull = this.calculator.sum(null);
            Assert.assertTrue(0 == sumOfNull);
        }
    
        @Test
        public void testSumOfEmptyList() throws Exception {
            System.out.println(LocalTime.now() + ": test testSumOfEmptyList about to run");
            int sumOfNull = this.calculator.sum(new ArrayList<>());
            Assert.assertTrue(0 == sumOfNull);
        }
    
        @Test
        public void testSum() throws Exception {
            System.out.println(LocalTime.now() + ": test testSum about to run");
            int sumOfNull = this.calculator.sum(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
            Assert.assertTrue(45 == sumOfNull);
        }
    }

     运行结果:

    13:05:07.033: BeforeClass
    constructor called!
    13:05:07.039: Before
    13:05:07.039: test testSum about to run
    13:05:07.062: After
    constructor called!
    13:05:07.065: Before
    13:05:07.065: test testSumOfEmptyList about to run
    13:05:07.067: After
    constructor called!
    13:05:07.070: Before
    13:05:07.070: test testSumOfNull about to run
    13:05:07.071: After
    13:05:07.071: AfterClass

    4 参数化测试

      有时在测试一个逻辑点时,会遇到测试多种类型输入的情形,最容易想到的方法是,

    写多个test method, 它们仅仅是输入输出不同。而使用参数化测试就可以简化测试代码,具体来说,

    当运行参数化测试时,test runner会创建多个test class的instance, 具体创建个数等于 参数个数 * test method个数

    运行参数化测试需要Parameterized作为Runner。

    import org.junit.Assert;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.Parameterized;
    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    
    @RunWith(Parameterized.class)
    public class ParameterizedSumCalculatorTest {
    
        @Parameterized.Parameters(name = "{index}: sum[{0}]={1}")
        public static Object[][] data() {
            return new Object[][]{
                    {null, 0},
                    {Collections.EMPTY_LIST, 0},
                    {Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9), 45}
            };
        }
    
        private SumCalculator calculator = new SumCalculator();
    
        private List<Integer> inputList;
    
        private Integer expected;
    
        public ParameterizedSumCalculatorTest(List<Integer> inputList, Integer expected) {
            this.inputList = inputList;
            this.expected = expected;
            System.out.println("constructor called!");
        }
    
        @Test
        public void test0() {
            System.out.println("test 0");
            System.out.println(inputList);
            Assert.assertEquals(expected, calculator.sum(inputList));
        }
    
        @Test
        public void test1() {
            System.out.println("in test1");
            System.out.println(inputList);
            Assert.assertEquals(expected, calculator.sum(inputList));
        }
    }

      在上面的代码中, 用注解@Parameters标记提供测试参数的方法,即上面的static方法data(),data()方法返回二维数组。

    注解的参数name是一个模板,其中模板参数{index}表示具体使用的测试参数在二维数组中的index,

    {0}和{1}表示测试参数是内层数组中的第几个参数,例如:在上面的代码中,当index=1时,name="1: sum[[]]=0"。

    那这个name模板的作用是啥呢,其实就是当测试用例跑不过的时候,用来识别是哪一组/多组参数跑失败了。 

      Parameterized作为Runner会为每组参数和每个测试方法创建测试Instance,在创建Instance过程中,

    需要把参数赋值给对象中的字段“inputList”和“expected”,赋值方式有两种:

    (1)添加使用“inputList”和“expected”作为初始化参数的构造函数

      如上例所示;

    (2)使用无初始化参数的构造函数,把field“inputList”和“expected”的访问限定符改为public,同时

    添加注解@Parameter(),括号中填入参数的位置,例如:

        @Parameter(0)
        public List<Integer> inputList;
      
        @Parameter(1)
        public Integer expected;

    其中无参构造函数可以省略。

     5 打包测试Suite

      如果想要一次性跑多个Test Class下的测试用例,可以新建一个Test Class,设置其Runner为Suite.class,

    然后,在加入注解@Suite.SuiteClasses({ TestClassA.class, TestClassB.class, ... })就可以了。

    import org.junit.runner.RunWith;  
    import org.junit.runners.Suite;  
      
    @RunWith(Suite.class)  
    @Suite.SuiteClasses({TestClassA.class, TestClassB.class})  
    public class AllCaseTest {  
      
    } 

    6  Rule

      在4.12版本中,官方推荐使用TestRule接口,所以,从TestRule着手,TestRule长这样:

    public interface TestRule {
        Statement apply(Statement base, Description description);
    }

    在TestRule的注释中,TestRule被描述为 an alteration in how a test is run and reported.

    个人理解TestRule是一种可以影响测试过程和结果记录的组件,具体来说,

    (1)它可以是test种用到的资源,比如文件系统,数据库连接等,测试前建立,测试后销毁;

    (2)或者是添加对test的额外检查;

    (3)或者是用于记录test结果等。

    TestRule可以使用两种注解来声明:

    (1)@org.junit.Rule : Rule用在public, non-static的Field或者Method上,

    (2)@org.junit.ClassRule: ClassRule用在Class上。

    例子:在测试执行前创建temporaryFolder,在执行后被删除。

       public static class HasTempFolder {
           @Rule
           public TemporaryFolder folder= new TemporaryFolder();
      
           @Test
           public void testUsingTempFolder() throws IOException {
               File createdFile= folder.newFile("myfile.txt");
               File createdFolder= folder.newFolder("subfolder");
               // ...
           }
       }
       @RunWith(Suite.class)
       @SuiteClasses({A.class, B.class, C.class})
       public class UsesExternalResource {
           public static Server myServer= new Server();
      
           @ClassRule
           public static ExternalResource resource= new ExternalResource() {
             @Override
             protected void before() throws Throwable {
                myServer.connect();
            }
      
            @Override
            protected void after() {
                   myServer.disconnect();
            }
         };
       }
    TestRule中的ExpectedException用于检测test是否抛出了期望的Exception,抛出则测试通过,反之,则测试不通过。 
        @Rule
        public ExpectedException thrownRule = ExpectedException.none();
    
        @Test
        public void throwsExceptionWithSpecificType() {
            thrownRule.expect(NullPointerException.class);
            throw new NullPointerException();
        }

    执行顺序:TestRules在@Before,@After,@BeforeClass,@AfterClass之前执行。

    7 其他

    (1)限时测试:在@Test中加入timeout参数就可以:

    @Test(timeout=1000)  
    public void testWithTimeout() {  
      ...  
    } 

     (2)@Ignore注解

    忽略这个test medhot

    8 Spring测试框架

    参考文章写得很好,这里直接给出链接: Junit使用教程(四)

    参考文章:

    【1】JUnit in Action 2E by Tahciver, Leme, Massol, and Gregory;

    【2】Junit 使用教程(二,三,四) by 鹏霄万里展雄飞。

    【3】JUnit 官网

  • 相关阅读:
    LeetCode 788. Rotated Digits
    LeetCode 606. Construct String from Binary Tree
    LeetCode 13. Roman to Integer
    LeetCode 387. First Unique Character in a String
    LeetCode 520. Detect Capital
    LeetCode 557. Reverse Words in a String III
    RSA加密算法及其与SpringMVC集成
    用phantomjs 进行网页整页截屏
    redis应用场景
    MQ产品比较-ActiveMQ-RocketMQ
  • 原文地址:https://www.cnblogs.com/tlz888/p/7043493.html
Copyright © 2020-2023  润新知