10

I have been developing a command-line tool which calls System.exit() (don't want to use exceptions instead of) on certain inputs.

I am familiar with Java: How to test methods that call System.exit()? and its the most elegant approach.

Unfortunately, it is not enough pure, due to I had to add the dependencies to system-rules, junit-interface

Is there any common pattern for dealing with System.exit in specs2 which is more pure than my current approach which don't use 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"});
    }
}
Community
  • 1
  • 1
sergiusz.kierat
  • 139
  • 1
  • 10
  • 1
    Check out https://groups.google.com/forum/#!topic/scalatest-users/pyKHtcP6HXM, where Bill Venners (author of ScalaTest) provides a helpful answer. – Kevin Meredith Aug 22 '16 at 15:31

2 Answers2

12

If you really wish to go with a method using System.exit(), the simplest way to test it was actually called is to replace your SecurityManager with one that'll throw an ExitException (subclassing SecurityException) when System.exit() is called:

class 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)
}

test 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
    }
}
sergiusz.kierat
  • 139
  • 1
  • 10
Ven
  • 19,015
  • 2
  • 41
  • 61
  • I probably missed this part. Nevertheless, it would be nice to have `myObject.myFunctionExiting() must throwAn(ExitException(1))` – sergiusz.kierat Aug 22 '16 at 10:08
  • sorry for a mess, you should revert your last change, we have to use your proposition, why? because specs2 don't verify status.code, due to an [implementation](https://github.com/etorreborre/specs2/blob/master/matcher/src/main/scala/org/specs2/matcher/ExceptionMatchers.scala#L47) – sergiusz.kierat Aug 22 '16 at 10:18
  • you could check the exception message instead, right? – Ven Aug 22 '16 at 10:21
  • or simply uses `must throwAn[ExitException]` – Ven Aug 22 '16 at 10:22
  • I can use `must throwA[ExitException]`, but it is not equivalent with try { myObject.myFunctionExiting() Failure("shouldn't read this code") } catch { case e: ExitException => e.status must_== 1 } – sergiusz.kierat Aug 22 '16 at 10:25
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/121521/discussion-between-sergiusz-kierat-and-ven). – sergiusz.kierat Aug 22 '16 at 10:30
  • Why do you test `1 must_== 1`? – Martin Mar 09 '18 at 05:25
  • Great answer! I confirm that it works with scalatest as well. – Bunder Jun 27 '19 at 09:28
1

First option: use some exception instead of System.exit.

Second option: call application in separate thread and check return codes.

Third option: mock System.exit. There are many possibilities to do that, mentioned one is quite good.

However, there is no specs2-specific pattern to work with System.exit. Personally I'd suggest first or second options.

dveim
  • 3,381
  • 2
  • 21
  • 31
  • 1. First option is not acceptable for me. 2. Second option is a bit over-engineered to me and more complex than **system-rules** 3. Third option - same as previous IMO, using **system-rules** is concise, except that it don't use **specs2** :( – sergiusz.kierat Aug 22 '16 at 09:23
  • 2
    For third option, you can use mocking library that can mock static classes, e.g. PowerMock. – dveim Aug 22 '16 at 10:34
  • 1
    Another option is to provide/inject a simple component that itself calls System.exit(), and assert via a standard mocking framework that that component is called correctly – Brian Agnew Nov 06 '18 at 10:16