System Rules

A collection of JUnit rules for testing code that uses java.lang.System.

Get System Rules · GitHub

The scope of System Rules is divided into four sections.

Command-Line Applications

Command-line applications read from the command-line and write to it. Use SystemErrRule, SystemOutRule and TextFromStandardInputStream to provide the input text and verify the output.

System.exit

Use the ExpectedSystemExit rule to test code that calls System.exit(…). Verify that System.exit(…) is called, verify the status code of this call or check assertions the application terminated.

System Properties

If your test deals with system properties, you have to set them and restore them after the test. Use the ClearSystemProperty, ProvideSystemProperty and RestoreSystemProperties rules for this purpose.

Security Managers

Use the ProvideSecurityManager rule to provide a specific security manager for your test. The system's security manager is restored after the test.

Advice: System Rules needs at least JUnit 4.9.

All examples below are using the @Rule annotation, but the rules can be used with the @ClassRule annotation, too.

Clear Properties

The ClearSystemProperty rule deletes the property before the test and restores the original value of the property when the test is finished.

public void MyTest {
	@Rule
	public final ClearSystemProperty myPropertyIsCleared
	 = new ClearSystemProperty("MyProperty");

	@Test
	public void overrideProperty() {
		assertNull(System.getProperty("MyProperty"));
	}
}

Provide Properties

The ProvideSystemProperty rule provides an arbitrary value for a system property to a test. After the test the original value is restored.

public void MyTest {
	@Rule
	public final ProvideSystemProperty myPropertyHasMyValue
	 = new ProvideSystemProperty("MyProperty", "MyValue");

	@Rule
	public final ProvideSystemProperty otherPropertyIsMissing
	 = new ProvideSystemProperty("OtherProperty", null);

	@Test
	public void overrideProperty() {
		assertEquals("MyValue", System.getProperty("MyProperty"));
		assertNull(System.getProperty("OtherProperty"));
	}
}

You could also use a single instance of the rule to achieve the same effect:

public void MyTest {
	@Rule
	public final ProvideSystemProperty properties
	 = new ProvideSystemProperty("MyProperty", "MyValue").and("OtherProperty", null);

	@Test
	public void overrideProperty() {
		assertEquals("MyValue", System.getProperty("MyProperty"));
		assertNull(System.getProperty("OtherProperty"));
	}
}

You can use a properties file to supply properties for the ProvideSystemProperty rule. The file can be from the file system or the class path. In the first case use

@Rule
public final ProvideSystemProperty properties
 = ProvideSystemProperty.fromFile("/home/myself/example.properties");

and in the second case use

@Rule
public final ProvideSystemProperty properties
 = ProvideSystemProperty.fromResource("example.properties");

Restore Properties

The RestoreSystemProperties rule undoes changes of all system properties when the test finishes (whether it passes or fails).

public void MyTest {
	@Rule
	public final RestoreSystemProperties restoreSystemProperties
	 = new RestoreSystemProperties();

	@Test
	public void overrideProperty() {
		//after the test the original value of "MyProperty" will be restored.
		System.setProperty("MyProperty", "other value");
		...
	}
}

System.err and System.out

The SystemErrRule and the SystemOutRule help you to create tests for classes that write to System.err or System.out. They can record everything written to System.err or System.out. The text is available by calling getLog().

public void MyTest {
	@Rule
	public final SystemErrRule systemErrRule = new SystemErrRule().enableLog();

	@Test
	public void writesTextToSystemErr() {
		System.err.print("hello world");
		assertEquals("hello world", systemErrRule.getLog());
	}
}
public void MyTest {
	@Rule
	public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();

	@Test
	public void writesTextToSystemOut() {
		System.out.print("hello world");
		assertEquals("hello world", systemOutRule.getLog());
	}
}

If you verify logs that contain line separators than the separators are different (e.g. Linux: \n, Windows: \r\n). Use getLogWithNormalizedLineSeparator() for a log that always has the line separator \n.

