如果我有一個在特定條件下調用自己的方法,是否可以編寫一個測試來驗證行爲?我很想看到一個例子,我不關心模擬框架或語言。我在C#中使用RhinoMocks,所以我很好奇它是否是框架的一個缺失功能,或者如果我誤解了一些基本的東西,或者它是不可能的。如何編寫一個遞歸方法的Mockist測試
回答
假設你想要做的事就像從一個完整的路徑獲得的文件名,例如:
c:/windows/awesome/lol.cs -> lol.cs
c:/windows/awesome/yeah/lol.cs -> lol.cs
lol.cs -> lol.cs
,你必須:
public getFilename(String original) {
var stripped = original;
while(hasSlashes(stripped)) {
stripped = stripped.substringAfterFirstSlash();
}
return stripped;
}
,你想寫:
public getFilename(String original) {
if(hasSlashes(original)) {
return getFilename(original.substringAfterFirstSlash());
}
return original;
}
這裏的遞歸是一個實現細節,不應該進行測試。您確實希望能夠在兩個實現之間切換,並驗證它們是否產生相同的結果:兩者都爲上述三個示例生成lol.cs。這就是說,因爲你是通過名字遞歸而不是說thisMethod.again()等,所以在Ruby中你可以將原始方法別名爲新名稱,用舊名稱重新定義方法,調用新的名稱並檢查您是否以新定義的方法結束。
def blah
puts "in blah"
blah
end
alias blah2 blah
def blah
puts "new blah"
end
blah2
你是說在這種情況下,驗證方法狀態的單元測試是否足夠好? – JeremyWeir 2010-03-01 17:45:16
對不起,我不完全瞭解你的問題。 在我的文件路徑示例中,驗證方法輸出的單元測試就足夠了,甚至比驗證遞歸的單元測試更好。 但是,我不知道你的具體情況,所以可能會有所不同。 – miaubiz 2010-03-02 09:14:52
@jayrdub - 在一般情況下,狀態驗證正是您希望單元測試所要做的。檢查方法的返回值和/或被測對象的公共屬性。其他一切都是實現細節,並且在重構期間可能會更改。 – TrueWill 2010-03-04 15:07:57
在我意識到的任何模擬框架中,沒有任何東西需要監視堆棧深度/(遞歸)函數調用次數。但是,適當的模擬前提條件提供正確輸出的單元測試應該與嘲諷非遞歸函數相同。
導致堆棧溢出的無限遞歸需要單獨調試,但單元測試和嘲笑從來沒有擺脫過這種需求。
一個自稱在一定條件下,是有可能寫一個測試,以驗證該行爲的方法?
是的。但是,如果您需要測試遞歸,則最好將入口點分爲遞歸和遞歸步驟以進行測試。
無論如何,這裏是如何測試它的例子,如果你不能這樣做。你真的不需要任何嘲弄:
// Class under test
public class Factorial
{
public virtual int Calculate(int number)
{
if (number < 2)
return 1
return Calculate(number-1) * number;
}
}
// The helper class to test the recursion
public class FactorialTester : Factorial
{
public int NumberOfCalls { get; set; }
public override int Calculate(int number)
{
NumberOfCalls++;
return base.Calculate(number)
}
}
// Testing
[Test]
public void IsCalledAtLeastOnce()
{
var tester = new FactorialTester();
tester.Calculate(1);
Assert.GreaterOrEqual(1, tester.NumberOfCalls );
}
[Test]
public void IsCalled3TimesForNumber3()
{
var tester = new FactorialTester();
tester.Calculate(3);
Assert.AreEqual(3, tester.NumberOfCalls );
}
你誤解了模擬對象的目的。 Mock(以Mockist的意義)用於測試與被測系統相關性的行爲交互。
所以,舉例來說,你可能有這樣的事情:
interface IMailOrder
{
void OrderExplosives();
}
class Coyote
{
public Coyote(IMailOrder mailOrder) {}
public void CatchDinner() {}
}
狼取決於IMailOrder。在生產代碼中,Coyote的實例將傳遞一個實現IMailOrder的Acme實例。 (這可以通過手動依賴注入或通過DI框架完成。)
想要測試方法CatchDinner並驗證它是否調用OrderExplosives。要做到這一點,您可以:
- 創建實現IMailOrder模擬對象,並通過將模擬對象給它的構造創建狼實例(被測系統)。 (Arrange)
- 致電CatchDinner。 (Act)
- 請求模擬對象驗證是否滿足給定的期望值(OrderExplosives called)。 (Assert)
當您設置對模擬對象的期望可能取決於您的模擬(隔離)框架。
如果您正在測試的類或方法沒有外部依賴關係,則不需要(或不想)爲該組測試使用模擬對象。這個方法是遞歸的還是沒有關係都沒關係。
您一般都想測試邊界條件,因此您可能會測試不應遞歸的調用,具有單個遞歸調用的調用以及深度遞歸調用。 (miaubiz有大約遞歸是一個實現細節,雖然好點。)
編輯:通過在最後一段我的意思是帶參數的調用或對象會觸發一個給定的遞歸深度狀態「呼」。我也推薦閱讀The Art of Unit Testing。
編輯2:例使用Moq測試代碼:
var mockMailOrder = new Mock<IMailOrder>();
var wily = new Coyote(mockMailOrder.Object);
wily.CatchDinner();
mockMailOrder.Verify(x => x.OrderExplosives());
「如果您正在測試的類或方法沒有外部依賴關係,那麼您不需要(或不想)爲該組測試使用模擬對象,這種方法是否遞歸併不重要。」 這是我需要提醒的部分,謝謝。我最喜歡你的回答,但是在我能夠之前自動選擇。 – JeremyWeir 2010-03-04 17:20:59
@jayrdub - 謝謝! :) – TrueWill 2010-03-04 18:59:15
這裏是我的 '農民' 的方法(在Python,經測試,請參閱基本原理的評論)是
注實現詳細的「暴露」在這裏不成問題,因爲你正在測試的是底層架構,它恰好被「頂級」代碼所利用。所以,測試它是合法的,行爲良好(我也希望,這是你的想法)。
的代碼(其主要思想是從一個單一的,而是「不可測」遞歸函數去的等效對遞歸依賴性的(並且因此可測試)功能):
def factorial(n):
"""Everyone knows this functions contract:)
Internally designed to use 'factorial_impl' (hence recursion)."""
return factorial_impl(n, factorial_impl)
def factorial_impl(n, fct=factorial):
"""This function's contract is
to return 'n*fct(n-1)' for n > 1, or '1' otherwise.
'fct' must be a function both taking and returning 'int'"""
return n*fct(n - 1) if n > 1 else 1
測試:
import unittest
class TestFactorial(unittest.TestCase):
def test_impl(self):
"""Test the 'factorial_impl' function,
'wiring' it to a specially constructed 'fct'"""
def fct(n):
"""To be 'injected'
as a 'factorial_impl''s 'fct' parameter"""
# Use a simple number, which will 'show' itself
# in the 'factorial_impl' return value.
return 100
# Here we must get '1'.
self.assertEqual(factorial_impl(1, fct), 1)
# Here we must get 'n*100', note the ease of testing:)
self.assertEqual(factorial_impl(2, fct), 2*100)
self.assertEqual(factorial_impl(3, fct), 3*100)
def test(self):
"""Test the 'factorial' function"""
self.assertEqual(factorial(1), 1)
self.assertEqual(factorial(2), 2)
self.assertEqual(factorial(3), 6)
輸出:
Finding files...
['...py'] ... done
Importing test modules ... done.
Test the 'factorial' function ... ok
Test the 'factorial_impl' function, ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
- 1. 爲單一方法編寫classist和mockist單元測試是否有優勢?
- 2. 測試遞歸方法
- 3. 編寫並測試一個方法PrintSquare
- 4. 如何爲reject_body_scores(歸因)方法編寫測試代碼?
- 5. 如何編寫一個junit測試用例添加方法?
- 6. 編寫一個遞歸方法來比較兩個字符串?
- 7. 編寫一個分析XML數據的遞歸方法
- 8. 如何用軸編寫遞歸算法?
- 9. 如何編寫內部類中的方法的測試方法?
- 10. 如何編寫一個遞歸方法來返回int中的數字總和?
- 11. 如何在rspec中測試一個採用遞歸方法的非變量?
- 12. 如何編寫遞歸函數的方法定義?
- 13. 如何使用遞歸編寫鏈表的包含方法? java
- 14. 如何在java中編寫鏈表的遞歸toString方法
- 15. 如何編寫遞歸函數的方法定義?
- 16. 如何編寫一個遞歸方法來檢查這種情況?
- 17. 如何在NUnit中編寫測試方法來測試savefile方法?
- 18. 爲從同一類調用方法的方法編寫測試
- 19. 如何在Go中編寫測試作爲一種方法?
- 20. 如何爲一種方法編寫rspec測試
- 21. 試圖寫一個Python遞歸函數
- 22. 當爲ffmpeg寫一個測試時,如何編寫一個makefile?
- 23. 編寫測試多個瀏覽器的一種方法
- 24. 如何測試和模擬遞歸方法?
- 25. 編寫NUnit測試用例的方法
- 26. 爲客戶編寫一個wcf服務的迴歸測試
- 27. 爲一個方法寫一個遞歸關係
- 28. 可以使用循環的任何方法遞歸編寫?
- 29. 編寫一個測試方法通過別人
- 30. 如何編寫基於TDD的方法的否定測試?
這我不清楚。什麼是試圖測試?該方法自稱爲「在特定條件下」(即「調用堆棧」將在某些條件下遵循某一特定路徑)或其他內容? – Ando 2010-03-02 08:23:10