3

I want to test a void type method that writes a String to the stdout. For this matter my team and I are trying to compare its output with the String / text we expect to see when we call the method.

Note: I'm a newbie both to Java and StackOverflow, so if there's a better way of doing this I'd be thankful to be notified about that.

 public static boolean test_printAdequateOption(String txt, double[]
 sol, Formatter out, String expectedOutput) {
         //Obj.
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         PrintStream ps = new PrintStream(baos);
         PrintStream sout = System.out;

         System.setOut(ps);

         printAdequateOption(txt, sol, out);

         System.out.flush();
         System.setOut(sout);



     return  baos.toString().equals(expectedOutput);
 }

The thought behind this test is to print the output of the method printAdequateOption to a PrintStream in such way that it allows me to save it as a String and thus compare it to an expected ouput parameter.

dreamer
  • 317
  • 1
  • 13
  • This seems like a proper answer to your question: http://stackoverflow.com/questions/32241057/how-to-test-a-print-method-in-java-using-junit – Michael Lihs Dec 28 '16 at 17:41
  • Nice. Do you have a question? – Ole V.V. Dec 28 '16 at 17:54
  • Yes, I want to understand how to compare a void type method with a string – user7350907 Dec 28 '16 at 17:55
  • Isn't that what you method is doing? @user7350907 – Ole V.V. Dec 28 '16 at 18:22
  • It's currently returning false for some reason. – user7350907 Dec 28 '16 at 18:34
  • I also tried to use return assertEquals(baos.toString(),expectedOutput) but I can't get it to work either (it's marked as a syntax error) – user7350907 Dec 28 '16 at 18:35
  • There could be various reasons why those strings are actually different - for example your console output might contain newlines (and/or trailing whitespaces); whereas your expected output is **not** containing those. The only way to find out: have your test method print out both strings character by character! – GhostCat Dec 28 '16 at 18:44
  • 1
    First thing for you to understand: here is not programming school where we hold your hand and fix all your problems with you. You started with a question "is this code ok"; but then, oops, your code isnt doing what it should be doing; and now mention that simple syntax errors seem to be a real big problem for you. We cant help with **all** of that. Nonetheless, I will update my answer a bit for you. But really: do not expect more. – GhostCat Dec 28 '16 at 19:19
  • Thanks for your accept ... as promised; I put some stuff into my answer; hope that helps! – GhostCat Dec 28 '16 at 19:24
  • 1
    Excuse me for being rude, did you have your friends upvote the question? I can't see why anyone independent would do that. – Ole V.V. Dec 28 '16 at 19:30
  • @user7350907 Please note: i reworked my answer, to get to the real JUnit context. I kinda overlooked that the other day. – GhostCat Dec 30 '16 at 07:44
  • @OleV.V. If you know that you are going to be rude ... then consider not putting up those words? I have seen worse questions getting upvoted; and accusing him of "friends upvoting him" is really harsh; as you of course do not have *any* evidence for that. At least to me, such kind of accusations is one of the really "hard" things to make on this platform; thus I would be really really careful about doing that. – GhostCat Dec 30 '16 at 07:46

2 Answers2

3

I would suggest refactoring your code to make it more testable. Obviously, your method printAdequateOption(...) both formats/creates the string and then calls System.out.println() (or something like that I assume).

You do not really care to test that the JDK call to print to standard output works for correct input, but you are interested in checking that the string that will be printed has the expected format. So essentially you need to extract the string creation/formatting into a separate method that will return the formatted string. The testing of such a method should be straightforward.

Thomas Kabassis
  • 1,326
  • 1
  • 12
  • 17
  • Great answer newbie ... I kinda overlooked the JUnit part; and really didn't emphasize enough how he should rework his design. Changed that; and you got a +1 for your excellent input ;-) – GhostCat Dec 30 '16 at 07:43
0

First:

  1. Naming: you only use the _ char within SOME_CONSTANT; so your method would better be called testPrintAdequateOption()
  2. Making the test itself a JUnit @Test; and then; instead of returning a boolean; you make a real assert within the test code; so that the test really fails on unexpected results (see below for how to do that)
  3. In case you really want to keep it this way, the name is slightly misleading. You would rather call your method doesPrintAdequateOptionGiveCorrectResultsOn() or something like that: methods returning a boolean should indicate in their name that they are about, well a boolean choice!

From a pure technical point the above code is OK. Besides the one design problem ... if testing content written to stdout is the "only" way to test your code; then you should look into make real testing possible.

In other words: your test is the best you can do; but the thing that you intend to test; respectively the fact that you have to do it this way ... is the real problem here!

Edit: I somehow overlooked that you were really asking for JUnit unit tests. Thus lets give a better answer and simply show how a real JUnit test should look like here:

@Test
public boolean testPrintAdequateOption() {   
     // overwrite stdout to allow for capturing print statement
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     PrintStream ps = new PrintStream(baos);
     PrintStream sout = System.out;
     System.setOut(ps);

     // call method with specific input
     String text = ...
     double[] sol = ...
     Formatter formatter = ...
     printAdequateOption(text, sol, formatter);

     System.out.flush();
     System.setOut(sout);

     assertThat(baos.toString(), is("expected output));
  }

That is how a reasonable test would look like; to be executed using JUnit; for example within your eclipse IDE.

Please note: I am using the assertThat assert, together with the is hamcrest matcher. See here about those two.

And the funny thing is: when you write such a test, it becomes really obvious that you should simply change your logic; very much as tomkab suggests in his answer - you don't want that printAdequateOption() produces a string and just prints that to System.out. Instead, you want a method that produces that string ... that you can directly. So no such detour as you are doing in your code!

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Hello GhostCat, – user7350907 Dec 28 '16 at 18:41
  • Hello GhostCat, Thanks for the reply.These tests are meant for a college project, and we weren't even told to use JUnit. We're having issues in testing methods that are used to list information but just like you said in a personal perspective there is no real testing possible in those methods. Can you advise us on what should our team do ? – user7350907 Dec 28 '16 at 18:44
  • why manually construct the error message? You should simply use assertion: `assert("output message", baos.toString(), expectedOutput );` – Adrian Shum Dec 29 '16 at 10:11
  • @user7350907 so is it a JUnit question or not? You marked this question JUnit in both the title and tag. – Adrian Shum Dec 29 '16 at 10:17
  • well I didn't downvote. What I said is, if it is aimed to be a proper JUnit test (which was suggested by the question itself, and your answer), you shouldn't compose your error message manually and send it to stdout. You should use assertion (JUnit, Hamcrest, etc) so that 1. it properly fails the test 2. It shows your intention 3. It construct proper message for you. Sorry my previous code is wrong :P (haven't noticed in a hurry). – Adrian Shum Dec 30 '16 at 01:46
  • In your case, proper JUnit assertion should look like `assertEquals("Output Message", expectedOutput, baos.toString());`. Hamcrest assertion should look like `assertThat(bao.toString(), is(expectedOutput));` . People should always remember, Unit Testing should be automated. You should not rely on manual inspection of output message to determine manually whether the test success or fail – Adrian Shum Dec 30 '16 at 01:48
  • @AdrianShum I guess I was out of my mind when I put up this answer and overlooked that he really asked for JUnit. I thus reworked my answer; it should better fit his needs now. Thank you very much for your help here! Gonna find some good answer of you and upvote that ... – GhostCat Dec 30 '16 at 07:40