Java: test System output including “new lines” with assertEquals

▼魔方 西西 提交于 2020-08-04 05:33:48

问题


I'm currently writing a Unit Test for the Strategy Design Pattern. I'm comparing the System output with a string in the assertEquals method.The output looks the same but my test keeps failing.. . I'm thinking that I'm forgetting something to do with new lines or tabs?

My Unit Test:

import static org.junit.Assert.*;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class MiniDuck1Test {

    private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
    private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();

    @Before
    public void setUpStreams() {
        System.setOut(new PrintStream(outContent));
        System.setErr(new PrintStream(errContent));
    }

    @After
    public void cleanUpStreams() {
        System.setOut(null);
        System.setErr(null);
    }

    @Test
    public void testDuck1() {       
        Duck mallard = new MallardDuck();
        mallard.performQuack();
        mallard.performFly();

        Duck model = new ModelDuck();
        model.performFly();
        model.setFlyBehavior(new FlyRocketPowered());
        model.performFly();

        assertEquals("Quack\nI'm flying!!\nI can't fly\nI'm flying with a rocket", outContent.toString().trim());
    }
}

Output (second and third line appear red):

Quack
I'm flying!!
I can't fly
I'm flying with a rocket

Edit:

The quickest solution seemed to be adding an "\r" in front of my "\n". Multiple answers informed me that this needs to be done for Windows. After applying this my assertEquals looked like:

assertEquals("Quack\r\nI'm flying!!\r\nI can't fly\r\nI'm flying with a rocket", outContent.toString().trim());

Also: I forgot to mention that most of the code came from the book: "Head First Design Patterns" by Eric Freeman, Elisabeth Robson, Bert Bates and Kathy Sierra.


回答1:


In addition to the other answers if you are looking for a platform-independent way...

A quick platform-independent solution can be to replace the line separators

String expected = "Quack\nI'm flying!!\nI can't fly\nI'm flying with a rocket"
                       .replaceAll("\\n|\\r\\n", System.getProperty("line.separator"));
assertEquals(expected, outContent.toString().trim());

or using a PrintWriter to build the expected string.

StringWriter expectedStringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(expectedStringWriter);

printWriter.println("Quack");
printWriter.println("I'm flying!!");
printWriter.println("I can't fly");
printWriter.println("I'm flying with a rocket");
printWriter.close();

String expected = expectedStringWriter.toString();
assertEquals(expected, outContent.toString());

or create an own assert class to re-use it

class MyAssert {

    public static void assertLinesEqual(String expectedString, String actualString){
        BufferedReader expectedLinesReader = new BufferedReader(new StringReader(expectedString));
        BufferedReader actualLinesReader = new BufferedReader(new StringReader(actualString));

        try {
            int lineNumber = 0;

            String actualLine;
            while((actualLine = actualLinesReader.readLine()) != null){
                String expectedLine = expectedLinesReader.readLine();
                Assert.assertEquals("Line " + lineNumber, expectedLine, actualLine);
                lineNumber++;
            }

            if(expectedLinesReader.readLine() != null){
                Assert.fail("Actual string does not contain all expected lines");
            }
        } catch (IOException e) {
            Assert.fail(e.getMessage());
        } finally {
            try {
                expectedLinesReader.close();
            } catch (IOException e) {
                Assert.fail(e.getMessage());
            }
            try {
                actualLinesReader.close();
            } catch (IOException e) {
                Assert.fail(e.getMessage());
            }
        }
    }
}

Then you can give a better problem description if the test fails. E.g.

MyAssert.assertLinesEqual(
         "Quack\nI'm flying!!\nI can not fly\nI'm flying with a rocket\n",
         outContent.toString());

will output

org.junit.ComparisonFailure: Line 2 
Expected :I can not fly
Actual   :I can't fly



回答2:


So far all the other answers are technically correct; but still they are don't mention one core thing: it is simply bad practice to capture stdout/stderr and then do exact string matching; assuming that your class writes there.

Even more when you are testing multiple method calls, and expect that some final output "proves" that all the methods were called, and so on.

So, yes, theoretically you can do things this way (for training/learning purposes); but the "pattern" that you are using here is simply spoken "bad practice". What happens when you later decide to remove those print statements (because, well, production code doesn't do print statements). Then all your tests render worthless. Or when some other user changes one character in the strings going to stdout?

Thus: the real answer is to step back and consider what the real "side effects" could be that these method calls will cause; and look for better ways to verify them.




回答3:


Try using \r\n instead of \n.

assertEquals("Quack\r\nI'm flying!!\r\nI can't fly\r\nI'm flying with a rocket", outContent.toString().trim());



回答4:


Could it be, that you are on a Windows system and need to check for \r\n instead of just \n



来源:https://stackoverflow.com/questions/41674408/java-test-system-output-including-new-lines-with-assertequals

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!