Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.in and System.exit do not work together within the same test #14

Closed
stevenmanton opened this issue Aug 13, 2014 · 6 comments
Closed

Comments

@stevenmanton
Copy link

I'm attempting to test a command line runner (based on Spring Batch). Unfortunately, the runner exits rather than, say, returning a value to a trivial wrapper method that exits with that value. In any case, I'd also like to pass data by piping it in through stdin. I can confirm this works in a stand-alone application. However, the mocked data I'm passing into systemInMock doesn't seem to make it to the main method. My code looks something like the following

public class CommandLineRunnerStdInOutTests {

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

    @Rule
    public final TextFromStandardInputStream systemInMock = emptyStandardInputStream();

    @Rule
    public final StandardOutputStreamLog log = new StandardOutputStreamLog();

    private static final String INPUT_FILE = "data.csv";
    private static final String EXPECTED_FILE = "expected_data.csv";

    @Test
    public void testJob() throws Exception {
        String fileContents = Resources.toString(Resources.getResource(INPUT_FILE), Charsets.UTF_8);
        assertTrue(fileContents.length() >  20);
        systemInMock.provideText(fileContents); // passes: file is read correctly
        exit.expectSystemExitWithStatus(0); // passes: batch job completes, but doesn't see any input records
        CommandLineRunner.main(new String[]{"config.xml", "batchJob"});
    }

    @Test
    public void testFileOut() throws Exception {
        String fileContents = Resources.toString(Resources.getResource(EXPECTED_FILE), Charsets.UTF_8);
        assertEquals(fileContents, log.getLog()); // fails because log.getLog() returns empty String
    }
}

Thanks for writing such an awesome tool! Even with this issue, it has still helped me tremendously!

@stefanbirkner
Copy link
Owner

The test testFileOut() fails because it doesn't write anything to System.out. JUnit tests (= test methods) are independent. For each of your test methods a new instance of CommandLineRunnerStdInOutTests is created.

Unfortunately I cannot reproduce the problem of testJob(). I created a small application

public class Gh14App {
  public static void main(String... args) throws Exception {
    String output = args [0] + System.in.read() + args[1];
    System.out.write(output.getBytes());
    System.exit(0);
  }
}

and wrote a test

public class Gh14AppTest {

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

  @Rule
  public final TextFromStandardInputStream systemInMock = emptyStandardInputStream();

  @Rule
  public final StandardOutputStreamLog log = new StandardOutputStreamLog();

  @Test
  public void testJob() throws Exception {
    systemInMock.provideText("A");
    exit.expectSystemExitWithStatus(0);
    Gh14App.main("pre", "post");
    assertThat(log.getLog(), is(equalTo("preApost")));
  }
}

This test runs successfully.

How do you read the input from System.in? Maybe you can provide the code snippet that does this work.

@stevenmanton
Copy link
Author

Thanks for the quick response! I'm a little curious about the assertion in testJob(). In my code, it seems that the assertions after the exiting method are never reached. I think that's why you have the method exit.checkAssertionAfterwards(...). So I think that test might be passing simply because the assertion is never called. Can you verify this? if you change your code to

  @Test
  public void testJob() throws Exception {
    systemInMock.provideText("A");
    exit.expectSystemExitWithStatus(0);
    exit.checkAssertionAfterwards(new Assertion() {
      public void checkAssertion() {
        assertThat(log.getLog(), is(equalTo("preApost")));
      }
    });
    Gh14App.main("pre", "post");
  }

does the test still pass? Does it fail if you change to equalTo("XYZ")? If so, then it's probably something buried in how Spring handles stdin. Basically, I just set Spring to read from the file /dev/stdin; it doesn't use Java's System.in method. Still, I would be puzzled because it seems to work just fine when I call my program from the command line. For instance,

cat dat.csv | java -cp myJar.jar  CommandLineRunner

works just fine.

@stefanbirkner
Copy link
Owner

You're right concerning the assertion stuff. But testJob() is still successful when I use checkAssertionAfterwards (and replace preApost with pre65post) and it fails for equalTo("XYZ").

@stefanbirkner
Copy link
Owner

If your code reads from the File /dev/stdin then TextFromStandardInputStream won't help you, because it only modifies the InputStream that is returned by System.in.

IMHO you should use the Java abstraction System.in instead of using the file /dev/stdin. In this way your application is more agnostic to its enviroment.

@stevenmanton
Copy link
Author

Thanks so much for looking into this. I really appreciate you effort. It seems clear that the problem is in how I handle stdin. Thanks for the advice :-)

@stefanbirkner
Copy link
Owner

It is my pleasure to help you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants