簡體   English   中英

我應該如何對代碼生成器進行單元測試?

[英]How should I unit test a code-generator?

這是一個我知道的難題和開放式問題,但我想我會把它扔到地板上,看看是否有人有任何有趣的建議。

我開發了一個代碼生成器,它將我們的python接口帶到我們的C ++代碼(通過SWIG生成),並生成將其公開為WebServices所需的代碼。 當我開發這個代碼時,我使用TDD做到了,但我發現我的測試很脆弱。 因為每個測試本質上都想驗證對於給定位的輸入代碼(恰好是C ++標頭)我會得到一個給定的輸出代碼,我寫了一個小引擎,從XML輸入文件中讀取測試定義並生成測試來自這些期望的案例。

問題是我害怕進入修改代碼。 那個單元測試自己的事實是:復雜的,b:脆弱的。

因此,我正在嘗試考慮解決這個問題的替代方法,並且讓我感到震驚,我可能會以錯誤的方式解決它。 也許我需要更多地關注結果,IE:我生成的代碼實際運行並執行我想要的代碼,而不是代碼看起來像我想要的那樣。

有沒有人有任何類似於他們想要分享的東西的經歷?

我開始用我自己的代碼生成器編寫我的經驗總結,然后回過頭來重新閱讀你的問題,發現你自己已經觸及了同樣的問題,專注於執行結果而不是代碼布局/外觀。

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

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

回想一下,“單元測試”只是一種測試。 您應該能夠對代碼生成器的內部部分進行單元測試。 你在這里真正看到的是系統級測試(又名回歸測試)。 這不僅僅是語義......有不同的思維模式,方法,期望等等。這肯定是更多的工作,但你可能需要咬緊牙關並建立一個端到端的回歸測試套件:修復C ++文件 - > SWIG接口 - > python模塊 - >已知輸出。 你真的想根據預期的輸出檢查已知的輸入(固定的C ++代碼)(最終的Python程序出來的)。 直接檢查代碼生成器結果就像區分對象文件一樣......

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

如果您在* nux上運行,您可以考慮轉儲unittest框架以支持bash腳本或makefile。 在Windows上,您可以考慮構建一個運行生成器的shell應用程序/函數,然后使用代碼(作為另一個進程)和unittest。

第三種選擇是生成代碼,然后從中構建一個除了單元測試之外的應用程序。 你需要一個shell腳本或者其他什么來為每個輸入運行它。 至於如何編碼預期的行為,我發現它可以用與使用生成的接口而不是C ++的C ++代碼完全相同的方式完成。

單元測試只是測試特定單元。 因此,如果您正在為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進行單元測試,而不需要任何其他系統執行實際功能。 這是單元測試。

這是一種從創建到完成接近系統的聰明方式,每種行為都有不同的When規范:

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

總而言之,單個單元/規范可以具有多種行為,並且隨着您開發單元/系統,規范也會增長; 如果您的被測系統依賴於其中的其他具體系統,請注意。

我發現你需要測試生成的內容而不是生成內容。

在我的例子中,程序生成許多類型的代碼(C#,HTML,SCSS,JS等),這些代碼可以編譯成Web應用程序。 我發現減少整體回歸錯誤的最好方法是測試Web應用程序本身,而不是測試生成器。

不要誤解我的意思,仍然有單元測試檢查一些生成器代碼,但我們最大的幫助就是生成的應用程序本身的UI測試。

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

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

這很甜蜜。

祝好運!

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

基本上,你真正想知道的是你的發電機是否會產生你所期望的,而無需對每種可能的組合進行物理測試(也是:不可能)。 通過確保您的發電機以您期望的方式保持一致,您可以感覺更好,發電機將在更復雜的情況下取得成功。

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

只是想指出在驗證結果時仍然可以實現細粒度測試:您可以通過將它們嵌入到一些設置和驗證代碼中來測試各個代碼塊:

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

如果您從較小的塊組裝生成的代碼,並且塊不經常更改,您可以運行更多條件並進行更好的測試,並且希望避免在更改一個塊的細節時讓所有測試中斷。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM