2008-08-14 41 views
22

這是一個困難的,開放式的問題,我知道,但我想我扔在地上,看看如果任何人有任何有趣的建議。我應該如何測試代碼生成器?

我已經開發出一種碼生成器,其將我們Python接口我們的C++代碼(通過SWIG生成),併產生以暴露此作爲Web服務所需的代碼。當我開發這個代碼時,我使用TDD做了它,但是我發現我的測試很脆弱。因爲每個測試基本上是想驗證的輸入給定碼位(這恰好是一個C++頭)我得到的輸出給定碼位我寫了一個小引擎,從XML輸入文件讀取測試定義和生成測試來自這些期望的案例。

問題是我害怕去修改代碼。這一點以及單元測試自己的事實是:複雜的,而b:脆弱的。

所以我試圖想其他辦法解決這個問題,這讓我覺得我也許解決了錯誤的方式。也許我需要更多地關注結果,IE:我生成的代碼實際上是否運行並執行我想要的代碼,而不是代碼是否按照我希望的方式運行。

有沒有人有過類似的東西,他們會關心分享的任何經驗?

+0

我實際上面臨着同樣的問題,下面的答案都不能令人滿意。當然,您可以單元測試代碼生成器的各個部分。問題是你怎麼知道生成的代碼是正確的,即沒有迴歸或類似的東西,因此如何爲生成的代碼編寫自動化測試(無論它們是否稱爲單元測試或集成測試)? – 2009-11-30 15:58:39

+1

@詹姆斯:那裏沒有簡單的答案......我剛剛重讀了這個問題,當時我的迴應和所有問題都回來了。我可能會在接下來的幾周內再次發佈這款遊戲,因爲我會時不時地遇到各種各樣的迴歸消息,並且發現這些消息越來越關鍵。 – jkp 2009-11-30 21:46:58

回答

12

我開始寫了我有我自己的代碼發生器經驗的總結,然後回去和重新閱讀你的問題,發現你已經在同樣的問題觸動自己,專注於執行結果,而不是代碼佈局/看。

問題是,這是很難測試,生成的代碼可能不適合實際的單元測試系統的環境中運行,你怎麼編碼預期的結果?

我發現你需要將代碼生成器分解成更小的部分並進行單元測試。如果你問我,單元測試完整的代碼生成器更像集成測試,而不是單元測試。

0

是,結果是唯一重要的事情。真正的瑣事是編寫一個框架,允許您生成的代碼獨立運行......在那裏度過您的時間。

0

如果您正在* nux上運行,您可能會考慮轉儲unittest框架以支持bash腳本或makefile。在Windows上,你可能會考慮構建一個運行生成器的shell應用程序/函數,然後使用代碼(作爲另一個進程)並進行單元測試。

第三種選擇是生成代碼,然後從中構建一個應用程序,該應用程序只包含unittest。同樣你需要一個shell腳本或者不需要爲每個輸入運行這個腳本。至於如何對預期行爲進行編碼,我認爲它可以以與您使用生成的接口而非C++代碼的C++代碼幾乎相同的方式完成。

4

回想一下,「單元測試」只是一種測試。你應該能夠單元測試你的代碼生成器的內部部分。你真正在看的是系統級測試(又名迴歸測試)。它不只是語義...有不同的思維方式,方法,期望等這當然更多的工作,但你可能需要咬緊牙關,建立一個終端到終端的迴歸測試套件:固定的C++文件 - >痛飲接口 - > python模塊 - >已知的輸出。你真的想檢查已知的輸入(固定的C++代碼)與期望的輸出(來自最終的Python程序)。直接檢查代碼生成器結果就像差異目標文件一樣...

0

只是想指出的是,你仍然可以實現細粒度的測試,同時驗證結果:通過嵌套他們內部的一些設置和驗證碼,你可以測試的代碼單獨大塊:

int x = 0; 
GENERATED_CODE 
assert(x == 100); 

