Skip to content

Latest commit

 

History

History
278 lines (202 loc) · 34.7 KB

unittest.md

File metadata and controls

278 lines (202 loc) · 34.7 KB

Unit Testing 單元測試

Questions

  • Infinitest https://infinitest.github.io/ 會在 code 變動時自動執行測試,聽起來不錯??
  • 什麼是 FIRST principle??
  • Coverage 100% 的意義是什麼?
  • 做單元測試時,我們總是對其他 dependency 做了假設 "我這樣呼叫你,就會完成一些事",這些東西應該寫在 Javadoc 上才對??
  • 除以 0 的複雜度有辦被算出來嗎??

定位

Unit 指的是什麼?

關鍵是 Seams

基礎

Arrange-Act-Assert (3A) 原則?

測試的單位 input 越少越好??

  • 以某個複雜的 data structure 做為 input 時,若沒有用到全部的資料,在測試時如何只提供部份的資料 -- 像 builder 的存在一樣,如何讓會影響結果的 input 被突顯出來? 話說回來,複雜的 data structure 只不過是另一種 object? (例如 hashmap,不同的 key 帶不同的資料),所以 builder pattern 應該也適用。

Good Tests

FIRST Principle ??

  • Test-Driven Development on Android with the Android Testing Support Library (Google I/O '17) - YouTube (2017-05-18) 07:10 列舉 unit test 應該要掌握的要點 - thorough、repeatable、focused、verify behavior (避免對實作做太多假設,測試 behavior,才不會 implementation 改變就影響 test;這與 Mockito "focusing on interactions between objects" 的想法一致)、fast (尤其測試數量會很大,否則會不想寫測試、做 refactoring)、concise (將測試做為 documentation);比 FIRST 多了 focused (快速定位出問題點) 與 verify behavior。
  • Test-Driven Development on Android with the Android Testing Support Library (Google I/O '17) - YouTube (2017-05-19) https://www.youtube.com/watch?v=pK7W5npkhho Mobile Nijia 的場子,ATSL (包括 AndroidJUnitRunner、rules、Espresso) 就是這個小 team 在負責,圍遶著 Test Pyramid 與 "Testing is about tradeoffs",從定位 large E2E test 的 UI test (Espresso) 講到 small unit test (Robolectric),再回頭講怎麼搭配 hermetic environment 把 large E2E test 拆成數個 medium integration test,宣告下一版 ATSL 會有 Android Test Orchestrator 與 Multiprocess Espresso,分別解決 large E2E test 的 flakiness 與 UI 可能執行在不同 process 的問題。其間最讓人意外的是 Robolectric 似乎在 Google 內部逐漸受到重視,雖然官方常未宣稱支持這個專案,但從 Android CTS 測試進度已達 70% 這件事看來,正式成為 ATSL 的一員也是可以期待的?
  • F.I.R.S.T Principles of Unit Testing · ghsukumar/SFDC_Best_Practices Wiki (2016-06-28) Fast, Isolated/Independent, Repeatable, Self-Validating, Thorough & Timely,其中 Isolated/Independent 與 Repeatable 互為因果,最重要的就是不要跟環境、會變動的資料、執行順序相依;Fast 背後主要的考量是量很大,所以加總起來的時間會很可觀。
    • Fast 若不夠快,開發人員就會不想多執行它;由於 unit test 的量很大,若每個 test 不控制在幾個 millisecond,加總起來的時間會很可觀。
    • Isolated/Independent - 提及 3 As - Arrange, Act, Assert,其中 arrange 與 "不要跟環境相依" 也有點關係 (不要在這裡做 assert),另外只做 a single logical assert (== multiple physical asserts),避免 order-of-run dependency。
    • Repeatable - 因為不跟環境、會變動的資料 (例如 date/time) 相依,所以結果是決定論的 deterministic (de-ter-mi-nis-tic) ;感覺 repeatable 是 isolated/independent 的產物。
    • Self-validating - 不需要人為介入去判斷 pass 或 fail。
    • Thorough and Timely - 比起 100% coverage,是不是覆蓋所有的 use case scenario 更為重要。另外儘可能做到 TDD,這樣就不用事後做 refactoring。
  • FIRST Principles for Writing Good Unit Tests - HowToDoInJava (2016-08-22) Fast 提到慢的原因極可能是跟 database/file/network 產生相依,這些 "external environment not under your direct control" 會防礙你達成 repeatable;另外 isolated 除了要避開不受控制的外部環境、變動資料,也要 "focused only on a small amount of behavior"。
    • FIRST 是個 acronym - First, Isolated, Repeatable, Self-validating, Timely
    • First - 會造成慢的原因,多半是跟外部環境相依,例如 database、file、network calls 等。
    • Isolated - 不只是 test case 可以單獨執行 (不受其他 test case 的影響),也跟測試失敗時,能否立即反應出是哪裡出錯有關 (不用花時間找問題在哪裡);這跟 "tests focused only on a small amount of behavior" 有關 => If one of your test methods can break for more than one reason, consider splitting it into separate tests,跟 Single Responsibility Principle (SRP) 的概念有點像。
    • Repeatable - 跟 "external environment not under your direct control" 隔離開來,若不得以要存取 database,可以考慮 in-memory database。
    • Self-validating - 測試過程中不需要人為介入。
    • Timely - 雖然可以在任何時候寫 unit test,但儘早做的好處多多。
  • Agile in a Flash: F.I.R.S.T (2009-02) http://agileinaflash.blogspot.tw/2009/02/first.html #ril

Coverage

  • 從 coverage 來看哪些沒測試不一定準,因為程式有走過並不代表有做驗證,因為最好是根據 contract 來寫 test case。
  • Codecov https://codecov.io/ 有很多 opensource project 在用??

Coverage 100% 的意義是什麼?

Testable

Readability 可讀性

Non-static nested class (inner class) 如何在測試時不管 enclosing class??

  • EnclosingClass 底下的 public class NonStaticNestedClass 為例。
  • 利用 Mockito 生出一個假的 enclosing object,例如 Mockito.mock(EnclosingClass.class).new NonStaticNestedClass(...) 就可以了。

Legacy Code & Refactoring

Concurrency / Multithreading

Fluent Assertion

One Assertion Per Test?

  • 所以什麼是 single concept? 實務上可以搭配 test method 的 naming - action_Given_Then() (例如 addTask_DuplicateName_ShowError()) 來做判斷,只要 verification 都在 then 的範圍下即可,因為 then 不會寫得太長,自然 verification 的範圍就會受到限制。
  • One Assertion Per Test Considered Ambiguous (2013-08-26) https://www.franzoni.eu/one-assertion-per-test-considered-ambiguous/ 解讀成只能用一次 assert method 是錯的,應說說是 Single Concept Per Test。

參考資料:

  • 關於傳說中的 One Assert Per Test,或許 Single Concept Per Test 是相對務實的 "I think the single assert rule is a good guideline. ... But I am not afraid to put more than one assert in a test. I think the best thing we can say is that the number of asserts in a test ought to be minimized. … Perhaps a better rule is that we want to test a single concept in each test function." -- Clean Code

  • should I worry about the unexpected? | monkey island https://monkeyisland.pl/2008/07/12/should-i-worry-about-the-unexpected/ 提到 "I really want to have small, separate test methods that are focused around behavior" 及 "small&focused test methods"

  • F.I.R.S.T Principles of Unit Testing · ghsukumar/SFDC_Best_Practices Wiki /~https://github.com/ghsukumar/SFDC_Best_Practices/wiki/F.I.R.S.T-Principles-of-Unit-Testing 在 Isolated/Independent 提到 "typically there should be only a single logical assert. A logical assert could have multiple physical asserts as long as all the asserts test the state of a single object." 這裡 "a single logical assert = multiple physical asserts" 的說法很不錯。

  • One Assertion Per Test (2004-02-23) http://www.artima.com/weblogs/viewpost.jsp?thread=35578 #ril

  • 忘了在哪裡寫下 "focused" 比 "single assertion" 更重要??

    • 幾個相關的 state 要一起驗證才對,例如 select(item) 後,isSelected(item)getSelectedCount() 都會跟著起變化。
  • Google Testing Blog: Testing on the Toilet: Writing Descriptive Test Names https://testing.googleblog.com/2014/10/testing-on-toilet-writing-descriptive.html 一開始的範例就用了 3 個 assertion,與其說是 single assertion,倒不如說是 single behavior?? "By giving tests more explicit names, it forces you to split up testing different behaviors into separate tests"

  • Programmatically Speaking – One assertion per test, please (2015-07-25) http://programmaticallyspeaking.com/one-assertion-per-test-please.html #ril

  • Testing made sweet with a Mockito by Jeroen Mols - YouTube It is better to have a simple test that works than a complicated test that seems to work. (在 Javadoc 裡),呼應 conclusion 裡的 "keep unit tests small and focused",要跟 production code 視為一樣重要,否則之後會咬你一口

Overspecified (Over Specification)

Naming

  • Naming 很重要,如果可以簡單用幾個單字描述清楚 state under test 與 expected behavior,那麼 test code 就不會驗太多,讓 test case 保持專注 (focused)。

參考資料:

New

Singleton

Public fields

  • Public fields 在測試上似乎也會形成阻礙?? 至少 mocking framework 無法 verify 對 field 的存取?? 但似乎很少人在談論這個?

Private Method

Static Method

  • 如果 3rd party lib 只有 static method 可用,怎麼辦? => 按照 "Don't mock types you don't own" 的原則,為它提供一層 wrapper;不過這跟是否為 static 沒有直接關係,而是處理 3rd party dependency 的通用方式。在 http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ 也提到 "If you’re stuck with a library class’ static methods, wrap it ..."
  • Legacy code 很容易有 static method,如果要全改成 instance method,工程會太大
    • 以 Mockito + PowerMock 來說,一開始會覺得可以處理掉 static method dependency 很好用,但其實只是 "緩兵之計",因為當你要測試 static method 所在的 class 時,還是會再遇到它自己的 dependency 要 mock 的問題...
  • 感覺 static method 也沒那麼糟,除非內部有些 dependencies 要在 unit test 時做 mock
  • Google Testing Blog: Static Methods are Death to Testability (2008-12-17) https://testing.googleblog.com/2008/12/static-methods-are-death-to-testability.html - 大體上 "leaf methods are ok to be static but other methods should not be" 是對的,只擔心某天 leaf method 不再是 leaf 而已;Static method 中尤以 factory methods 最糟,因為內部建立了 object graph。
  • FAQ · mockito/mockito Wiki /~https://github.com/mockito/mockito/wiki/FAQ#user-content-can-i-mock-static-methods 提到 "Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change",如果面對的是 "scary legacy code",請用 JMockit 或 PowerMock;但 static method 跟 procedural code 有關係??

Legacy code 很容易有 static method,如果要全改成 instance method,工程會太大??

  • 以 Mockito + PowerMock 來說,一開始會覺得可以處理掉 static method dependency 很好用,但其實只是 "緩兵之計",因為當你要測試 static method 所在的 class 時,還是會再遇到它自己的 dependency 要 mock 的問題...

Test Doubles

Stubbing

Don't mock type you don't own!

Don't mock value objects

不要將 test code 混入 production code

Verification / Assertion 驗證

如何驗證 data class 只有特定的 field / property 被異動??