Ci Ty Chen
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
1
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- tags: Unit Test, 單元測試的藝術筆記 --- # 單元測試的藝術第一章心得 ## 初步定義 ### 維基百科上對於「單元測試」的定義為 : > 「一個**單元測試**就是一段程式碼(通常是一個方法),這段程式呼叫了另一段程式碼,然後**驗證某些假設的正確性**。如果這些假設是錯誤的,單元測試就會失敗。一個單元可以是一個方法或函數。」 我們在開發產品的時候 , 會依照某一個假設或是邏輯去實作某一個方法.而單元測試就是我們另外撰寫一個或多個方法去檢查 Production Code 是否有按照我們期待的方式去執行. ### 被測試系統 System Under Test (SUT) - 被你的測試程式所測試的對象 > 「代表 System Under Test,有些人喜歡用 CUT(Class Under Test 或 Code Under Test)。在測試中,**被測試的東西稱為 SUT**。」 作者以前覺得單元測試的傳統定義 , 技術上是正確的. 但後來作者改變了想法. 認為**單元**代表的是**系統中的「工作單元」或是一個「使用案例」(use case)**. ### 工作單元的定義如下 > 從呼叫系統的一個公開方法,到產生一個測試可見的最終結果,在此期間內這個系統所發生的行為統稱為一個工作單元。所謂**一個可見的最終結果指的是,我們只需透過系統的公共 API 和行為就可以觀察到它**,而不需透過系統內部狀態才能得知結果 > 工作單元這個概念意味著一個單元,它既可以小到只包含一個方法,也可以大到包括實現某個功能的多個類別與函數。 > 作者認為工作單元**不是越小越好**。如果你所建立的工作單元越大,它的最終結果對使用這 API 的使用者可見度就越高,測試其實會更容易維護 #### 一個最終結果可以是下列其中一種形式 - 被呼叫的公開方法回傳一個結果值(回傳非 void 函數) - 在呼叫方法的前後,系統可見的狀態或行為發生變化,這樣的變化不需要透過查詢私有狀態就能取得與判斷 - 例如:被測試方法會去創造一個使用者帳戶 , 則被測試方法執行前後是否有額外新增一個**使用者帳戶**. 是我們可以觀察到的結果 - 例如:被測試方法會去修改某物件的 public 屬性 , 則被測試方法執行前後是否有正確修改該物件的 **public 屬性**. 是我們可以觀察到的結果 - 例如:被測試方法執行後 , 會導致另外一個方法執行的結果不同 , 則此變化也是我們可以觀察的結果. - 呼叫一個不受測試所控制的第三方系統 - 例如:呼叫一個第三方 log 系統。這個系統不是你寫的,而且你也沒有它的原始碼。 也就是說 , 我們可以透過驗證下列事項 , 來判斷我們的單元測試是否成功或是失敗 - 方法的回傳值是否正確 - 被測試方法執行後 , 是否會發生可見的狀態或是行為改變. - 被測試方法呼叫某個方法的次數是否正確. ##### 到目前為止單元測試的定義 > 「一個單元測試是一段程式呼叫一個工作單元,並驗證工作單元的一個具體最終結果。如果對這個最終結果的假設是錯誤的,那單元測試就失敗了。一個單元測試的範圍,可以小到一個方法,大到多個類別。」 ## 優秀單元測試的特質 單元測試應該具備以下特質: - 它應該是自動化,而且可被重複執行的 - 它應該容易被實現 - 它到第二天應該還有存在意義(不是臨時性的) - 任何人都可以按個按鈕執行它 - 它的執行速度應該很快 - 它的執行結果應該一致 - 它應該要能完全掌控被測試的單元 - 它應該是完全被隔離的(獨立於其他測試) - 如果它的執行結果是失敗的,應該要很簡單清楚地呈現我們的期望為何,問題在哪 很多人把對軟體進行測試與單元測試的概念混為一談,要釐清這個誤解,你可以問自己以下幾個問題: - 我兩週前所寫的單元測試,今天還能正常執行並得到結果嗎?兩個月前的呢?兩年前的呢? - 我兩個月前所寫的單元測試,團隊中任一人都能正常執行並得到結果嗎? - 我能在幾分鐘內跑完單元測試嗎? - 我能一鍵執行所有我寫過的單元測試嗎? - 我能在幾分鐘內寫出一個基本的單元測試嗎? 如果以上任一題答案是「不能」,那可能你寫的其實是整合測試。 --- ## 整合測試 #### 整合測試的目標 > Integration testing is executed by testers and tests integration between software modules. 整合測試是測試系統是否能夠正常的運作 (複數模組合併) #### 整合測試的定義 >「整合測試是對一個工作單元進行測試,而這個測試對被測試的單元並沒有完全的控制,而是使用該單元一個或多個真實依賴的相依物件,例如時間、網路、資料庫、執行緒或亂數產生器等等。」 對被測試的單元並沒有完全的控制可能帶來的問題 : - 例如 : 在程式中使用目前時間的 DateTime.Now,那麼每次測試執行所取得的都是不同時間,也就是說你無法設定被測試方法執行的環境. 此會導致測試結果不穩定. 另外整合測試還有一個問題 , 測試的東西太多了 - 當發現錯誤時 , 會無法立刻判斷是程式碼中哪一個部分導致這個錯誤產生. #### 總結來說 單元測試與整合測試想要測試的東西不同 - 整合測試會實際使用真實的相依物件或資源 , 確定模組整合後 , 實際上真的能夠運作(至少運作結果是可以接受的). - 單元測試為了保證單元測試的結果高度穩定以確實的驗證方法是否有正確執行某個假設 , 會將被測試單元與其他相依物件隔離開來 單元測試跟整合測試都很重要 , 單元測試用來驗證鎖是否可以正常開關 , 但若沒有整合測試 , 可能會如下圖那樣 , 沒有檢查到系統實際上無法正常運作 ![K9wH0ZU.gif](https://github.com/s0920832252/C_Sharp/blob/master/Files/ArtOfUnitTest/K9wH0ZU.gif?raw=true) ## 最終版的單元測試定義 >「一個單元測試是一段自動化的程式碼,這段程式會呼叫測試的工作單元,之後對這個單元的單一最終結果的某些假設或期望進行驗證。單元測試幾乎都是使用單元測試框架進行撰寫的。撰寫單元測試很容易,執行起來快速。單元測試可靠、易讀、並且很容易維護。只要產品程式碼不發生變化,單元測試的執行結果是穩定一致的。」 ## 簡單的單元測試範例 假設有個類別叫做 SimpleParser 需要測試。這個類別有個方法叫 ParseAndSum: 輸入是由零個或多個逗號(,)分開的數字所組成的一個字串,如果這個字串不包含任何數字,回傳 0,如果只有單一數字則回傳該數 int 值,如包含多個數字,則將數字相加後回傳總和。 ```C# // 範例目前只能處理零個或是一個的狀況 public class SimpleParser { public int ParseAndSum(string numbers) { if (numbers.Length == 0) { return 0; } if (!numbers.Contains(",")) { return int.Parse(numbers); } else { throw new InvalidOperationException("I can only handle 0 or 1 numbers for now!"); } } } ``` TestReturnsZeroWhenEmptyString 會驗證 ParseAndSum 的回傳結果, 若不正確或是有例外發生會輸出錯誤訊息到 Console ```C# // 透過一個簡單的方法來測試傳空字串給 SimpleParser.ParseAndSum 的時候是否會回傳 0 class SimpleParserTests { public static void TestReturnsZeroWhenEmptyString() { try { SimpleParser p = new SimpleParser(); int result = p.ParseAndSum(string.Empty); if (result != 0) { Console.WriteLine(@"***SimpleParserTests.TestReturnsZeroWhenEmptyString:"+ "------ Parse and sum should have returned 0 on an empty string"); } else { // 顧名思義,我希望 Print 出這句。 Console.WriteLine("Print me some success man!"); } } catch (Exception e) { Console.WriteLine(e); } } } ``` 控制台測試程式 ```C# public static void Main(string[] args) { try { SimpleParserTests.TestReturnsZeroWhenEmptyString(); } catch (Exception e) { Console.WriteLine(e); } } ``` ## 測試驅動開發 TDD ### TDD 的步驟 - 撰寫一個會失敗的測試,以**證明產品中程式或功能的缺失** - 撰寫符合測試預期的產品程式碼(實作功能),以通過測試 - 重構程式碼 (不論是產品程式碼或是測試程式都可以重構) ##### 使用 TDD 撰寫單元測試的流程 ![W0zaxJ4.png](https://github.com/s0920832252/C_Sharp/blob/master/Files/ArtOfUnitTest/W0zaxJ4.png?raw=true) ##### 傳統撰寫單元測試的流程 (注意括號內的文字 - 有空的話!!! ) ![DxX1kYE.png](https://github.com/s0920832252/C_Sharp/blob/master/Files/ArtOfUnitTest/DxX1kYE.png?raw=true) 重構時機可以再多個測試完成後才進行 , 也可以通過每個測試後進行. 重構能使我們的 code 更易讀和好維護 , 同時我們還可以透過之前完成的單元測試來保證這次重購 , 並沒有導致功能壞掉. > 重構意味著在不改變功能為前提之下 , 修改程式碼 , 改變其可讀性、可維護性. ### TDD 的三種核心技能 - 僅僅做到先撰寫測試,並不能保證測試是可維護、可讀且可靠的。(目前這本書在講的) - 僅僅做到撰寫出可維護、可讀、可靠的測試,並不能保證你能獲得測試先行的各種好處。 - 僅僅做到測試先行,且測試可讀、可維護、可靠,並不能保證你能產出一個設計完善的系統。 想要成功使用 TDD , 你需要這三種技能 - 知道如何撰寫優秀的測試(目前這本書在講的) - 撰寫程式碼前先寫測試 - 良好的測試設計 ##### 作者建議 : 不要一次補足上述的三種技能 , 循序漸進地一個一個來. 不然通常是放棄收尾... ## 總結 一個優秀的單元測試會有下列特質 - 一段自動化的程式,它會呼叫另一個方法,然後驗證這方法或是該類別的邏輯行為與預期結果相同 - 用一個自動化測試框架進行編寫 - 容易撰寫 - 執行快速 - 能由開發團隊裡任何人重複執行且得到一樣的結果 ### Thank you! You can find me on - [GitHub](https://github.com/s0920832252) - [Facebook](https://www.facebook.com/fourtune.chen) 若有謬誤 , 煩請告知 , 新手發帖請多包涵 # :100: :muscle: :tada: :sheep:

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully