Сравнение текстовых файлов с Junit

Я сравниваю текстовые файлы в junit, используя:

public static void assertReaders(BufferedReader expected,
          BufferedReader actual) throws IOException {
    String line;
    while ((line = expected.readLine()) != null) {
        assertEquals(line, actual.readLine());
    }

    assertNull("Actual had more lines then the expected.", actual.readLine());
    assertNull("Expected had more lines then the actual.", expected.readLine());
}

Это хороший способ сравнить текстовые файлы? Что предпочтительнее?


person jon077    schedule 21.01.2009    source источник
comment
Я голосую за решение, которое у вас есть в вопросе. Поскольку это более старый пост, надстройки JUnit устарели (вероятно, несовместимы с JUnit 4), мне не нравятся Apache Utils, и, учитывая, насколько лаконичен ваш метод assertReaders, я не вижу оправдания для включения совершенно новой библиотеки коммунальных услуг. Ваш сработал сразу - спасибо!   -  person PMorganCA    schedule 17.04.2020


Ответы (9)


junit-addons имеет хорошую поддержку: FileAssert

Это дает вам исключения, такие как:

junitx.framework.ComparisonFailure: aa Line [3] expected: [b] but was:[a]
person IAdapter    schedule 21.01.2009
comment
Просто обратите внимание, что последняя версия центрального репозитория vin maven относится к 2003 году и версии 1.4, поэтому я не знаю, совместима ли она с последними версиями. - person kohlerfc; 20.11.2015

Вот один простой способ проверить, являются ли файлы полностью одинаковыми:

assertEquals("The files differ!", 
    FileUtils.readFileToString(file1, "utf-8"), 
    FileUtils.readFileToString(file2, "utf-8"));

Где file1 и file2 — это File экземпляра, а FileUtils< /a> взято из Apache Commons IO.

Не так много собственного кода для поддержки, что всегда является плюсом. :) И очень просто, если вы уже используете Apache Commons в своем проекте. Но нет хороших, подробных сообщений об ошибках, как в решении Марка.

Изменить:
Хех, присмотревшись к FileUtils API, можно увидеть даже более простой способ:

assertTrue("The files differ!", FileUtils.contentEquals(file1, file2));

В качестве бонуса эта версия работает со всеми файлами, а не только с текстом.

person Jonik    schedule 21.01.2009
comment
Форма assertTrue лаконична, но относительно бесполезна в случае сбоя. По крайней мере, метод assertEquals покажет вам несколько символов, в которых они отличаются. - person Stephen; 10.06.2010
comment
Обновление: сейчас я бы рекомендовал Google Guava вместо Commons IO для чтения файлов в виде строки: Files.toString(file1, Charset.forName("UTF-8")); В таком случае нет большой разницы, но в целом Guava является более чистой, лучше документированной и активно поддерживаемой библиотекой. - person Jonik; 18.07.2011
comment
А начиная с Java 7 вы можете читать текстовый файл как строку без каких-либо внешних библиотек: new String(Files.readAllBytes(Paths.get("/path/to/file.txt")), StandardCharsets.UTF_8) - person Jonik; 24.12.2015

Вот более полный список компараторов файлов в различных сторонних библиотеках Java:

person Scott Langley    schedule 09.06.2010
comment
Милый обзор! Избавил меня от веб-поиска, если у весны что-то есть;) - person JBA; 14.04.2015

С 2015 года я бы рекомендовал AssertJ, элегантную и всеобъемлющую библиотеку утверждений. Для файлов вы можете утверждать против другого файла:

@Test
public void file() {
    File actualFile = new File("actual.txt");
    File expectedFile = new File("expected.txt");
    assertThat(actualFile).hasSameContentAs(expectedFile);
}

или против встроенных строк:

@Test
public void inline() {
    File actualFile = new File("actual.txt");
    assertThat(linesOf(actualFile)).containsExactly(
            "foo 1",
            "foo 2",
            "foo 3"
    );
}

Сообщения об ошибках также очень информативны. Если строка отличается, вы получите:

java.lang.AssertionError: 
File:
  <actual.txt>
and file:
  <expected.txt>
do not have equal content:
line:<2>, 
Expected :foo 2
Actual   :foo 20

и если в одном из файлов больше строк, вы получите:

java.lang.AssertionError:
File:
  <actual.txt>
and file:
  <expected.txt>
do not have equal content:
line:<4>,
Expected :EOF
Actual   :foo 4
person Bogdan Calmac    schedule 15.03.2015
comment
Метод hasContentEqualTo устарел с момента этого комментария. Вместо этого используйте hasSameContentAs. - person Stephan; 01.02.2017

Простое сравнение содержимого двух файлов с помощью java.nio.file API.

byte[] file1Bytes = Files.readAllBytes(Paths.get("Path to File 1"));
byte[] file2Bytes = Files.readAllBytes(Paths.get("Path to File 2"));

String file1 = new String(file1Bytes, StandardCharsets.UTF_8);
String file2 = new String(file2Bytes, StandardCharsets.UTF_8);

assertEquals("The content in the strings should match", file1, file2);

