簡體   English   中英

如何使用 MSpec 有效地測試固定長度的平面文件解析器?

[英]How to effectively test a fixed length flat file parser using MSpec?

我有這個方法簽名: List<ITMData> Parse(string[] lines)

ITMData有 35 個屬性。

您將如何有效地測試這樣的解析器?

問題:

  • 我應該加載整個文件(我可以使用 System.IO)嗎?
  • 我應該將文件中的一行放入字符串常量中嗎?
  • 我應該測試一條或多條線
  • 我應該測試 ITMData 的每個屬性還是應該測試整個 object?
  • 我的測試命名如何?

編輯

我將方法簽名更改為ITMData Parse(string line)

測試代碼:

[Subject(typeof(ITMFileParser))]
public class When_parsing_from_index_59_to_79
{
    private const string Line = ".........";
    private static ITMFileParser _parser;
    private static ITMData _data;

    private Establish context = () => { _parser = new ITMFileParser(); };

    private Because of = () => { _data = _parser.Parse(Line); };

    private It should_get_fldName = () => _data.FldName.ShouldBeEqualIgnoringCase("HUMMELDUMM");
}

編輯 2

我仍然不確定是否應該只測試每個 class 的一個屬性 在我看來,這讓我可以為規范提供更多信息,即當我解析從索引 59 到索引 79 的單行時,我得到了 fldName。 如果我測試一個 class 中的所有屬性,我會丟失此信息 我是否過度指定了我的測試?

我的測試現在看起來像這樣:

[Subject(typeof(ITMFileParser))]
public class When_parsing_single_line_from_ITM_file
{
    const string Line = ""

    static ITMFileParser _parser;
    static ITMData _data;

    Establish context = () => { _parser = new ITMFileParser(); };

    private Because of = () => { _data = _parser.Parse(Line); };

    It should_get_fld??? = () => _data.Fld???.ShouldEqual(???);
    It should_get_fld??? = () => _data.Fld???.ShouldEqual(???);
    It should_get_fld??? = () => _data.Fld???.ShouldEqual(???);
    It should_get_fld??? = () => _data.Fld???.ShouldEqual(???);
    It should_get_fld??? = () => _data.Fld???.ShouldEqual(???);
    It should_get_fld??? = () => _data.Fld???.ShouldEqual(???);
    It should_get_fld??? = () => _data.Fld???.ShouldEqual(???);
    ...

}

我應該加載整個文件(我可以使用 System.IO)嗎?

如果你這樣做,它就不再是一個單元測試——它變成了一個集成或回歸測試。 如果您希望它顯示單元測試不會顯示的可能錯誤,您可以這樣做。 但這不太可能。

至少在開始時,您可能最好使用單元測試。

我應該將文件中的一行放入字符串常量中嗎?

如果您打算編寫多個使用相同輸入行的測試,那么當然可以。 但就個人而言,我可能傾向於編寫一堆不同的測試,每個測試都傳遞不同的輸入字符串。 那時,沒有太多理由制作一個常量(除非它是一個局部常量,在測試方法中聲明)。

我應該測試一條或多條線嗎?

您沒有指定,但我假設您的 output 與您的輸入是一對一的——也就是說,如果您傳入三個字符串,您將返回三個ITMData 在這種情況下,對多線測試的需求將受到限制。

幾乎總是值得測試退化的情況,在這種情況下它將是一個空字符串數組(零行)。 並且可能值得至少進行一次包含多行的測試,這樣您就可以確保迭代中沒有愚蠢的錯誤。

但是,如果您的 output 與您的輸入是一對一的,那么您確實有另一種方法想要退出——您應該有一個ParseSingleLine方法。 那么你的Parse只不過是迭代行並調用ParseSingleLine 您仍然需要對 Parse 進行少量測試,但您的大部分測試將集中在ParseSingleLine

如果我遇到這樣的問題,我通常會這樣做:

