2012年4月17日 星期二

Professional Test Driven Development with C#: Developing Real World Applications with TDD

Bear在這裡買的
amazon的參考

C#測試驅動開發
作者:(美)本德//麥格瓦特|譯者:賈洪峰//李菊彥
出版社:清華大學
ISBN:9787302279716
出版日期:2012/03/01
頁數:301
人民幣:RMB 58 元



Bear: 這是一本TDD入門的好書,內容非常淺顯易懂,藉由簡單的例子來教你TDD的實務作法與觀念,建議要先看這本後再看The Art of Unit Testing: With Examples in .Net。最好能靜下心來真正了解原文P.78 The Green Phase 要表達的意境,然後再進入P.79~94(原文P.79~94)這個超簡單的例子,作者一步一步的把TDD實作上的"眉角"透過實例表達出來,看完或許會和Bear一樣有豁然開朗且頓悟的感覺。

P.5(原文P.5)
這些測試腳本通常是由創建系統的開發人員編寫的。在這些情況下,腳本中通常描述的是系統"將會"如何運作,而不是"應當"如何運作。

Another issue was that often these test scripts were written by the developer who created the system. In these cases the scripts usually described how the system would act, not how it should act.

P.9(原文P.10)
TDD終止了遞迴錯誤的出現。可能遇到這種情景:正在開發一個系統時,相同的錯誤總是一次又一次重覆出現。您可能覺得終於抓住並終止了這個錯誤,但在兩個星期之後發現它又回來了。而利用TDD方法,只要報告了一個缺陷,就會編寫一個測試來揭露它。

TDD puts a stop to recurring bugs. You ’ ve probably been in situations where you are developing a system and the same bug seems to come back from QA repeatedly. You think you ’ ve fi nally tracked it down and put a stop to it, only to see it return two weeks later. With TDD, as soon as a defect is reported, a new test is written to expose it. When this test passes, and continues to pass, you know the defect is gone for good.

P.15(原文P.16)
但事實上,好的開發人員應當意識到所有輸入都是魔鬼,必須對其有效性進行獨立驗證。

But the truth is that good developers realize that all input is evil and must be validated independently. And in the end, the business user would rather have the value – 1 returned. You write a test to demonstrate this defect:

P.21(原文P.21)
一個非常正確的座右銘就是"早集成,常集成"

A good motto is “ Integrate early; integrate often. ”

記住,好的單元測試不會突破被測內容的邊界。如果發現在單元測試中測試了不止一件事情,則要研究一下這個測試自身,可能對邊界施加了太多壓力。

Another situation you may fi nd yourself in where you are violating this concept is by mixing your unit tests and integration test. Remember, a good unit test does not break the boundaries around what is being tested. If you fi nd you are testing more than one thing in your unit tests, look at the test itself. You may be pushing too hard against those boundaries.

P.22(原文P.)
事實上,敏捷方法的常見做法就是盡快把軟件提交給用戶,那怕它還沒有全部完成。顯然,在集成應用程序之前,這是不可能實現的。因此可以推斷,全面的集成測試是成功敏捷方法的關鍵組成部份。

In fact, a common practice in agile methodologies is to get software, even if it ’ s not entirely complete, in front of users quickly. Clearly, this is not possible without integrating your application. Therefore, you can infer that thorough integration testing is a key component of a successful agile methodology.

P.25(原文P.25)
用TestFixture 特性來表明它是一個測試類,用Test屬性表明方法是用於測試的。

The following example shows a class using the TestFixture attribute to indicate that it is a test class, and the Test attribute to indicate the method that is our test:

可以使用Setup特性來定義一個將在運行測試之前執行的方法。

In such a case, you can use the Setup attribute to defi ne a method that will execute before your test runs:

P.36(原文P.35)
簡而言之,這意味著:在對類進行內部實例化時,不是靜態創建該類所依賴的對象,而是向其提供這些對象的一些實例,這些實例符合該依賴項所需要的接口。在創建對象時,應當將這些實例作為構造函數實參傳遞給對象。這樣就可以很輕鬆地用模擬對象來代替實際對象,供完全集成的應用程序使用。

Dependency Injection — Mocking is a key concept that makes TDD a viable way of writing software. To use mocking effectively, your application should use Dependency Injection. In short, this means that instead of statically creating objects that your class is dependent on as part of the class ’ s internal instantiation process, you should provide the class with instances of those objects that conform to the interface needed by the dependency. These instances should be passed in as constructor arguments to the object as it is being created. This makes it easy to substitute mocked objects for actual objects that would be used by the fully integrated application. Dependency Injection is explained in more detail in Chapter 5.

測試是應用程序的質量基礎。測試依靠模擬和存根來確保它們能夠正確地與系統中所包含的各種依賴項進行交互。如果用模擬走捷徑,將會損害測試,進而使軟件受到損害。

