2011-10-09 76 views
1

我正在使用JUnit框架为使用Java的某种编程语言编写解释器测试。为此,我创建了大量测试用例,其中大部分都包含测试语言的代码片段。由于这些代码片段通常很小,因此将它们嵌入Java代码很方便。然而,Java不支持多行字符串文字,这使得代码片段有点晦涩由于转义序列和必要性分裂更长的字符串字面量,例如:如何使用JUnit测试解释器?

String output = run("let a := 21;\n" + 
        "let b := 21;\n" + 
        "print a + b;"); 
assertEquals(output, "42"); 

理想我想是这样的:

String output = run(""" 
    let a := 21; 
    let b := 21; 
    print a + b; 
"""); 
assertEquals(output, "42"); 

一个可能的解决方案是将代码片段移动到外部文件并引用相应测试用例中的每个文件。但是这增加了很大的维护负担。

另一种解决方案是使用不同的JVM语言(例如支持多行字符串的Scala或Jython)编写测试。这将为项目添加一个新的依赖项,并需要移植现有的测试。

有没有其他的方式来保持测试代码片段的清晰度,同时不增加太多的维护?

+0

请编辑这个问题,因为它不是关于解释器,而是关于多行字符串。 – rightfold

回答

1

移动测试用例到一个文件在过去的工作对我来说,这是一个解释器,以及:

  1. 创建一个XML文件,方含的片段来解释,以及预期的结果。这是一个相当简单的XML定义,主要包含testID,value,expected result,typedescription的测试元素列表。我们用testIDdescription来记录失败的测试。

它主要是因为我们有一个类似于run方法的通用定义良好的接口,因此重构仍然是可能的。在我们的例子中,这不会增加维护工作量,事实上,我们可以通过向XML文件添加更多元素来轻松创建新测试。

也许这不是单元测试应该使用的最佳方式,但它对我们来说效果很好。

+0

非常感谢您分享您的体验。我想过类似的方法,但似乎只有一个加载并执行所有事情的JUnit测试会使调试更加困难。当你有许多JUnit测试用例时,你可以在失败的测试用例中放置一个断点,并使用IDE轻松地重新执行它。但是你怎么用一个测试用例来做呢? – vitaut

+0

@vitaut:你说得对,那有点笨拙。我们通过定义1或2个由循环方法调用的内部方法来以编程方式解决这个问题。如果我没有记错,我们还为XML内的每个测试用例设置了多个XML文件和一个开关,以启用/禁用某些测试 - 因此,您可以手动将测试执行减少到一种情况,或者只需复制所需的测试值到另一个测试类中,并在需要调试器时从您的IDE手动执行它。这是3或4年前,所以还没有给出使用其他语言的选项。 – home

+1

@vitaut,你可以使用的一个小诀窍是添加一个标记字符(如感叹号)作为要调试的测试的名称/标题的第一个字符,并在循环中添加几行代码,如'if(name .startsWith(“!”)){log.debug(“Debugging”+ name); }'并将断点放在日志行上。这样你就可以通过添加/删除一个'!'来选择要调试哪个测试。 – rsp

1

既然您在谈论其他JVM语言,那么您是否考虑过Groovy?您将不得不添加外部依赖项,但仅在编译/测试时(您不必将其放在生产包中),并提供多行字符串。在你的情况中有一个主要优点:它的语法向后兼容Java(这意味着你不必重写你的测试)!

1

我已经完成了这个过去。我做了类似于家中建议的事情,我使用了包含测试及其预期结果的外部文件,但使用@Parameterized测试运行器。

@RunWith(Parameterized.class) 
public class ParameterTest { 
    @Parameters 
    public static List<Object[]> data() { 
     List<Object[]> list = new LinkedList<Object[]>(); 
     for (File file : new File("/temp").listFiles()) { 
      list.add(new Object[]{file.getAbsolutePath(), readFile(file)}); 
     } 

     return list; 
    } 

    private static String readFile(File file) { 
     // read file 
     return "file contents"; 
    } 

    private String filename; 
    private String contents; 

    public ParameterTest(String filename, String contents) { 
     this.filename = filename; 
     this.contents = contents; 
    } 

    @Test 
    public void test1() { 
     // here we test something 
    } 

    @Test 
    public void test2() { 
     // here we test something 
    } 
} 

在这里,我们在/ TEMP每个文件运行test1() & test2()一次,文件名的参数和文件的内容。测试类将被实例化,并为您添加到用@Parameters注释的方法列表中的每个项目进行调用。

使用此测试运行器,如果失败,可以重新运行特定的文件;大多数IDE支持重新运行单个失败的测试。 @Parameterized的缺点是没有任何方法可以明智地识别测试,以便名称出现在Eclipse JUnit插件中。所有你得到的是0,1,2等,但至少你可以重新运行失败的测试。

就像家里说的那样,良好的日志记录功能对于正确识别失败的测试以及帮助调试尤其是在IDE外运行时非常重要。