Java:测试系统输出,包括;“新线路”;用assertEquals

Java:测试系统输出,包括;“新线路”;用assertEquals,java,unit-testing,junit,Java,Unit Testing,Junit,我目前正在为策略设计模式编写一个单元测试。我正在将系统输出与assertEquals方法中的字符串进行比较。输出看起来相同,但我的测试一直失败。我在想我忘记了一些与新行或标签有关的事情了 我的单元测试: import static org.junit.Assert.*; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import org.junit.After; import org.junit.Befor

我目前正在为策略设计模式编写一个单元测试。我正在将系统输出与assertEquals方法中的字符串进行比较。输出看起来相同,但我的测试一直失败。我在想我忘记了一些与新行或标签有关的事情了

我的单元测试:

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());
    }
}
输出(第二行和第三行显示为红色):

编辑:

最快的解决方案似乎是在我的“\n”前面添加一个“\r”。多个答案告诉我需要为Windows执行此操作。应用此选项后,我的assertEquals如下所示:

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

另外:我忘了提到大多数代码来自Eric Freeman、Elisabeth Robson、Bert Bates和Kathy Sierra的《头优先设计模式》一书。

尝试使用\r\n而不是\n

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

如果您正在寻找一种独立于平台的方式,可能是因为您在Windows系统上,需要检查
\r\n
,而不仅仅是
\n

,以及其他答案

一种与平台无关的快速解决方案是更换线路分离器

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());
或者使用
PrintWriter
构建所需的字符串

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());
或者创建一个自己的断言类来重用它

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());
            }
        }
    }
}
然后,如果测试失败,您可以给出更好的问题描述。例如

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

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

到目前为止,所有其他答案在技术上都是正确的;但他们仍然没有提到一个核心问题:捕获stdout/stderr然后进行精确的字符串匹配是一种糟糕的做法假设您的类在那里写作

当您测试多个方法调用,并期望某些最终输出“证明”所有方法都被调用时,情况会更糟,以此类推

因此,是的,理论上你可以这样做(出于培训/学习目的);但是,你在这里使用的“模式”被简单地称为“坏习惯”。当您稍后决定删除这些打印语句时会发生什么情况(因为,好吧,生产代码不执行打印语句)。那么你所有的测试都会变得毫无价值。或者,当其他用户更改字符串中的一个字符时,将转到标准输出


因此,真正的答案是退后一步,考虑这些方法调用会造成什么样的“副作用”;并寻找更好的方法来验证它们

TestCase.equals()
在显示不匹配字符串的错误位置方面做得很好,而不是
Assert.equals()
。只需相应地编辑导入语句。尝试比较字符串的长度。可能是一些人建议的
\r\n
,或者是其他一些看不见的差异。我正在Windows 10中使用Eclipse。解决了它如果您要测试输出,因为您正在开发一个在命令行上运行的应用程序呢?很简单:那么您就有了一个创建消息的组件。这是你测试的。然后,您有一个组件负责获取消息并将其写入某处,例如stdout。这是你测试的。再一次-无需通过读取标准输出来测试消息的布局!想想“关注点分离”和“单一责任原则”!这对于单元测试来说很好,但是如何进行集成测试来证明以下情况:-将一些文本打印到输出缓冲区,说10行-打印一些转义码,如“向上移动”+“清除行”4次-打印3行其他行-如何断言最终输出包含9行最终行(10+-4+3)的文本有趣的一点。我想我会尝试得到一个“mock”来表示所写的东西。准确记录输入内容的东西。
org.junit.ComparisonFailure: Line 2 
Expected :I can not fly
Actual   :I can't fly