public class SystemLambda extends Object
SystemLambda is a collection of functions for testing code
that uses java.lang.System.
Command-line applications terminate by calling System.exit with
some status code. If you test such an application then the JVM that runs the
test exits when the application under test calls System.exit. You can
avoid this with the method
catchSystemExit which also returns the
status code of the System.exit call.
@Test
void application_exits_with_status_42(
) throws Exception {
int statusCode = catchSystemExit(() -> {
System.exit(42);
});
assertEquals(42, statusCode);
}
The method catchSystemExit throws an AssertionError if the
code under test does not call System.exit. Therefore your test fails
with the failure message "System.exit has not been called."
The method
withEnvironmentVariable
allows you to set environment variables within your test code that are
removed after your code under test is executed.
@Test
void execute_code_with_environment_variables(
) throws Exception {
List<String> values = withEnvironmentVariable("first", "first value")
.and("second", "second value")
.execute(() -> asList(
System.getenv("first"),
System.getenv("second")
));
assertEquals(
asList("first value", "second value"),
values
);
}
The function
restoreSystemProperties
guarantees that after executing the test code each System property has the
same value like before. Therefore you can modify System properties inside of
the test code without having an impact on other tests.
@Test
void execute_code_that_manipulates_system_properties(
) throws Exception {
restoreSystemProperties(() -> {
System.setProperty("some.property", "some value");
//code under test that reads properties (e.g. "some.property") or
//modifies them.
});
}
Command-line applications usually write to the console. If you write such
applications you need to test the output of these applications. The methods
tapSystemErr,
tapSystemErrNormalized,
tapSystemOut and
tapSystemOutNormalized allow you
to tap the text that is written to System.err/System.out. The
methods with the suffix Normalized normalize line breaks to
\n so that you can run tests with the same assertions on different
operating systems.
@Test
void application_writes_text_to_System_err(
) throws Exception {
String text = tapSystemErr(() -> {
System.err.print("some text");
});
assertEquals(text, "some text");
}
@Test
void application_writes_mutliple_lines_to_System_err(
) throws Exception {
String text = tapSystemErrNormalized(() -> {
System.err.println("first line");
System.err.println("second line");
});
assertEquals(text, "first line\nsecond line\n");
}
@Test
void application_writes_text_to_System_out(
) throws Exception {
String text = tapSystemOut(() -> {
System.out.print("some text");
});
assertEquals(text, "some text");
}
@Test
void application_writes_mutliple_lines_to_System_out(
) throws Exception {
String text = tapSystemOutNormalized(() -> {
System.out.println("first line");
System.out.println("second line");
});
assertEquals(text, "first line\nsecond line\n");
}
You can assert that nothing is written to
System.err/System.out by wrapping code with the function
assertNothingWrittenToSystemErr/assertNothingWrittenToSystemOut. E.g. the following tests fail:
@Test
void fails_because_something_is_written_to_System_err(
) throws Exception {
assertNothingWrittenToSystemErr(() -> {
System.err.println("some text");
});
}
@Test
void fails_because_something_is_written_to_System_out(
) throws Exception {
assertNothingWrittenToSystemOut(() -> {
System.out.println("some text");
});
}
If the code under test writes text to
System.err/System.out then it is intermixed with the output
of your build tool. Therefore you may want to avoid that the code under test
writes to System.err/System.out. You can achieve this with
the function muteSystemErr/muteSystemOut. E.g. the
following tests don't write anything to
System.err/System.out:
@Test
void nothing_is_written_to_System_err(
) throws Exception {
muteSystemErr(() -> {
System.err.println("some text");
});
}
@Test
void nothing_is_written_to_System_out(
) throws Exception {
muteSystemOut(() -> {
System.out.println("some text");
});
}
Interactive command-line applications read from System.in. If you
write such applications you need to provide input to these applications. You
can specify the lines that are available from System.in with the
method withTextFromSystemIn
@Test
void Scanner_reads_text_from_System_in(
) throws Exception {
withTextFromSystemIn("first line", "second line")
.execute(() -> {
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
assertEquals("first line", scanner.nextLine());
});
}
For complete test coverage you may also want to simulate System.in
throwing exceptions when the application reads from it. You can specify such
an exception (either RuntimeException or IOException) after
specifying the text. The exception will be thrown by the next read
after the text has been consumed.
@Test
void System_in_throws_IOException(
) throws Exception {
withTextFromSystemIn("first line", "second line")
.andExceptionThrownOnInputEnd(new IOException())
.execute(() -> {
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
scanner.nextLine();
assertThrownBy(
IOException.class,
() -> scanner.readLine()
);
});
}
@Test
void System_in_throws_RuntimeException(
) throws Exception {
withTextFromSystemIn("first line", "second line")
.andExceptionThrownOnInputEnd(new RuntimeException())
.execute(() -> {
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
scanner.nextLine();
assertThrownBy(
RuntimeException.class,
() -> scanner.readLine()
);
});
}
You can write a test that throws an exception immediately by not providing any text.
withTextFromSystemIn()
.andExceptionThrownOnInputEnd(...)
.execute(() -> {
Scanner scanner = new Scanner(System.in);
assertThrownBy(
...,
() -> scanner.readLine()
);
});
The function
withSecurityManager
lets you specify the SecurityManager that is returned by
System.getSecurityManger() while your code under test is executed.
@Test
void execute_code_with_specific_SecurityManager(
) throws Exception {
SecurityManager securityManager = new ASecurityManager();
withSecurityManager(
securityManager,
() -> {
//code under test
//e.g. the following assertion is met
assertSame(
securityManager,
System.getSecurityManager()
);
}
);
}
After withSecurityManager(...) is executed
System.getSecurityManager() returns the original security manager
again.
| Modifier and Type | Class and Description |
|---|---|
static class |
SystemLambda.SystemInStub
A stub that defines the text provided by
System.in. |
static class |
SystemLambda.WithEnvironmentVariables
A collection of values for environment variables.
|
| Constructor and Description |
|---|
SystemLambda() |
| Modifier and Type | Method and Description |
|---|---|
static void |
assertNothingWrittenToSystemErr(Statement statement)
Executes the statement and fails (throws an
AssertionError) if
the statement tries to write to System.err. |
static void |
assertNothingWrittenToSystemOut(Statement statement)
Executes the statement and fails (throws an
AssertionError) if
the statement tries to write to System.out. |
static int |
catchSystemExit(Statement statement)
Executes the statement and returns the status code that is provided to
System.exit(int) within the statement. |
static void |
muteSystemErr(Statement statement)
Executes the statement and suppresses the output of the statement to
System.err. |
static void |
muteSystemOut(Statement statement)
Executes the statement and suppresses the output of the statement to
System.out. |
static void |
restoreSystemProperties(Statement statement)
Executes the statement and restores the system properties after the
statement has been executed.
|
static String |
tapSystemErr(Statement statement)
Executes the statement and returns the text that was written to
System.err by the statement. |
static String |
tapSystemErrNormalized(Statement statement)
Executes the statement and returns the text that was written to
System.err by the statement. |
static String |
tapSystemOut(Statement statement)
Executes the statement and returns the text that was written to
System.out by the statement. |
static String |
tapSystemOutNormalized(Statement statement)
Executes the statement and returns the text that was written to
System.out by the statement. |
static SystemLambda.WithEnvironmentVariables |
withEnvironmentVariable(String name,
String value)
Executes the statement with the specified environment variables.
|
static void |
withSecurityManager(SecurityManager securityManager,
Statement statement)
Executes the statement with the provided security manager.
|
static SystemLambda.SystemInStub |
withTextFromSystemIn(String... lines)
Executes the statement and lets
System.in provide the specified
text during the execution. |
public static void assertNothingWrittenToSystemErr(Statement statement) throws Exception
AssertionError) if
the statement tries to write to System.err.
The following test fails
@Test
void fails_because_something_is_written_to_System_err(
) throws Exception {
assertNothingWrittenToSystemErr(() -> {
System.err.println("some text");
});
}
The test fails with the failure "Tried to write 's' to System.err
although this is not allowed."statement - an arbitrary piece of code.AssertionError - if the statements tries to write to
System.err.Exception - any exception thrown by the statement.assertNothingWrittenToSystemOut(Statement)public static void assertNothingWrittenToSystemOut(Statement statement) throws Exception
AssertionError) if
the statement tries to write to System.out.
The following test fails
@Test
void fails_because_something_is_written_to_System_out(
) throws Exception {
assertNothingWrittenToSystemOut(() -> {
System.out.println("some text");
});
}
The test fails with the failure "Tried to write 's' to System.out
although this is not allowed."statement - an arbitrary piece of code.AssertionError - if the statements tries to write to
System.out.Exception - any exception thrown by the statement.assertNothingWrittenToSystemErr(Statement)public static int catchSystemExit(Statement statement) throws Exception
System.exit(int) within the statement. Additionally it avoids
that the JVM is shut down because of a call to System.exit(int).
@Test
void application_exits_with_status_42(
) throws Exception {
int statusCode = catchSystemExit(() -> {
System.exit(42);
});
assertEquals(42, statusCode);
}
statement - an arbitrary piece of code.System.exit(int).AssertionError - if the statement does not call
System.exit(int).Exception - any exception thrown by the statement.public static void muteSystemErr(Statement statement) throws Exception
System.err. Use this to avoid that the output of your build tool
gets mixed with the output of the code under test.
@Test
void nothing_is_written_to_System_err(
) throws Exception {
muteSystemErr(() -> {
System.err.println("some text");
}
);
}
statement - an arbitrary piece of code.Exception - any exception thrown by the statement.muteSystemOut(Statement)public static void muteSystemOut(Statement statement) throws Exception
System.out. Use this to avoid that the output of your build tool
gets mixed with the output of the code under test.
@Test
void nothing_is_written_to_System_out(
) throws Exception {
muteSystemOut(() -> {
System.out.println("some text");
});
}
statement - an arbitrary piece of code.Exception - any exception thrown by the statement.muteSystemErr(Statement)public static void restoreSystemProperties(Statement statement) throws Exception
@Test
void execute_code_that_manipulates_system_properties(
) throws Exception {
System.clearProperty("some property");
System.setProperty("another property", "value before test");
restoreSystemProperties(() -> {
System.setProperty("some property", "some value");
assertEquals(
"some value",
System.getProperty("some property")
);
System.clearProperty("another property");
assertNull(
System.getProperty("another property")
);
});
//values are restored after test
assertNull(
System.getProperty("some property")
);
assertEquals(
"value before test",
System.getProperty("another property")
);
}
statement - an arbitrary piece of code.Exception - any exception thrown by the statement.public static String tapSystemErr(Statement statement) throws Exception
System.err by the statement.
@Test
void application_writes_text_to_System_err(
) throws Exception {
String textWrittenToSystemErr = tapSystemErr(() -> {
System.err.print("some text");
});
assertEquals("some text", textWrittenToSystemErr);
}
statement - an arbitrary piece of code.System.err by the statement.Exception - any exception thrown by the statement.tapSystemOut(Statement)public static String tapSystemErrNormalized(Statement statement) throws Exception
System.err by the statement. New line characters are replaced
with a single \n.
@Test
void application_writes_mutliple_lines_to_System_err(
) throws Exception {
String textWrittenToSystemErr = tapSystemErrNormalized(() -> {
System.err.println("some text");
});
assertEquals("some text\n", textWrittenToSystemErr);
}
statement - an arbitrary piece of code.System.err by the statement.Exception - any exception thrown by the statement.tapSystemOut(Statement)public static String tapSystemOut(Statement statement) throws Exception
System.out by the statement.
@Test
void application_writes_text_to_System_out(
) throws Exception {
String textWrittenToSystemOut = tapSystemOut(() -> {
System.out.print("some text");
});
assertEquals("some text", textWrittenToSystemOut);
}
statement - an arbitrary piece of code.System.out by the statement.Exception - any exception thrown by the statement.tapSystemErr(Statement)public static String tapSystemOutNormalized(Statement statement) throws Exception
System.out by the statement. New line characters are replaced
with a single \n.
@Test
void application_writes_mutliple_lines_to_System_out(
) throws Exception {
String textWrittenToSystemOut = tapSystemOutNormalized(() -> {
System.out.println("some text");
});
assertEquals("some text\n", textWrittenToSystemOut);
}
statement - an arbitrary piece of code.System.out by the statement.Exception - any exception thrown by the statement.tapSystemErr(Statement)public static SystemLambda.WithEnvironmentVariables withEnvironmentVariable(String name, String value)
@Test
void execute_code_with_environment_variables(
) throws Exception {
List<String> values = withEnvironmentVariable("first", "first value")
.and("second", "second value")
.and("third", null)
.execute(() -> asList(
System.getenv("first"),
System.getenv("second"),
System.getenv("third")
));
assertEquals(
asList("first value", "second value", null),
values
);
}
You cannot specify the value of an an environment variable twice. An
IllegalArgumentException is thrown when you try.
Warning: This method uses reflection for modifying internals of the
environment variables map. It fails if your SecurityManager forbids
such modifications.
name - the name of the environment variable.value - the value of the environment variable.SystemLambda.WithEnvironmentVariables instance that can be used to
set more variables and run a statement with the specified environment
variables.SystemLambda.WithEnvironmentVariables.and(String, String),
SystemLambda.WithEnvironmentVariables.execute(Callable),
SystemLambda.WithEnvironmentVariables.execute(Statement)public static void withSecurityManager(SecurityManager securityManager, Statement statement) throws Exception
@Test
void execute_code_with_specific_SecurityManager(
) throws Exception {
SecurityManager securityManager = new ASecurityManager();
withSecurityManager(
securityManager,
() -> {
//code under test
//e.g. the following assertion is met
assertSame(securityManager, System.getSecurityManager())
}
);
}
The specified security manager is only present during the test.securityManager - the security manager that is used while the
statement is executed.statement - an arbitrary piece of code.Exception - any exception thrown by the statement.public static SystemLambda.SystemInStub withTextFromSystemIn(String... lines)
System.in provide the specified
text during the execution. In addition several Exceptions can be
specified that are thrown when System.in#read is called.
@Test
void Scanner_reads_text_from_System_in(
) throws Exception {
withTextFromSystemIn("first line", "second line")
.execute(() -> {
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
assertEquals("first line", scanner.nextLine());
});
}
You can also simulate a System.in that throws an
IOException or RuntimeException. Use
@Test
void System_in_throws_IOException(
) throws Exception {
withTextFromSystemIn()
.andExceptionThrownOnInputEnd(new IOException())
.execute(() -> {
assertThrownBy(
IOException.class,
() -> new Scanner(System.in).readLine())
);
)};
}
@Test
void System_in_throws_RuntimeException(
) throws Exception {
withTextFromSystemIn()
.andExceptionThrownOnInputEnd(new RuntimeException())
.execute(() -> {
assertThrownBy(
RuntimeException.class,
() -> new Scanner(System.in).readLine())
);
)};
}
If you provide text as parameters of withTextFromSystemIn(...)
in addition then the exception is thrown after the text has been read
from System.in.
lines - the lines that are available from System.in.SystemLambda.SystemInStub instance that is used to execute a
statement with its execute
method. In addition it can be used to specify an exception that is thrown
after the text is read.SystemLambda.SystemInStub.execute(Statement),
SystemLambda.SystemInStub.andExceptionThrownOnInputEnd(IOException),
SystemLambda.SystemInStub.andExceptionThrownOnInputEnd(RuntimeException)Copyright © 2020. All rights reserved.