zoukankan      html  css  js  c++  java
  • JUnit的Rule的使用

    我们在使用JUnit的时候,为了使测试结果更加清晰明确,会有以下的需求:

    • 一个类有多个测试方法,想知道日志是哪个测试方法

    • 想在所有测试方法的前后加上一些语句,用于初始化和销毁一些资源

    • 如果一个测试方法可能抛出多个异常,想知道运行时到底抛出哪些异常

    • ...

    以上的需求可以通过为每个方法加上相似的语句来实现,但这样未免有点麻烦和冗余,好在JUnit4为我们提供了一劳永逸的方法,使用Rule。

    Rule简介

    Rule是JUnit4.7加入的新特性,有点类似于拦截器,用于在测试方法执行前后添加额外的处理。实际上是@Before,@After的另一种实现。使用时需要放在实现了TestRule的成员变量上或者返回TestRule的方法上,且修饰符为public。Rule会应用于该类每个测试方法。

    内置Rule

    JUnit提供了很多内置的TestRule的实现满足日常使用,具体有如下:

    • Verifier:所有测试结束后对测试执行结果添加额外的逻辑验证测试最终成功与否。该抽象类为子类提供一个接口方法verify()供扩展

          String result = "success";
          /**
           * Verifier:用于在测试方法执行结束后,进行一些结果校验
           */
          @Rule
          public Verifier verifier = new Verifier() {
              @Override
              protected void verify() throws Throwable {
                  if (result.equals("fail")) {
                      throw new Exception("failed");
                  }
              }
          };
      
          @Test
          public void verifierTest() {
              result = "fail";
          }
      

      运行结果:

      java.lang.Exception: failed
      ...
      
    • ErrorCollector:是Verifier类的一个子类实现,用于在测试执行过程中收集错误信息,不会中断测试,最后调用verify()方法处理。

          /**
           * ErrorCollector:可以收集多个多个异常,并在方法结束后一起打印出来
           */
          @Rule
          public ErrorCollector errorCollector = new ErrorCollector();
      
          @Test
          public void errorCollectorTest() {
              errorCollector.addError(new RuntimeException("error 1"));
              System.out.println("==================================");
              errorCollector.addError(new RuntimeException("error 2"));
          }
      

      运行结果:

      ==================================
      java.lang.RuntimeException: error 1
      ...
      java.lang.RuntimeException: error 2
      ...
      
    • TemporaryFolder:是抽象类ExternalResource的一个子类实现,用于在JUnit测试执行前后,创建和删除临时目录

          /**
      
           * TemporaryFolder:创建临时目录/文件,测试方法执行结束后自动删除
           * 可以在构造方法中传入使用的父目录,否则默认使用系统临时目录
           */
          @Rule
          public TemporaryFolder temporaryFolder = new TemporaryFolder(new File("C:\Users\test"));
          @Test
           public void temporaryFolderTest() throws IOException, InterruptedException {
               temporaryFolder.newFolder("test");
               temporaryFolder.newFile("hello.txt");
               Thread.sleep(10 * 1000);
       }
      
    • TestName:是抽象类TestWatcher的一个子类实现,用于在测试执行过程中获取测试方法名称。在starting()中记录测试方法名,在getMethodName()中返回

      /**
       * TestName:获取当前测试方法的方法名
       */
       @Rule
       public TestName testName = new TestName();
       @Test
       public void testNameTest() {
           System.out.println("method is " + testName.getMethodName());
       }
      

      运行结果:

        method is testNameTest
      
    • TestWatcher:监视测试方法生命周期的各个阶段。该抽象类为子类提供了五个接口方法succeeded(), failed(), skipped(), starting()及finished()供扩展

          /**
           * TestWatcher:在测试方法开始,结束,成功,失败,跳过这些时间点调用相应方法
           */
          @Rule
          public TestWatcher testWatcher = new TestWatcher() {
              @Override
              protected void succeeded(Description description) {
                  System.out.println("success");
              }
      
              @Override
              protected void failed(Throwable e, Description description) {
                  System.out.println("failed");
              }
              @Override
               protected void starting(Description description) {
                   System.out.println("starting");
               }
               @Override
               protected void finished(Description description) {
                   System.out.println("finished");
               }
               @Override
               protected void skipped(AssumptionViolatedException e, Description description) {
                   System.out.println("skipped");
               }
           };
           @Test
           public void testWatcherTest() {
               System.out.println("testWatcher");
           }
      

      运行结果:

      starting
      testWatcher
      success
      finished
      
    • Timeout:与@Test中的timeout相对应,@Test只能修饰待测试方法,Timeout可以修饰待测试类

      /**
       * Timeout:超时时间,方法运行超时则抛出TestTimedOutException异常
       */
       @Rule
       public Timeout timeout = new Timeout(5, TimeUnit.SECONDS);
       @Test
       public void timeoutTest() throws InterruptedException {
           System.out.println("timeout");
           Thread.sleep(6*1000);
       }
      

      运行结果:

        org.junit.runners.model.TestTimedOutException: test timed out after 5 seconds
        ...
      
    • ExpectedException:与@Test中的expected相对应,提供更强大灵活的异常验证功能,@Test只能修饰待测试方法,ExpectedException可以修饰待测试类

          /**
           * ExpectedException:指定测试方法出现的异常,未出现或者出现其他类型的异常测试不通过
           */
          @Rule
          public ExpectedException expectedException=ExpectedException.none();
      
          @Test
          public void expectedExceptionTest(){
              expectedException.expect(NullPointerException.class);
      
              throw new RuntimeException();
          }
      

      运行结果:

      java.lang.AssertionError: 
      Expected: an instance of java.lang.NullPointerException
           but:  is a java.lang.RuntimeException
      

    自定义Rule

    当内置的Rule无法满足你的需求的时候,你还可以通过实现TestRule来自定义Rule。

    • 自定义RuleTest实现类

      /**
      
       * 自己实现TestRule
       */
      public class MyTestRule implements TestRule {
          /**
           * @param base 基础行为,要进行封装的行为
      
           * @param description test方法的描述,包括方法名,类名等
           * @return 可以使在基础行为上添加新动作,也可以是一个全新的动作
           */
          @Override
          public Statement apply(Statement base, Description description) {
              return new Statement() {
                  @Override
                  public void evaluate() throws Throwable {
                      String name = description.getMethodName();
                      System.out.println("开始调用" + name);
                      base.evaluate();
                      System.out.println("结束调用" + name);
                  }
              };
          }
      }
      

      使用自定义的Rlue类:

        @Rule
          public MyTestRule myTestRule=new MyTestRule();
      
          @Test
          public void myTestRuleTest(){
              System.out.println("myTestRuleTest");
          }
      

      运行结果:

      开始调用myTestRuleTest
      myTestRuleTest
      结束调用myTestRuleTest
      
    • 使用匿名类自定义Rule

          @Rule
      
          public TestRule myRule(){
              TestRule rule = (base, description) -> new Statement() {
                  @Override
                  public void evaluate() throws Throwable {
                      String name = description.getMethodName();
                      System.out.println("start invoke " + name);
      
                      base.evaluate();
                      System.out.println("finished invoke " + name);
      
                  }
              };
              return rule;
          }
          @Test
          public void myRuleTest(){
              System.out.println("myRule");
          }
      

      运行结果:

      start invoke myRuleTest
      myRule
      finished invoke myRuleTest
      
    Stay Hungry , Stay Foolish , Stay Patient , Stay Love !
  • 相关阅读:
    从新注册 .DLL CMD 运行regsvr32 *.dll注册该DLL 或 regsvr32 /s *.DLL 求证
    短信猫 TIdTCPServer TIdTCPClient
    转:Delphi和Office程序开发 --不错可查阅
    主窗体里面打开子窗体&&打印饼图《Delphi 6数据库开发典型实例》--图表的绘制
    TeeChart Pro 5.0
    软件打开时间、窗体透明度、背景色---《用delphi开发共享软件》-15.1任务管理器
    设置随机启动--《用delphi开发共享软件》-15.1任务管理器
    实现窗体随着鼠标移动(控件)--《用delphi开发共享软件》-15.1任务管理器
    第二百六十一节,Tornado框架模板引擎本质
    第二百六十节,Tornado框架-内置模板方法
  • 原文地址:https://www.cnblogs.com/henryyao/p/10789484.html
Copyright © 2011-2022 走看看