只要你有您生成的代碼由較小的塊組裝而成,並且塊不會頻繁更改,您可以運行更多條件並進行更好的測試,並且希望避免在更改某個塊的特定情況時使所有測試中斷。

0

單元測試就是測試一個特定的單元。所以,如果你正在編寫一個A類規範,這是理想的,如果A類沒有B和C類的真實具體版本。

好的我注意到後來這個問題的標籤包括C++/Python,但原理是相同的:

public class A : InterfaceA 
    { 
     InterfaceB b; 

     InterfaceC c; 

     public A(InterfaceB b, InterfaceC c) { 
      this._b = b; 
      this._c = c; } 

     public string SomeOperation(string input) 
     { 
      return this._b.SomeOtherOperation(input) 
       + this._c.EvenAnotherOperation(input); 
     } 
    } 

由於上述系統A至噴射系統B和C的接口,就可以進行單元測試只是系統A,而無需真正的功能正在由任何其他系統來執行。這是單元測試。

這裏是從創建接近系統完成,用不同的當規範爲每個片行爲的一個巧妙的方式:

public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification 
{ 
    private string _actualString; 

    private string _expectedString; 

    private string _input; 

    private string _returnB; 

    private string _returnC; 

    [It] 
    public void Should_return_the_expected_string() 
    { 
     _actualString.Should().Be.EqualTo(this._expectedString); 
    } 

    public override void GivenThat() 
    { 
     var randomGenerator = new RandomGenerator(); 
     this._input = randomGenerator.Generate<string>(); 
     this._returnB = randomGenerator.Generate<string>(); 
     this._returnC = randomGenerator.Generate<string>(); 

     Dep<InterfaceB>().Stub(b => b.SomeOtherOperation(_input)) 
         .Return(this._returnB); 
     Dep<InterfaceC>().Stub(c => c.EvenAnotherOperation(_input)) 
         .Return(this._returnC); 

     this._expectedString = this._returnB + this._returnC; 
    } 

    public override void WhenIRun() 
    { 
     this._actualString = Sut.SomeOperation(this._input); 
    } 
} 

所以在最後,單個單元/規範可以有多個行爲,規範隨着您開發單位/系統而增長;如果您的系統受其他系統的影響,請注意。

0

我的建議是找出一組已知的輸入輸出結果,例如您已經擁有的一些更簡單的情況,並且單元測試產生的代碼。完全可能的是,當你改變發生器時,產生的確切字符串可能會略有不同......但你真正關心的是它是否以相同的方式解釋。因此,如果您測試結果的方式是測試該代碼(如果它是您的功能),那麼您會發現它是否以您想要的方式成功。

基本上,你真正想知道的是,如果沒有對每種可能的組合(也是不可能的)進行物理測試,你的發電機是否會產生你所期望的。通過確保您的發電機符合您的期望,您可以更好地感受到發電機將會在更復雜的情況下取得成功。

通過這種方式,您還可以建立一套迴歸測試(單元測試需要保持正常工作)。這將有助於確保對生成器的更改不會破壞其他形式的代碼。當你遇到你的單元測試沒有捕獲到的錯誤時,你可能希望包含它以防止類似的破壞。

0

我發現你需要測試你生成的東西比生成它更多。

在我的情況下,程序生成編譯成Web應用程序的許多類型的代碼(C#,HTML,SCSS,JS等)。我發現減少總體迴歸錯誤的最好方法是測試Web應用程序本身,而不是測試生成器。

不要誤解我的意思,還有單元測試檢查一些生成器代碼,但是我們最大的挑戰就是對生成的應用程序本身進行了UI測試。

由於我們產生它我們還產生一個很好的抽象的JS,我們可以用它來編程測試應用程序。我們遵循這裏概述的一些想法:http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089

最重要的是,它真正從端到端測試系統,從代碼生成到實際生成的內容。一旦測試失敗,很容易將其追溯到發生器損壞的地方。

這是非常甜蜜。

祝你好運!