我正在試驗一些C++遺留代碼。特別是,我有一個類層次結構,例如A < B < C
(即,A
是B
的子類,並且B
是C
的子類),並且存在全局引用到類型爲C
的對象,該對象從全局系統的代碼(單例模式)。目標是用一些僞造的對象替換那個對象(實際上,C
用於訪問數據庫)。如何僞造包含非虛函數的C++類?
我的第一次嘗試是引入接口IA, IB, and IC
(其中包含相應類的功能的純虛擬版本),讓每個類實現其接口,並將全局參考的類型更改爲IC
。在我測試的設置功能中,我將用我自己實現的IC
替換全局引用的C
對象,從而使整個系統使用我的假實現。
但是,類A, B
和C
每個都包含相當多的非虛函數。現在,如果我要讓類從我的接口繼承,我會將這些函數的語義從非虛擬變爲虛擬(Feathers在「使用遺留代碼高效工作」,第367頁討論了這個問題。換句話說:我必須檢查每個對我的全局對象的調用,而且我必須確保在更改之後,仍然會調用相同的函數。這聽起來像是很多錯誤PRONE工作給我。
我也想過讓非虛函數「最終」,即告訴的A, B
和C
的功能不能在子類中隱藏(編譯這將使編譯器告訴我的B
所有潛在危險的功能和C
- 如果一個函數沒有隱藏在基類中,上述效果根本就不會發生),但似乎並沒有被C++支持(我們還沒有使用C++ 11,但即使是最終的關鍵字似乎只適用於虛擬功能)。
爲了使情況更加困難,類A, B
和C
還包含公共屬性,虛函數以及一些模板函數。
所以我的問題是:如何應對我上面描述的情況?有沒有我錯過的C++功能,哪些可以幫助我的方案?任何設計模式?甚至是任何重構工具?我的主要要求是變化必須儘可能安全,因爲我想假冒的課程對系統來說是相當重要的......我也會對一個「醜陋」的解決方案感到高興,這個解決方案可以讓我進行測試(如果系統適當覆蓋測試,可以在後面重新構建)。
編輯:我搞砸了我的繼承層次結構(它顛倒了) - 現在已經得到糾正。
編輯2:我們最終結束如下:我們只做了我們當前測試用例實際需要的虛擬函數。然後,我們檢查每個調用的方法(這是可管理的)。這讓我們可以用Google Mocks來模擬我們的課程。越來越多的測試案例到位,我們希望我們的變化能夠隨時間節省。請注意,當問我的問題時,我認爲Google Mocks只能模擬純粹的接口;這是而不是的情況,因此允許如上所述的增量方法。
通常情況下,測試的頭號問題是全局對象,所以我建議在做其他事情之前擺脫它。 – Puppy
是'A'和/或'B'具體類嗎?如果是這樣,他們*需要*成爲? –
@Puppy:我們的系統有很多全局對象,我認爲這是一個典型的雞與雞蛋問題:爲了安全地擺脫全局變量,我需要測試,並且爲了編寫測試,我需要擺脫全局。因此,我現在試圖通過儘可能少的和儘可能安全的重構來實現測試,並且我計劃在稍後處理完整的全局問題。 – csoltenborn