2009-06-29 86 views
5

可以使用接口打破所有依賴關係,只是爲了使類可測試?由於許多虛擬調用而不是普通方法調用,因此它在運行時會涉及大量開銷。在C++中進行單元測試

測試驅動開發如何在現實世界的C++應用程序中工作?我閱讀了使用遺留代碼有效地工作,並且喜歡它非常有用,但是不趕快加速實踐TDD。

如果我這樣做,很多時候,我不得不因爲大量的邏輯完全改變rewite單元測試中發生重構。我的代碼更改經常會改變數據處理的基本邏輯。我沒有看到編寫單元測試的方式,在大型重構中不必改變。

可能有人可以點我一個開源C++應用程序至極使用TDD通過例子來學習。

回答

5

更新:見this question too.

我只能在這裏回答一些部分:

是好的,打破使用接口的所有相關性只是爲了一類可測試?由於許多虛擬調用而不是普通方法調用,因此它在運行時會涉及大量開銷。

如果你的表現會受太大的緣故吧,沒有(基準!)。如果你的發展受到太多影響,否(估計額外的努力)。如果它看起來不會有太大影響,並且從長遠來看有所幫助,並幫助您提高質量,是的。

你總是可以'測試'你的測試類,或者一個TestAccessor對象,通過它你的測試可以調查其中的東西。這避免了爲了測試而動態分配所有內容。 (它的確聽起來很不錯)

設計可測試的接口並不容易。有時你必須添加一些額外的方法來訪問內部進行測試。它會讓你感到畏縮,但它有好處,而且往往不是那些函數在實際應用中有用,遲早也會如此。

如果我做了重構,它經常發生,因爲大規模的邏輯改變,我必須完全重新進行單元測試。我的代碼更改經常會改變數據處理的基本邏輯。我沒有看到編寫單元測試的方式,在大型重構中不必改變。

defintion的大型重構變化很大,包括測試。很高興你有他們,因爲他們也會在重構之後測試一些東西。

如果您花費更多的時間重構比創建新功能,也許您應該考慮在編碼之前多思考一下,以便找到能夠承受更多變化的更好的界面。另外,在接口穩定之前編寫單元測試是一件痛苦的事情,不管你做什麼。

對接口變化很大的代碼越多,每次更改的代碼越多。我認爲你的問題在於此。我已經在大部分地方設置了足夠穩定的接口,並且不時重構部分。

希望它有幫助。

+0

這不是我編碼之前不認爲的。在客戶看到新功能的初始實現後,需求常常會發生變化。另一個原因是,出於市場營銷的原因,做一個快速和骯髒的實施有時是不切實際的。在這種情況下,我不打算進行單元測試,但有時由於緊張的調節,有時候快速和骯髒的黑客變成了真實的東西。以後再做這個爛攤子測試並不容易。 – frast 2009-06-29 22:18:00

3

我經常使用宏,#if和其他預處理技巧來爲C和C++中的單元測試目的「模擬」依賴關係,這正是因爲使用這些宏我不必支付任何運行時間代碼編譯爲生產而非測試時的成本。不優雅,但相當有效。

至於重構,他們很可能需要的時候,他們是如此壓倒性地大,intrustive你描述更改測試。不過,我並沒有發現自己經常那麼劇烈地重構。

+1

我想到你的解決方案,但我擔心它可能會發生,因爲預處理器定義,我測試的不是「真正的代碼」。但我認爲這是值得嘗試的。 – frast 2009-06-29 22:07:55

+0

是的,宏和其他預處理器攻擊是脆弱的東西,除非小心謹慎地使用,並根據特定的有限模式 - 這是我的答案中的「不雅」部分。依賴注入的宏觀等價物(有時也可以通過模板或typedef實現,就像@jaif所說的,在簡單的情況下)是一個非常有限和嚴格的案例,而這就是所有你需要讓你「模擬」你的依賴爲單元測試目的。 – 2009-06-29 22:31:39

1

明顯的答案是使用模板而不是接口來分解依賴關係。當然這可能會影響編譯時間(具體取決於您如何實現它),但至少應該消除運行時間開銷。稍微簡單一些的解決方案可能僅僅依賴一組typedef,這些typedef可以用一些宏或類似的東西來替換掉。

