2011-10-09 69 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外運行時非常重要。