【问题标题】:Scala: How to test methods that call System.exit()?Scala:如何测试调用 System.exit() 的方法?
【发布时间】:2016-08-22 18:52:20
【问题描述】:

我一直在开发一个命令行工具,它在某些输入上调用System.exit()不想使用异常而不是)。

我熟悉Java: How to test methods that call System.exit()? 和它最优雅的approach

不幸的是,它不够纯,由于我必须将依赖项添加到system-rulesjunit-interface

specs2 中处理System.exit 是否有任何通用模式,比我目前不使用 specs2 的方法更纯粹?

import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.ExpectedSystemExit;

public class ConverterTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void emptyArgs() {
        exit.expectSystemExit();
        Converter.main(new String[]{});
    }

    @Test
    public void missingOutArgument() {
        exit.expectSystemExitWithStatus(1);
        Converter.main(new String[]{"--in", "src/test/resources/078.xml.gz"});
    }
}

【问题讨论】:

标签: scala unit-testing sbt specs2 testability


【解决方案1】:

如果您真的希望使用使用System.exit() 的方法,测试它实际调用的最简单方法是将您的SecurityManager 替换为一个会抛出ExitException(子类化SecurityException)的方法System.exit() 被调用:

SystemExitSpec

import java.security.Permission

import org.specs2.mutable.Specification
import org.specs2.specification.BeforeAfterAll

sealed case class ExitException(status: Int) extends SecurityException("System.exit() is not allowed") {
}

sealed class NoExitSecurityManager extends SecurityManager {
  override def checkPermission(perm: Permission): Unit = {}

  override def checkPermission(perm: Permission, context: Object): Unit = {}

  override def checkExit(status: Int): Unit = {
    super.checkExit(status)
    throw ExitException(status)
  }
}


abstract class SystemExitSpec extends Specification with BeforeAfterAll {

  sequential

  override def beforeAll(): Unit = System.setSecurityManager(new NoExitSecurityManager())

  override def afterAll(): Unit = System.setSecurityManager(null)
}

测试ConverterSpec

import org.specs2.execute.Failure

import scala.io.Source

class ConverterSpec extends SystemExitSpec {

"ConverterSpec" should {

    "empty args" >> {
      try {
        Converter.main(Array[String]())
        Failure("shouldn't read this code")
      } catch {
        case e: ExitException =>
          e.status must_== 1
      }
      1 must_== 1
    }
}

【讨论】:

  • 我可能错过了这部分。不过,如果有myObject.myFunctionExiting() must throwAn(ExitException(1)) 那就太好了
  • 对不起,一团糟,你应该恢复你上次的改变,我们必须使用你的提议,为什么?因为 specs2 不验证 status.code,因为 implementation
  • 你可以检查异常信息,对吧?
  • 或者干脆使用must throwAn[ExitException]
  • 我可以使用must throwA[ExitException],但它不等同于 try { myObject.myFunctionExiting() Failure("不应读取此代码") } catch { case e: ExitException => e。状态必须_== 1 }
【解决方案2】:

第一个选项:使用一些异常而不是System.exit

第二种选择:在单独的线程中调用应用程序并检查返回码。

第三个选项:模拟System.exit。有很多可能性可以做到这一点,提到的一种非常好。

但是,没有specs2 特定的模式可用于System.exit。我个人建议第一个或第二个选项。

【讨论】:

  • 1.第一个选项对我来说是不可接受的。 2. 第二个选项对我来说有点过度设计,并且比 system-rules 更复杂 3. 第三个选项 - 与以前的 IMO 相同,使用 system-rules 简洁,除了它不使用 specs2 :(
  • 对于第三个选项,您可以使用可以模拟静态类的模拟库,例如PowerMock。
  • 另一种选择是提供/注入一个简单的组件,该组件本身调用 System.exit(),并通过标准模拟框架断言该组件被正确调用
猜你喜欢
  • 2010-09-23
  • 2022-01-05
  • 2011-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多