Или, если вы хотите сравнить отдельные строки:

List<String> file1 = Files.readAllLines(Paths.get("Path to File 1"));
List<String> file2 = Files.readAllLines(Paths.get("Path to File 2"));

assertEquals(file1.size(), file2.size());

for(int i = 0; i < file1.size(); i++) {
   System.out.println("Comparing line: " + i)
   assertEquals(file1.get(i), file2.get(i));
}
person Jonathan Andersson    schedule 30.03.2016

Я бы предложил использовать Assert.assertThat и сопоставитель hamcrest (junit 4.5 или новее - возможно, даже 4.4).

Я бы закончил с чем-то вроде:

assertThat(fileUnderTest, containsExactText(expectedFile));

где мой матчер:

class FileMatcher {
   static Matcher<File> containsExactText(File expectedFile){
      return new TypeSafeMatcher<File>(){
         String failure;
         public boolean matchesSafely(File underTest){
            //create readers for each/convert to strings
            //Your implementation here, something like:
              String line;
              while ((line = expected.readLine()) != null) {
                 Matcher<?> equalsMatcher = CoreMatchers.equalTo(line);
                 String actualLine = actual.readLine();
                 if (!equalsMatcher.matches(actualLine){
                    failure = equalsMatcher.describeFailure(actualLine);
                    return false;
                 }
              }
              //record failures for uneven lines
         }

         public String describeFailure(File underTest);
             return failure;
         }
      }
   }
}

Плюсы матчера:

  • Состав и повторное использование
  • Use in normal code as well as test
    • Collections
    • Используется в фиктивных фреймворках
    • Может использоваться общая функция предиката
  • Действительно хорошая лог-способность
  • Можно комбинировать с другими сопоставителями, а описания и описания отказов точны и точны.

Минусы:

  • Ну, это довольно очевидно, верно? Это более многословно, чем assert или junitx (в данном конкретном случае).
  • Вам, вероятно, потребуется включить библиотеки hamcrest, чтобы получить максимальную пользу
person Stephen    schedule 21.01.2009

FileUtils конечно хороший. Вот еще один простой подход для проверки того, совпадают ли файлы.

assertEquals(FileUtils.checksumCRC32(file1), FileUtils.checksumCRC32(file2));

Хотя assertEquals() обеспечивает немного больше обратной связи, чем assertTrue(), результат checksumCRC32() является длинным. Таким образом, это может быть не очень полезно.

person Mookie Wilson    schedule 13.01.2011
comment
+1, я думаю, это может пригодиться для действительно больших файлов (когда вас заботит только то, различаются ли файлы, а не в чем разница) - person Jonik; 26.07.2011

Если в ожидаемом больше строк, чем в фактическом, вы не получите assertEquals, прежде чем позже перейдете к assertNull.

Хотя это довольно легко исправить:

public static void assertReaders(BufferedReader expected,
    BufferedReader actual) throws IOException {
  String expectedLine;
  while ((expectedLine = expected.readLine()) != null) {
    String actualLine = actual.readLine();
    assertNotNull("Expected had more lines then the actual.", actualLine);
    assertEquals(expectedLine, actualLine);
  }
  assertNull("Actual had more lines then the expected.", actual.readLine());
}
person Jon Skeet    schedule 21.01.2009
comment
Мне нравится, что ваш ответ не зависит от сторонних библиотек, но этот код не скомпилируется. Область действия переменной «actual» ограничена циклом while, поэтому последняя строка assertNull не будет компилироваться. - person buzz3791; 01.02.2014
comment
@buzz3791: Нет, область действия actualLine ограничена циклом while. Областью действия actual является весь метод. - person Jon Skeet; 01.02.2014

Это моя собственная реализация equalFiles, не нужно добавлять какую-либо библиотеку в ваш проект.

private static boolean equalFiles(String expectedFileName,
        String resultFileName) {
    boolean equal;
    BufferedReader bExp;
    BufferedReader bRes;
    String expLine ;
    String resLine ;

    equal = false;
    bExp = null ;
    bRes = null ;

    try {
        bExp = new BufferedReader(new FileReader(expectedFileName));
        bRes = new BufferedReader(new FileReader(resultFileName));

        if ((bExp != null) && (bRes != null)) {
            expLine = bExp.readLine() ;
            resLine = bRes.readLine() ;

            equal = ((expLine == null) && (resLine == null)) || ((expLine != null) && expLine.equals(resLine)) ;

            while(equal && expLine != null)
            {
                expLine = bExp.readLine() ;
                resLine = bRes.readLine() ; 
                equal = expLine.equals(resLine) ;
            }
        }
    } catch (Exception e) {

    } finally {
        try {
            if (bExp != null) {
                bExp.close();
            }
            if (bRes != null) {
                bRes.close();
            }
        } catch (Exception e) {
        }

    }

    return equal;

}

И чтобы использовать его, просто используйте обычный метод AssertTrue JUnit.

assertTrue(equalFiles(expected, output)) ;
person Mauricio Gracia Gutierrez    schedule 07.06.2015