Thursday, March 24, 2011

Comparing text files w/ Junit

I am comparing text files in junit using:

  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());
  }

Is this a good way to compare text files? What is preferred?

From stackoverflow
  • If expected has more lines than actual, you'll fail an assertEquals before getting to the assertNull later.

    It's fairly easy to fix though:

    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());
    

    }

  • junit-addons(http://sourceforge.net/projects/junit-addons) has nice support for it

    http://junit-addons.sourceforge.net/junitx/framework/FileAssert.html

    it gives you exceptions like junitx.framework.ComparisonFailure: aa Line [3] expected: [b] but was:[a]

  • I'd suggest using Assert.assertThat and a hamcrest matcher (junit 4.5 or later - perhaps even 4.4).

    I'd end up with something like:

    assertThat(fileUnderTest, containsExactText(expectedFile));
    

    where my matcher is:

    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;
             }
          }
       }
    }
    

    Matcher pros:

    • Composition and reuse
    • Use in normal code as well as test
      • Collections
      • Used in mock framework(s)
      • Can be used a general predicate function
    • Really nice log-ability
    • Can be combined with other matchers and descriptions and failure descriptions are accurate and precise

    Cons:

    • Well it's pretty obvious right? This is way more verbose than assert or junitx (for this particular case)
    • You'll probably need to include the hamcrest libs to get the most benefit
  • Here's one simple approach for checking if the files are exactly the same:

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

    Where file1 and file2 are File instances, and FileUtils is from Apache Commons IO.

    Not much own code for you to maintain, which is always a plus. :) And very easy if you already happen to use Apache Commons in your project. But no nice, detailed error messages like in mark's solution.

    Edit:
    Heh, looking closer at the FileUtils API, there's an even simpler way:

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

    As a bonus, this version works for all files, not just text.

    Stephen : The assertTrue form is concise, but relatively useless when it fails. At least the assertEquals method will show you a few characters where they are different
  • Here is a more exhaustive list of File comparator's in various 3rd-party Java libraries:

    www.unitils.org/apidocs/org/unitils/thirdparty/org/apache/commons/io/FileUtils.html#contentEquals%28java.io.File,%20java.io.File%29

    www.dbunit.org/cobertura/org.dbunit.util.FileAsserts.html

    easytesting.org/assert/apidocs/org/fest/assertions/FileAssert.html

    junit-addons.sourceforge.net/junitx/framework/FileAssert.html

    static.springsource.org/spring-batch/apidocs/org/springframework/batch/test/AssertFile.html

    bits.netbeans.org/dev/javadoc/org-netbeans-modules-nbjunit/org/netbeans/junit/NbTestCase.html#assertFile%28java.io.File,%20java.io.File%29

0 comments:

Post a Comment