Tests form the quality baseline for your applications. Your tests rely on mocks and stubs to ensure that they can correctly interact with the various dependencies your system will contain. If you take shortcuts with your mocks, your tests and, by extension, your software will suffer.

P.43(原文P.43)
這一實踐方式被稱為"紅燈、綠燈、重構"。也就是說,開始是一個未能通過的測試,因為還沒有實現能夠通過這一測試的邏輯;然後實現此測試,使其得以通過;最後重構代碼,對其加以改進,同時不會影響測試結果。

This practice is known as “ red, green, refactor. ” This means that you start with a test that fails because you haven ’ t implemented the logic to make it pass yet. Then you implement the test so that it passes. Finally, you refactor to improve the code while not breaking the test.

為提高可維護性而進行的重構應當盡可能對代碼進行簡化。

Refactoring for maintainability should work to simplify the code wherever possible.

P.50(原文P.50)
事實上,在.NET中OCP就是依賴於繼承的。

In fact, in .NET the Open/Close Principle relies on inheritance.

P.50(原文P.51)
接口分離原則(The Interface Segregation Principle, ISP)規定,不應當強制客戶端依賴於其不使用的接口。

The Interface Segregation Principle (ISP) states that clients should not be forced to rely on interfaces they do not use.

P.57(原文P.58)
如果一個類過多地使用另一個類的方法,那就說它有些"特徵依賴(feature envy)"。

In addition to the previously mentioned length of this method, this code has another problem: It uses a property or method from the Invoice class on almost every line. A class that uses too many methods of another class is said to suffer from feature envy. In this case the PlaceOrderForWidgets method is clearly envious of the Invoice class. In situations like this, you see functionality that clearly wants to exist somewhere else, be it another class, method, or module. A chief problem that arises from feature envy is brittle code. By relying so much on the Invoice class, the developer has made this method dependent on it. Changes to Invoice could easily mean changes to this method. This method ’ s dependency on the Invoice class needs to be reduced. I ’ ll show you how to fix this problem in the section “ Extract Methods. ”

P.76(原文P.76)
盡早將應用程序的較小部份集成,就可以確保當前正在創建的組件能夠在以後合併到較大的應用程序中。

By integrating small pieces of the application early on, you can ensure that the components you are building now will be able to be combined into a larger application later.

P.77~78(原文P.78)
Bear: 這個例子可能會巔覆一般人的觀念,在TDD開發的過程中,程式碼竟是以通過單一測試為目的,完全不用在乎參數與是否合乎其它測試條件。剛開始可能不習慣,但多次回味後才能感受到作者的意思是要我們每次只專注於一個非常小的條件,目的就是搞定它,手段愈簡單愈好,只要能通過測試就是我們的目標。然後繼續進行下一個條件,以此類推,你會發現用這種方法之後,所有的事情突然變的簡單了,每一次的一小步進步(測試成功)讓我們愈來愈接近最終的目標,難怪書中曾提到的敏捷方法就算程式尚未全部寫完也能上線應戰(當然,心臟要夠大顆)。

P.92(原文P.93)
編寫的代碼越多,編寫錯誤代碼的機會也就越大,那去掉這一行就很有意義了。

This removes an unnecessary line of code. This may not seem like much, unless you consider that in Chapter 3 I pointed out that the more lines of code you write, the more chances you give yourself to write the wrong code. By eliminating an unneeded line of code, you reduce the chance that the Validate method has a bug.

P.99(原文P.101)
將成員變量聲明為上面的接口,就是隱含地表明我不在乎每個服務採用那種特定的具體實現,我只在乎它實現了所聲明的接口。這樣,就能將判斷具體類型的任務委托給依賴項注入框架去完成。

Interfaces are very important in harnessing the power of dependency injection. By declaring my member variables as interfaces above, I ’ m implicitly stating that I don ’ t care what the specific concrete implementation of each service is, I only care that it implements the interface that it ’ s being declared as. This enables me to delegate the task of determining what my specifi c concrete type should be to my dependency injection framework. The dependency injection framework then becomes the sole storehouse of the rules and logic to determine which concrete instance to use when. The use of interfaces helps to make this possible.

P.162(原文P.167)
測試最佳情景或"幸福路徑"對於確保代碼質量沒有任何幫助。要測試方法的邊緣情景:輸入那些位於期望邊界的參數、超出邊界的取值,以及預期之外的取值和條件。記住,所有輸入都是魔鬼。

Be sure to triangulate your tests. Testing the best - case scenario or the “ happy path ” does nothing to ensure that you are creating quality code. Test the edge cases for your methods: input parameters that are on the boundaries of what is expected, values that fall out of these boundaries, and unexpected values and conditions. Remember, all input is evil.