将TDD应用于XML解析器项目时遇到问题

时间:2014-02-05 15:26:50

标签: java xml unit-testing parsing tdd

我知道通常你应该做一个失败的测试然后编写代码以使测试在实际编码项目之前失败但我们做了倒退。测试是新的,但仍然很难为我们的小组写。我们的XMLParser类完全正常工作,XMLPaserTester类有一个硬编码的xml字符串,因为每次运行解析器检索3个最新的wiki页面更新时,我们测试的内容都会发生变化。

public class XMLParser {

private WikipediaConnection wC= new WikipediaConnection();
private String XML;
private String[] XMLArray;
private String firstRecentChange;
private String secondRecentChange;
private String thirdRecentChange;

private void findNecessaryXMLPart()
{
    try {
        XML = wC.makeXMLFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
    XMLArray = XML.split("recentchanges>");
    XML = XMLArray[1];
}

private String findTitle(String string, int titleNumber)
{
    String[] titleSplit =  XML.split("title=\"");
    String[] titleSplitAgain = titleSplit[titleNumber].split("\" timestamp=\"");

    return titleSplitAgain[0];
}

private Date findTimestamp(String string, int timestampNumber)
{
    String[] timestampSplit = XML.split("timestamp=\"");
    String[] timestampSplitAgain = timestampSplit[timestampNumber].split("\" comment=\"");

    return timeConverter(timestampSplitAgain[0]);
}
private String findComment(String string, int commentNumber)
{
    String[] commentSplit = XML.split("comment=\"");
    String[] commentSplitAgain = commentSplit[commentNumber].split("/><");

    return "\"" + commentSplitAgain[0];
}

private Date timeConverter(String string)
{
    String[] dateAndTime = string.split("T");
    String date = dateAndTime[0];
    String time = dateAndTime[1];

    DateTimeFormatter formatOfISOFormat=DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:SSZ");
    Date recentChange = formatOfISOFormat.parseDateTime(date + "T" + time)
                                                    .toDate();
    return recentChange;
}

private String stringConstructor(String string, int number)
{
    String title = findTitle(string, number);
    Date timestamp = findTimestamp(string, number);
    String comment = findComment(string, number);

    String constructedString = title + " was modified on " + timestamp + ". The comment is "
            + comment + ".";
    return constructedString;
}
public String toString()
{
    findNecessaryXMLPart();

    firstRecentChange = stringConstructor(firstRecentChange, 1);
    secondRecentChange = stringConstructor(secondRecentChange, 2);
    thirdRecentChange = stringConstructor(thirdRecentChange, 3);

    return firstRecentChange + "\n" + secondRecentChange + 
            "\n" + thirdRecentChange;
}
}

我计划制作一个测试案例来捕捉标题,时间和评论,只是弄清楚1同样的概念应该能够应用到另一个2.当我运行测试时,它会变回红色,给我错误预期[] Kasumi(死或活)实际[title =“] Kasumi(死或活)

public class XMLParserTester{

private static String XMLRecentChangesString = "<?xml version=\"1.0\"?><api><query-continue><recentchanges rccontinue=\"2014-02-03T13:40:21Z|634981610\" /></query-continue><query><recentchanges><rc type=\"edit\" ns=\"0\" title=\"Kasumi (Dead or Alive)\" timestamp=\"2014-02-03T13:40:24Z\" comment=\"/* Critical acclaim and popularity */\" /><rc type=\"edit\" ns=\"0\" title=\"Comparison of file archivers\" timestamp=\"2014-02-03T13:40:23Z\" comment=\"/* General information */ removed preview releases from 7-zip &amp; freearc &amp; KGB\" /><rc type=\"edit\" ns=\"0\" title=\"Colin Firth on stage and screen\" timestamp=\"2014-02-03T13:40:21Z\" comment=\"\" /></recentchanges></query></api>";
private String[] XMLArray;



@Test
public void testTitle(){
    XMLArray = XMLRecentChangesString.split("ns=\"0\"");
    XMLRecentChangesString = XMLArray[1];
    String[] titleSplit =  XMLRecentChangesString.split("title=\"\"");
    int titleNumber = 0;
    String[] titleSplitAgain = titleSplit[titleNumber].split("\" timestamp=\"");
    Assert.assertEquals("Kasumi (Dead or Alive)", titleSplitAgain[0]);
}
}

2 个答案:

答案 0 :(得分:0)

您正在将StringString[](数组)进行比较。假设期望的字符串位于数组的第一个位置,您可以写:

Assert.assertEquals("Kasumi (Dead or Alive)", titleSplit[0]);

修改

这是我要做的。我首先要改变方法以使其更易于测试(它不必是静态的,但因为我已经删除了对类的依赖性并声明它可以是静态的,所以让我们这样做):

//@VisibleForTesting is a guava annotation that makes your intention clear
//it is obviously not required

@VisibleForTesting
static String findTitle(String xml, int titleNumber) {
    String[] titleSplit =  xml.split("title=\"");
    String[] titleSplitAgain = titleSplit[titleNumber].split("\" timestamp=\"");

    return titleSplitAgain[0];
}

你的测试看起来像:

String xml = "<?xml version=\"1.0\"?> ...";
assertEquals("Kasumi (Dead or Alive)", XMLParser, findTitle(xml, 0));

答案 1 :(得分:0)

单元测试用于测试某些类提供的功能,总是通过这个类的公共方法,在你的测试中,你正在做类似于复制生产类中的代码的东西,这不是很有用,例如:

 public void
 test_can_get_the_3_most_recent_changes_in_wikipedia() {
    XMLParser parser = new XMLParser() // object under test

    assertEquals(parser, "....") // compare the result of parser with the string you expect

 }

这个测试揭示了你班级设计中的一些问题(这是TDD的真正好处!):

  • 使用toString()返回3个最近的更改,它真的很奇怪,为什么不是一个返回列表的方法“mostRecentChanges”??
  • 这一行:   private WikipediaConnection wC = new WikipediaConnection(); 您的课程与“wikipediaConnection”强烈相关,如果没有与维基百科的真正联系,您无法单独测试此课程。您违反了SOLID规则之一:依赖注入,类必须取决于抽象而不是具体类。

使用此更改进行更好的测试:

 public void
 test_can_get_the_3_most_recent_changes_in_wikipedia() {

    WikipediaConnection mockConnection = mock(WikepediaConnection.class);
    when(mockConnection.makeXMLFile()).thenReturn("your test xml"); // use mockito

    XMLParser parser = new XMLParser(mockConnection) // inject the dependency in constructor

    assertThat(parser.mostRecentChanges(), allOf(
       contains("one_change_you_expect"),
       contains("the_sencond..."),
       contains("the_third")
    ); // use hamcrest for clear test expectations

 }

这是对您的XMLParser的一个很好的测试,通过公共接口测试您的类实际功能并帮助您识别设计中的问题(测试仍然会发现一些问题,例如名称XMLParser,因为它也是一个坏名字通用)。

首次尝试测试时,我认为您的团队需要一些帮助,阅读一些好书(例如通过示例获得tdd或通过测试引导面向对象)或聘请具有TDD实际经验的人员。 / p>