簡體   English   中英

pImpl成語和可測試性

[英]The pImpl idiom and Testability

c ++中的pImpl慣用法旨在從該類的用戶隱藏類的實現細節(=私有成員)。 但是它也隱藏了該類的一些依賴關系,從測試的角度來看,這通常被認為是不好的。

例如,如果類A隱藏其類AImpl中的實現細節,只能從A.cpp訪問,並且AImpl依賴於許多其他類,則單元測試類A變得非常困難,因為測試框架無法訪問AImpl也沒辦法將依賴注入AImpl。

有沒有人遇到過這個問題? 你找到了解決方案嗎?

- 編輯 -

在一個相關的主題上,似乎人們建議應該只測試由接口而不是內部公開的公共方法。 雖然我可以在概念上理解該語句,但我經常發現我需要單獨測試私有方法。 例如,當公共方法調用包含一些非平凡邏輯的私有幫助器方法時。

為什么單元測試需要訪問A實現的內部?

單元測試應該是測試A,因此應該只關心A的輸入和輸出。 如果在A的界面中(直接或間接)看不到某些東西,那么它實際上可能根本不需要成為Aimpl的一部分(因為它的結果對於外部世界是不可見的)。

如果Aimpl產生需要測試的副作用,那表明你應該看一下你的設計。

pimpl背后的想法不是隱藏類的實現細節,(私有成員已經這樣做了),而是將實現細節移出標題。 問題是在C ++的包含模型中,更改私有方法/變量將強制重新編譯包含此文件的任何文件。 這是一種痛苦,這就是為什么pimpl試圖消除的原因。 它無助於防止對外部庫的依賴。 其他技術也是如此。

您的單元測試不應該依賴於類的實現。 他們應該驗證你的班級實際上是應該做的。 唯一真正重要的是物體如何與外界相互作用。 您的測試無法檢測到的任何行為必須是對象的內部因素,因此無關緊要。

話雖如此,如果在類的內部實現中發現太多復雜性,您可能希望將該邏輯分解為單獨的對象或函數。 基本上,如果您的內部行為過於復雜而無法間接測試,請將其作為另一個對象的外部行為並對其進行測試。

例如,假設我有一個類,它將一個字符串作為其構造函數的參數。 該字符串實際上是一個小的語言,它指定了對象的一些行為。 (字符串可能來自配置文件或其他東西)。 從理論上講,我應該能夠通過構造不同的對象和檢查行為來測試該字符串的解析。 但如果迷你語言足夠復雜,那將很難。 所以,我定義另一個函數,它接受字符串並返回上下文的表示(如關聯數組或其他東西)。 然后我可以與主對象分開測試該解析功能。

如果您正在進行依賴注入,那么任何依賴類A都應該通過其公共接口傳入 - 如果您的pImpl由於依賴性而干擾您的測試,那么您似乎並沒有注入這些依賴項。

單元測試應該只關注A類暴露的公共接口; A 內部的依賴關系不是你的問題。 只要正確注入所有內容,您就應該能夠傳入模擬而無需擔心A的內部實現。 從某種意義上說,你可以說可測試性和適當的pImpl是相輔相成的,因為不可測試的實現隱藏了不應隱藏的細節。

pImpl成語使測試變得更加容易。 很遺憾看到一套關於“不測試實施”主題的答案,以便在OP后很長時間內激發回答。

在通常的非基於pimpl的C ++中,您有一個包含公共和私有字段的類。 公共領域很容易測試,私有領域有點繁瑣。 公共和私有之間的划分很重要,因為它減少了api的寬度,並且通常使后來的更改更容易。

使用這個習語時,可以選擇更好的選項。 你可以擁有與單個類完全相同的“公共”接口,但現在只有一個私有字段包含某種類型的指針,例如

class my_things
{
  public:
    my_things();
    ~my_things();
    void do_something_important(int);
    int also_this();
  private:
    struct my_things_real;
    std::unique_ptr<my_things_real> state;
};

my_things_real類應該在與外部可見類的析構函數相同的源文件中可見,但不在標題中。 它不是公共接口的一部分,因此所有字段都可以是公共的。

void my_things::do_something_important(int x) { state->doit(x); } // etc

class my_things_real // I'd probably write 'struct'
{
  public:
    int value;
    void doit(int x) { value = x; }
    int getit() { return value; }
};

然后針對真實類別編寫單元測試。 盡可能多地測試它。 我故意稱它為“真實的”而不是“impl”,以幫助確保它不會被誤認為只是一個實現細節。

由於所有字段都是公共的,因此測試此類非常簡單。 外部接口非常小,因為它是由另一個類定義的。 晶圓薄的轉換層很難出錯,但您仍然可以通過外部api進行測試。 這是從更明顯地分離界面和實現的明顯勝利。

在一個含糊不清的相關說明中,令我覺得荒謬的是,如此多的其他連貫的人主張跳過單元測試,以查找通過外部API無法輕易訪問的任何內容。 最低級別的功能幾乎不受程序員錯誤的影響。 驗證api是否可用的測試對於驗證實現細節是否正確非常重要且正交。

單元測試應該使實現類達到它的步伐。 一旦PIMPL類出現在圖片中,您已進入“集成”領域 - 因此U / T不適用於此類。 PIMPL是關於隱藏實現的 - 你不應該知道實現的類設置。

暫無
暫無

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

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