1

關於你的第一個問題 - 這是很少值得打破東西只是爲了測試的緣故,雖然有時你可能使他們更好地爲重構的一部分,之前打破東西。軟件產品最重要的標準是它的工作原理,而不是可測試的。可測試性是非常重要的,因爲它可以幫助您製作更穩定,更適合最終用戶的產品。

測試驅動開發的一個重要組成部分就是選擇你的代碼是不太可能改變單元測試小的原子零件。如果由於大規模邏輯改變而必須重寫很多單元測試,則可能需要在更細粒度的級別進行測試,或者重新設計代碼以使其更穩定。一個穩定的設計不應該隨着時間的推移發生巨大的變化,測試也不會幫助您避免大規模重構。然而,如果正確的測試可以使得當你重構一些東西時,你可以更加確信你的重構是成功的,假設有一些測試不需要改變。

1

可以打破所有使用接口的依賴只是爲了讓一個類可測試嗎?由於許多虛擬調用而不是普通方法調用,因此它在運行時會涉及大量開銷。

我認爲可以打破依賴關係,因爲這會導致更好的界面。

如果我做了重構,它經常發生,因爲大規模的邏輯改變,我必須完全重新進行單元測試。我的代碼更改經常會改變數據處理的基本邏輯。我沒有看到編寫單元測試的方式,在大型重構中不必改變。

由於您的測試應該表達您的代碼的真實意圖,所以您不會在任何語言中使用這些大型重構。所以如果邏輯改變了,你的測試必須改變。

也許你並沒有真正做TDD,如:

  1. 創建一個測試失敗
  2. 創建代碼通過測試
  3. 創建另一個測試失敗
  4. 修復代碼通過兩個測試
  5. 沖洗並重復,直到您認爲您有足夠的測試,顯示您的代碼應該做什麼

這些步驟說你應該做一些小改動,而不是大改動。如果你留在後者,你無法逃避重構。沒有一種語言能夠幫你節省開支,而且由於編譯時間,鏈接時間,錯誤消息等等,C++將是最糟糕的。

我實際上是在用C++編寫的真實世界軟件在它下面有一個巨大的遺留代碼。我們使用TDD,它確實有助於演進軟件的設計。

0

如果我做了重構,它經常發生,因爲大規模的邏輯改變,我必須完全重寫單元測試。 ......我沒有看到編寫單元測試的方式,在大型重構中不必改變。

There are multiple layers of testing,並且其中一些層在大邏輯改變後不會中斷。另一方面,單元測試是爲了測試方法和對象的內部,並且需要比這更頻繁地進行更改。沒有錯,必然。事情就是這樣。

是否可以使用接口來打破所有依賴關係,只是爲了使類可測試?

It's definitely OK to design classes to be more testable。畢竟,這是TDD的目的之一。

由於許多虛擬調用而不是普通的方法調用,它涉及到運行時的大量開銷。

幾乎每個公司都有一些所有員工應遵循的規則列表。無知的公司只是列出他們能想到的每一個好品質(「我們的員工是高效,負責任,有道德,而且從不偷工減料」)。更聰明的公司實際上將他們的優先級排序如果有人提出不道德的方式來提高效率,公司是否會這樣做?最好的公司不僅打印宣傳冊,說明優先級如何排名,而且還確保管理層遵循排名。

程序完全有可能高效且易於測試。但是,有時您需要選擇哪個更重要。這是其中的一次。我不知道效率對你和你的計劃有多重要,但你確實是這樣。所以「你寧願有一個緩慢的,測試良好的程序,還是一個沒有全面測試覆蓋率的快速程序?」

0

它涉及到在 運行,因爲很多虛擬呼叫 不是純方法調用的顯著開銷。

請記住,如果您通過指向接口或對象的指針(或參考)訪問方法,則它只是虛擬調用開銷。如果通過堆棧中的具體對象訪問該方法,則不會產生虛擬開銷,甚至可以內聯。

此外,在分析代碼之前,千萬不要認爲這個開銷很大。幾乎總是,如果你比較你的方法正在做什麼,虛擬調用是毫無價值的。 (大部分的懲罰來自於不可能內聯單線方法,而不是來自額外的間接呼叫)。