提前一個簡短的免責聲明:我想我會更多 go 沿着“集成測試”或“作為一個整體測試解析器”路線而不是測試單個行。 在過去,我不止一次遇到過很多實現細節泄漏到我的測試中的情況,當我更改實現細節時,我不得不經常更改測試。 我猜是超規格的典型案例;-/

  1. 我不會在解析器中包含文件加載。 正如@mquander 所建議的那樣,我寧願 go 使用 TextReader 或 IEnumerable 作為輸入參數。 這將導致更快的測試,因為您可以在內存中指定解析器輸入並且不必接觸文件系統。
  2. 我不是手動滾動測試數據的忠實擁護者,因此在大多數情況下,我使用嵌入式資源和 ResourceManager 通過 assembly.GetManifestResource() 直接從規范程序集中加載測試數據。 我的解決方案中通常有一堆擴展方法來簡化資源的讀取(例如 TextReader TextResource.Load("NAME_OF_SOME_RESOURCE"))。
  3. 關於 MSpec:我在每個文件中使用一個 class 來解析。 對於在解析結果中測試的每個屬性,我都有一個單獨的 (It) 斷言。 這些通常是一個襯里,因此額外的編碼量並不大。 在文檔和診斷方面,恕我直言,這是一個巨大的優勢,因為當沒有正確解析屬性時,您可以直接看到哪個斷言失敗,而無需查看源代碼或搜索行號。 它也出現在您的 MSpec 結果文件中。 此外,您不會隱藏其他失敗的斷言(您修復一個斷言只是為了看到規范在下一行與下一個斷言失敗的情況)。 這當然會迫使您更多地考慮您在規范中使用的措辭,但對我來說這也是一個巨大的優勢,因為我是語言 forms 思維的支持者。 換句話說,如果您不知道如何為您的斷言命名,那么您的規范或實現可能有些可疑。
  4. 關於解析器的方法簽名:我不會返回像 List<T> 或數組這樣的具體類型,我也建議不要返回可變的 List<T> 類型。 你在這里基本上說的是:“嘿,我完成后你可以亂搞解析結果”,這在大多數情況下可能是你不想要的。 我建議改為返回 IEnumerable<T> (或 ICollection<T> 如果您以后真的需要修改它)

我通常會嘗試考慮常見的成功和失敗場景,以及邊緣情況。 需求也有助於設置適當的用例。 考慮使用Pex枚舉各種場景。

關於您的新問題:

我應該測試 ITMData 的每個屬性還是應該測試整個 object?

如果您想安全起見,您可能應該至少有一個測試來檢查每個屬性是否匹配。

我的測試命名如何?

關於這個話題有很多討論,比如這個 一般規則是在單元測試 class 中有多種方法,每種方法都旨在測試特定的東西。 在你的情況下,它可能是這樣的:

public void Check_All_Properties_Parsed_Correctly(){.....}

public void Exception_Thrown_If_Lines_Is_Null(){.....}

public void Exception_Thrown_If_Lines_Is_Wrong_Length(){.....}

因此,換句話說,測試您認為解析器“正確”的確切行為。 完成此操作后,您在更改解析器代碼時會感到更加輕松,因為您將擁有一個全面的測試套件來檢查您沒有破壞任何東西。 記住要經常進行實際測試,並在進行更改時保持測試更新! MSDN上有一個關於單元測試和測試驅動開發的相當好的指南。

一般來說,我認為您可以通過谷歌搜索找到大多數問題的答案。 還有幾本關於測試驅動開發的優秀書籍,它們不僅會帶您了解TDD 的原理,還可以帶您了解為什么 如果您相對編程語言不可知,我會推薦 Kent Beck 的Test Driven Development By Example ,否則就像Microsoft .NET 中的 Test-Driven Development 這些應該會讓你很快走上正確的軌道。

編輯:

我是否過度指定了我的測試?

在我看來,是的。 具體來說,我不同意你的下一行:

如果我測試一個 class 中的所有屬性,我會丟失此信息。

您究竟以何種方式丟失信息? 假設有兩種方法可以進行此測試,除了每次測試都有一個新的 class :

  1. 每個屬性都有不同的方法 您的測試方法可以稱為CheckPropertyXCheckPropertyY等。當您運行測試時,您將確切地看到哪些字段通過了,哪些字段失敗了。 這顯然滿足了您的要求,盡管我會說這仍然是矯枉過正。 我會 go 與選項 2:
  2. 有幾種不同的方法,每種方法都測試一個特定的方面。 這是我最初推薦的,我想你指的是什么。 當其中一個測試失敗時,您只會獲得有關每個方法失敗的第一件事的信息,但是如果您很好地編寫了 Assert 代碼,您將確切地知道哪個屬性不正確。 考慮以下代碼:

Assert.AreEqual("test1", myObject.PropertyX, "Property X was incorrectly parsed"); Assert.AreEqual("test2", myObject.PropertyY, "Property Y was incorrectly parsed");

當其中一個失敗時,您將知道哪條線失敗了。 修復相關錯誤並重新運行測試后,您將查看是否有任何其他屬性失敗。 這通常是大多數人采用的方法,因為創建 class 甚至每個屬性的方法會導致代碼過多,並且需要進行太多工作來保持最新狀態。

暫無
暫無

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

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