public void MyTest {
	@Rule
	public final SystemErrRule systemErrRule = new SystemErrRule().enableLog();

	@Test
	public void writesTextToSystemErr() {
		System.err.print(String.format("hello world%n)");
		assertEquals("hello world\n", systemErrRule.getLog());
	}
}
public void MyTest {
	@Rule
	public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();

	@Test
	public void writesTextToSystemOut() {
		System.out.print(String.format("hello world%n)");
		assertEquals("hello world\n", systemOutRule.getLog());
	}
}

The log can be cleared if you want to discard some text that has been written to the log.

public void MyTest {
	@Rule
	public final SystemErrRule systemErrRule = new SystemErrRule().enableLog();

	@Test
	public void writesTextToSystemErr() {
		System.err.print("hello world");
		systemErrRule.clear().
		System.err.print("foo");
		assertEquals("foo", systemErrRule.getLog());
	}
}
public void MyTest {
	@Rule
	public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();

	@Test
	public void writesTextToSystemOut() {
		System.out.print("hello world");
		systemOutRule.clear().
		System.out.print("foo");
		assertEquals("foo", systemOutRule.getLog());
	}
}

The output is still written to System.err and System.out. In general this is not necessary. Avoiding the output may speed up the test and reduce the clutter on the commandline. You can disable the output by calling mute().

@Rule
public final SystemErrRule systemErrRule = new SystemErrRule().mute();
@Rule
public final SystemOutRule systemOutRule = new SystemOutRule().mute();

Muting and logging can be combined.

@Rule
public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().mute();
@Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().mute();

In case of a failed test it is sometimes helpful to see the output. This is when the method muteForSuccessfulTests() comes into play.

@Rule
public final SystemErrRule systemErrRule = new SystemErrRule().muteForSuccessfulTests();
@Rule
public final SystemOutRule systemOutRule = new SystemOutRule().muteForSuccessfulTests();

System.in

The TextFromStandardInputStream rule helps you to create tests for classes which read from System.in. You specify the text provided by System.in, by calling provideText(String). The example's class under test reads two numbers from System.in and calculates the sum of these numbers.

Class Under Test

import java.util.Scanner;

public class Summarize {
  public static int sumOfNumbersFromSystemIn() {
    Scanner scanner = new Scanner(System.in);
    int firstSummand = scanner.nextInt();
    int secondSummand = scanner.nextInt();
    return firstSummand + secondSummand;
  }
}

Test

import static org.junit.Assert.*;
import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.*;

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

public class SummarizeTest {
  @Rule
  public final TextFromStandardInputStream systemInMock
    = emptyStandardInputStream();

  @Test
  public void summarizesTwoNumbers() {
    systemInMock.provideText("1\n2\n");
    assertEquals(3, Summarize.sumOfNumbersFromSystemIn());
  }
}

In rare cases you need to simulate a user who stops between inputs. (E.g. if you use mutliple Scanners or something else that relies on System.in.read() returning -1 after a line break.) In such cases you can provided the consecutive inputs as individual Strings to TextFromStandardInputStream.

Class Under Test

import java.util.Scanner;

public class Summarize {
  public static int sumOfNumbersFromSystemIn() {
    Scanner scanner = new Scanner(System.in);
    int firstSummand = scanner.nextInt();
    scanner = new Scanner(System.in);
    int secondSummand = scanner.nextInt();
    return firstSummand + secondSummand;
  }
}

Test

import static org.junit.Assert.*;
import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.*;

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

public class SummarizeTest {
  @Rule
  public final TextFromStandardInputStream systemInMock
    = emptyStandardInputStream();

  @Test
  public void summarizesTwoNumbers() {
    systemInMock.provideText("1\n", "2\n");
    assertEquals(3, Summarize.sumOfNumbersFromSystemIn());
  }
}

Using \n as line separator lets your test fail on systems that hava a different line separator (e.g. Windows). This problem can be avoided by using the method provideLines that is similar to provideText but appends the correct line separator to each text.

import static org.junit.Assert.*;
import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.*;

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

public class SummarizeTest {
  @Rule
  public final TextFromStandardInputStream systemInMock
    = emptyStandardInputStream();

  @Test
  public void summarizesTwoNumbers() {
    systemInMock.provideLines("1", "2");
    assertEquals(3, Summarize.sumOfNumbersFromSystemIn());
  }
}

System.exit()

If your code calls System.exit(), then your test stops and doesn't finish. The ExpectedSystemExit rule allows in-test specification of expected System.exit() calls. Furthermore you cannot use JUnit's assert methods because of the abnormal termination of your code. As a substitute you can provide an Assertion object to the ExpectedSystemExit rule.

Some care must be taken if your system under test creates a new thread and this thread calls System.exit(). In this case you have to ensure that the test does not finish before System.exit() is called.

Class Under Test

public class AppWithExit {
  public static String message;

  public static void doSomethingAndExit() {
    message = "exit ...";
    System.exit(1);		
  }

  public static void doNothing() {
  }
}

Test

public void AppWithExitTest {
  @Rule
  public final ExpectedSystemExit exit = ExpectedSystemExit.none();

  @Test
  public void exits() {
    exit.expectSystemExit();
    AppWithExit.doSomethingAndExit();
  }

  @Test
  public void exitsWithStatusCode1() {
    exit.expectSystemExitWithStatus(1);
    AppWithExit.doSomethingAndExit();
  }

  @Test
  public void writesMessage() {
    exit.expectSystemExitWithStatus(1);
    exit.checkAssertionAfterwards(new Assertion() {
      public void checkAssertion() {
        assertEquals("exit ...", AppWithExit.message);
      }
    });
    AppWithExit.doSomethingAndExit();
  }

  @Test
  public void systemExitWithStatusCode1() {
    exit.expectSystemExitWithStatus(1);
    AppWithExit.doSomethingAndExit();
  }

  @Test
  public void noSystemExit() {
    AppWithExit.doNothing();
    //passes
  }
}

Security Manager

If you need a special security manager to test your code, you may provide it by using the ProvideSecurityManager rule. This rule replaces the system's security manager with yours throughout the test

public void MyTest {
	private final MySecurityManager securityManager
	 = new MySecurityManager();

	@Rule
	public final ProvideSecurityManager provideSecurityManager
	 = new ProvideSecurityManager(securityManager);

	@Test
	public void overrideProperty() {
		assertEquals(securityManager, System.getSecurityManager());
	}
}

Authors

Marc Philipp (mail@marcphilipp.de)
Stefan Birkner (mail@stefan-birkner.de)

Contact

Stefan Birkner (mail@stefan-birkner.de)