• Stars
    star
    211
  • Rank 186,867 (Top 4 %)
  • Language
    Java
  • License
    MIT License
  • Created over 8 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

System Lambda is a collection of functions for testing code that uses java.lang.System

System Lambda

Build Status Linux Build Status Windows

System Lambda is a collection of functions for testing code which uses java.lang.System.

System Lambda is published under the MIT license. It requires at least Java 8.

For JUnit 4 there is an alternative to System Lambda. Its name is System Rules.

Installation

System Lambda is available from Maven Central.

<dependency>
    <groupId>com.github.stefanbirkner</groupId>
    <artifactId>system-lambda</artifactId>
    <version>1.2.1</version>
</dependency>

Please don't forget to add the scope test if you're using System Lambda for tests only. The changelog lists all the changes of System Lambda.

Usage

Import System Lambda's functions by adding

import static com.github.stefanbirkner.systemlambda.SystemLambda.*;

to your tests.

System.exit

Command-line applications terminate by calling System.exit with some status code. If you test such an application then the JVM that executes 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."

Environment Variables

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

System Properties

The method 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.
  });

  //Here the value of "some.property" is the same like before.
  //E.g. it is not set.
}

System.out and System.err

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, tapSystemOutNormalized, tapSystemErrAndOut and tapSystemErrAndOutNormalized 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("some text", 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("first line\nsecond line\n", text);
}

@Test
void application_writes_text_to_System_out(
) throws Exception {
  String text = tapSystemOut(() -> {
    System.out.print("some text");
  });
  assertEquals("some text", 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("first line\nsecond line\n", text);
}

@Test
void application_writes_text_to_System_err_and_out(
) throws Exception {
  String text = tapSystemErrAndOut(() -> {
    System.err.print("text from err");
    System.out.print("text from out");
  });
  assertEquals("text from errtext from out", text);
}

@Test
void application_writes_mutliple_lines_to_System_err_and_out(
) throws Exception {
  String text = tapSystemErrAndOutNormalized(() -> {
    System.err.println("text from err");
    System.out.println("text from out");
  });
  assertEquals("text from err\ntext from out\n", text);
}

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");
  });
}

System.in

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("second line", scanner.nextLine());
    });
}

For a 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();
      assertThrows(
        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();
      assertThrows(
        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);
    assertThrows(
      ...,
      () -> scanner.readLine()
    );
  });

Security Manager

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 executedSystem.getSecurityManager() returns the original security manager again.

Contributing

You have three options if you have a feature request, found a bug or simply have a question about System Lambda.

Development Guide

System Lambda is built with Maven and must be compiled under JDK 8. If you want to contribute code then

  • Please write a test for your change.
  • Ensure that you didn't break the build by running mvnw clean verify -Dgpg.skip.
  • Fork the repo and create a pull request. (See Understanding the GitHub Flow)

The basic coding style is described in the EditorConfig file .editorconfig.

System Lambda supports GitHub Actions (Linux) and AppVeyor (Windows) for continuous integration. Each pull request is automatically built by both CI servers. GitHub Actions tests with several Java versions (see Enhanced Testing) while AppVeyor tests with Java 8 only.

Project Decisions

There are decision records for some decisions that had been made for System Lambda. They are stored in the folder doc/Decision Records

Build with Docker

The script

./scripts/mvn_in_docker.sh clean verify -Dgpg.skip

builds System Lambda inside a Docker container. It uses your Maven local repository. This is helpful if you have a Linux or macOS without JDK 8.

Enhanced Testing

System Lambda is built with Java 8 and relies on JDK internals that are not available from Java 16 on (unless you run Java with additional flags). We verify that System Lambda works with newer Java versions by building it with OpenJDK 8 and executing tests with newer versions of OpenJDK. All this work is put into a script that you can run with

./scripts/test.sh

The script uses Docker for running the tests.

Release Guide

  • Select a new version according to the Semantic Versioning 2.0.0 Standard.
  • Update Changelog.md.
  • Set the new version in pom.xml and in the Installation section of this readme.
  • Commit the modified Changelog.md, pom.xml and README.md.
  • Run mvnw clean deploy with JDK 8.
  • Add a tag for the release: git tag system-lambda-X.X.X

More Repositories

1

system-rules

A collection of JUnit rules for testing code which uses java.lang.System.
Java
546
star
2

fake-sftp-server-rule

A JUnit rule that runs an in-memory SFTP server.
Java
40
star
3

fake-sftp-server-lambda

Runs an in-memory SFTP server while your tests are running.
Java
24
star
4

yfcc100m-downloader

YFCC100M Downloader
Python
24
star
5

fishbowl

Fishbowl provides helper methods for dealing with exceptions.
Java
22
star
6

travis-deploy-to-maven-central

An example Maven project for deploying a library to Maven Central
Shell
16
star
7

talkative-junit-tests

A JUnit test runner that supports arbitrary strings as test names.
Java
7
star
8

junit3

JUnit 3 only
Java
3
star
9

jfortschritt

JFortschritt is a small library for writing progress bars to the command-line.
Java
3
star
10

iti-ngram

Ngram analyzer written in Java
Java
1
star
11

semantic-wrapper

Generate wrapper classes with semantics.
Java
1
star
12

flea

A simple web server for static pages.
JavaScript
1
star
13

flashcardio

A web application for learning with flash cards.
JavaScript
1
star
14

locurlizer

A servlet filter for multilingual urls.
1
star
15

google-code-jam-template

A Java template for Google Code Jam
Java
1
star
16

glossy-buttons

A patch for Twitter Bootstrap that makes buttons glossy.
1
star
17

junit-playground

A playground with JUnit talks and experiments.
Java
1
star
18

chelidonium

Twitter Bootstrap meets Spring MVC
Java
1
star
19

jmarkdown2revealjs

Converts Markdown files to reveal.js slides.
Java
1
star
20

standardoutputstreamlog-ant

An ant project for verifying stefanbirkner/system-rules#8
Java
1
star
21

picover

A PicoContainer that makes it possible to use Spring managed beans.
Java
1
star
22

stefan-birkner.de

My Homepage
HTML
1
star
23

vallado

A library that supports testing exceptions with Java 8 Lambdas. Can be used with JUnit and with TestNG.
Java
1
star
24

junit4-issue-1488

This project contains code that verifies junitteam/junit#1488
HTML
1
star
25

gist-backup

Commandline tool for managing a backup of your Gists
Go
1
star