Testing Java with JUnit Pioneer
Tags: java, test, junit, junitpioneer
Published: 2021-05-24 10:32:11

---
title: Testing Java with JUnit Pioneer
published: true
description: JUnit Pioneer is a mandatory tool for your Java testing toolset.
tags: ["java", "test", "junit", "junitpioneer"]
cover_image: https://source.unsplash.com/E0AHdsENmDg
---

## Making test code readable with JUnit Pioneer

Browsing a project's test cases is a common approach to better understanding the codebase. Making our tests readable has a tremendous impact on this approach's feasibility. For that, [JUnit Pioneer][1] is a mandatory tool in your toolset! 

This article won't cover everything included with [JUnit Pioneer][1]. It's all pretty well documented.

Let's take the following example:

```java
class App {
  private static final Set<String> acceptableAnswers = Set.of("yes", "sure");

  public static void main(final String... args) {
    System.out.println("Hello, do you love Java?");
    verifyResponse(new Scanner(System.in));
    System.out.println("Me too!");
  }

  private static void verifyResponse(final Scanner scanner) {
    if (!acceptableAnswers.contains(scanner.next().toLowerCase())) {
      System.out.println("""
          Hmmm... I'm not sure I understand.
          Please reply with 'yes' or 'sure'.
          Do you love Java?""");
      verifyResponse(scanner);
    }
  }
}
```

This is pretty straightforward, executing the main method will ask: "Hello, do you love Java?" and will not accept any answers other than "yes" or "sure".

The common approach for testing code that takes data from the System's `in` stream and prints it to its `out` stream is to hijack the streams:

```java
class Manual_Hijack_System_Streams_Test {
  private InputStream origIn;
  private PrintStream origOut;
  private OutputStream output;

  @BeforeEach
  void initialize() {
    origIn = System.in;
    origOut = System.out;
    output = new ByteArrayOutputStream();
    System.setOut(new PrintStream(output));
  }

  @AfterEach
  void cleanup() {
    System.setIn(origIn);
    System.setOut(origOut);
  }

  @ParameterizedTest
  @ValueSource(strings = {"yes", "sure"})
  void verify_replying_with_an_acceptable_answer(final String answer) {
    System.setIn(inputStreamOf(answer));
    App.main(new String[0]);

    assertArrayEquals(
      new String[] {"Hello, do you love Java?", "Me too!"},
      output.toString().split(System.lineSeparator()));
  }

  @Test
  void verify_replying_with_a_wrong_and_an_acceptable_answer() {
    System.setIn(inputStreamOf(collectAnswers("no", "yes")));
    App.main(new String[0]);

    assertArrayEquals(
      new String[] {
        "Hello, do you love Java?",
        """
            Hmmm... I'm not sure I understand.
            Please reply with 'yes' or 'sure'.
            Do you love Java?""",
        "Me too!"},
      output.toString().split(System.lineSeparator()));
  }

  private InputStream inputStreamOf(final String input) {
    return new ByteArrayInputStream(input.getBytes(UTF_8));
  }

  private String collectAnswers(final String... answers) {
    return stream(answers).collect(joining(System.lineSeparator()));
  }
}

```

With [JUnit Pioneer][1], we don't need to worry about hijacking the streams:

```java
class Let_Junit_Pioneer_Do_Its_Thing_Test {
  @ParameterizedTest
  @ValueSource(strings = {"yes", "sure"})
  @StdIo
  void verify_replying_with_an_acceptable_answer(final String answer, final StdOut output) {
    var origIn = System.in;

    System.setIn(inputStreamOf(answer));
    App.main(new String[0]);

    assertArrayEquals(
      new String[] {"Hello, do you love Java?", "Me too!"},
      output.capturedLines());

    System.setIn(origIn);
  }

  @Test
  @StdIo({"no", "yes"})
  void verify_replying_with_a_wrong_and_an_acceptable_answer(final StdOut output) {
    App.main(new String[0]);

    assertArrayEquals(
      new String[] {
        "Hello, do you love Java?",
        """
            Hmmm... I'm not sure I understand.
            Please reply with 'yes' or 'sure'.
            Do you love Java?""",
        "Me too!"},
      output.capturedLines());
  }

  private InputStream inputStreamOf(final String input) {
    return new ByteArrayInputStream(input.getBytes(UTF_8));
  }
}
```

For the second test method, `verify_replying_with_a_wrong_and_an_acceptable_answer`, we're using the `StdIo` annotation to hijack the `in` stream and specify the value to be captured by the stream to two lines, "no" and then "yes."
We also use the arguments resolver to inject our method with a hijacked `out` stream, which we can use to validate the result.

For the first test method, `verify_replying_with_an_acceptable_answer`, we're using a propertyless `StdIo` annotation. This annotation marks our test case for the arguments resolver, but we don't need it to hijack the `in` stream.
However, we make it hijack the `out` stream by instructing the arguments resolver to inject it so we can validate the results, as we did in the second test method.

The reason is that the test container produces multiple test cases. One with "yes" as the answer and one with "sure."
This means that the value specified for the `in` stream differs for each case, meaning we had to hijack the `in` stream ourselves for this specific test.

Executing any of the above test classes will output the following:

```text
[INFO] '-- JUnit Jupiter [OK]
[INFO]   +-- Manual Hijack System Streams Test [OK]
[INFO]   | +-- verify replying with a wrong and an acceptable answer [OK]
[INFO]   | '-- verify replying with an acceptable answer (String) [OK]
[INFO]   |   +-- [1] yes [OK]
[INFO]   |   '-- [2] sure [OK]
[INFO]   '-- Let Junit Pioneer Do Its Thing Test [OK]
[INFO]     +-- verify replying with a wrong and an acceptable answer (StdOut) [OK]
[INFO]     '-- verify replying with an acceptable answer (String, StdOut) [OK]
[INFO]       +-- [1] yes [OK]
[INFO]       '-- [2] sure [OK]
```

Checkout a previous article of mine, [Property-Based Matrix Testing in Java][2] and see how we can turn this:

```java
  @ParameterizedTest
  @MethodSource("getArguments")
  void using_junit_parameterized_test_with_method_source(
      final Direction direction, final Status status) {
    assertTrue(true);
  }

  static Stream<Arguments> getArguments() {
    return Stream.of(Direction.values())
        .flatMap(d -> Stream.of(Status.values()).map(s -> arguments(d, s)));
  }
```

Into this:

```java
  @CartesianProductTest
  @CartesianEnumSource(Direction.class)
  @CartesianEnumSource(Status.class)
  void using_junit_pioneer_cartesian_product_test_with_enum_source(
      final Direction direction, final Status status) {
    assertTrue(true);
  }
```

Check out the code for this tutorial on [Github][0].

[0]: https://github.com/TomerFi/junit-pioneer-frontiers-pushed
[1]: https://junit-pioneer.org/
[2]: https://dev.to/tomerfi/property-based-matrix-testing-in-java-47p4