隔壁新婚少妇真紧真爽,超碰e日韩,av无码视屏日韩,老鸭窝手机在我

文章詳情

  1. 您現(xiàn)在的位置:首頁
  2. 資訊中心
  3. 技術資料
  4. 詳情

如何使用 TDD 為嵌入式軟件編寫更好的單元測試

什么是TDD?

測試驅動開發(fā)(TDD)是編寫軟件的迭代過程,其中單元測試是在實現(xiàn)之前開發(fā)的。這是一個緊密的反饋循環(huán),由以下步驟組成:

編寫一個單元測試,看著它失敗。

編寫足夠的代碼來通過測試。

改進代碼(不改變其行為)。

這些步驟通常被稱為“紅色、綠色、重構”,表示測試從失?。t色)到通過(綠色)的過程,有機會改進代碼和測試(重構)。在開發(fā)過程中,這個循環(huán)會一遍又一遍地重復數(shù)百或數(shù)千次。

在此過程中,編寫測試是驅動 軟件開發(fā)的動力。在編寫代碼之前,您需要考慮代碼要做什么,然后將這個想法保存在單元測試中。只有這樣你才能編寫下一段代碼。這迫使你非常清楚你想要代碼做什么。

每通過測試,您就會對軟件正常運行更有信心。而且,由于每一段代碼都是由測試驅動的,因此您終會獲得很大的測試覆蓋率——通過單元測試測試的代碼量。

不要浪費時間編寫不可測試的代碼

單元測試的問題之一(尤其是當您剛剛開始時)是您終可能會編寫難以測試的代碼。

例如,也許您需要訪問一些內部狀態(tài),但您不想公開它?;蛘?,您的被測單元可能有許多難以模擬的復雜依賴項。

編寫可測試的代碼需要經(jīng)驗,但如何才能獲得經(jīng)驗呢?好吧,事實證明, 如果您從 TDD 開始,您就不需要這種經(jīng)驗。 當您首先編寫測試時,您就無法編寫不可測試的代碼。

您將從一開始就取得成功,因此您將更有可能實際采用單元測試作為實踐。想象兩個場景:

場景 1:您編寫了一大堆代碼,然后嘗試找出如何測試它。當您無法快速弄清楚時,您就會放棄,因為您還有軟件要交付!也許您會學到一些有關如何使您的代碼下次更易于測試的知識。

場景 2:您有一個要創(chuàng)建的軟件模塊的想法,但您不確定如何測試它。因此,您花了一些時間來弄清楚如何編寫個測試。然后你編寫一些代碼使其通過。好吧!您剛剛編寫了個單元測試。干得好,你剛剛學到了一些東西。重復此操作,直到獲得經(jīng)過全面單元測試的模塊。恭喜...您剛剛學到了很多有關單元測試的知識。

TDD是一個體驗放大器。你邊做邊學。 TDD 鼓勵您做正確的事情,以便您學得更快。你學得越多,你就越能更好地編寫單元測試。

測試驅動的心態(tài)

測試時,您會以稍微不同的方式思考您正在編寫的代碼。您不必嘗試跟蹤 您希望軟件執(zhí)行的 所有操作,而只需擔心 您希望軟件執(zhí)行的下一操作。讓我們看一個例子來說明。

我喜歡討論 TDD 的例子之一是 命令解析器,因為它被用在很多嵌入式系統(tǒng)中。通常,您希望您的系統(tǒng)能夠與外界對話,以便它實際上可以做有趣的事情。這可能只是一個用于配置的簡單串行接口,也可能是與其他設備或互聯(lián)網(wǎng)的連接。

根據(jù)我的經(jīng)驗,這些類型的接口確實可以從單元測試中受益。它們通常是定制的,并且很快就會變得復雜——有許多代碼路徑和許多需要處理的錯誤情況。而且,由于這是系統(tǒng)的外部接口,因此您不能總是期望另一端的人表現(xiàn)良好。不過,通過一些單元測試,您可以確保一切按預期工作——并且所有錯誤情況都得到處理。

考慮一個帶有簡單命令解析器的嵌入式系統(tǒng)。它從某個地方(例如串行或 USB,但我們的解析器實際上并不關心)接收字符流,并在收到特定字符序列時執(zhí)行某些操作。在這種情況下,系統(tǒng)中有一個可以控制的揚聲器。

大多數(shù)嵌入式軟件開發(fā)人員的反應是開始在 command_parser.c 中編寫一大堆代碼。測試驅動的方法是不同的。

步是: 編寫測試,觀察它失敗。為了編寫測試,您需要弄清楚您希望命令解析器執(zhí)行的件事。如果有協(xié)議規(guī)范(哈,對!),您可以看一下。如果沒有,您現(xiàn)在可以決定代碼首先要做什么。這個怎么樣?

當收到“m”字符時,揚聲器將靜音。

好吧,這是一個簡單、小且定義明確的功能。讓我們編寫一個單元測試,如果執(zhí)行此操作的代碼已實現(xiàn),則該測試將通過。

#include "some_test_framework.h"

#include "some_mock_framework.h"

#include "command_parser.h"

#include "mock_speaker.h"

// A test for the command_parser.

void test_WhenAnMIsReceived_ThenTheSpeakerIsMuted(void)

{

// Receive an "m."

command_parser_put_char('m');

// Make sure the mute function is called.

EXPECT_CALL(speaker_mute());

}

哇,這只是一個測試,但這里有很多設計決策。

為命令解析器定義了一個新函數(shù):command_parser_put_char()。這就是將字符輸入命令解析器的方式,以及如何傳入“m”進行測試的方式。

揚聲器模塊還定義了另一個新功能:speaker_mute()。這將實現(xiàn)揚聲器的實際靜音。當這個函數(shù)被調用時,你就知道測試已經(jīng)通過了。

由于這是一個單元測試,command_parser將被單獨測試,并且不會調用真正版本的speaker_mute()。相反,將提供一個模擬函數(shù)(可能包含在 中mock_speaker.h),并且該EXPECT_CALL宏是使用任何模擬機制的替代品。如果speaker_mute()未調用該函數(shù),測試將失敗。

請注意,這些功能實際上還不存在。但是......您剛剛定義了您想要的確切行為,并且您有一個明確的方法來測試它。如果你現(xiàn)在運行測試,它肯定會失敗。事實上,它會編譯失敗,因為函數(shù)不存在。

現(xiàn)在進行第二步:編寫足夠的代碼來通過測試。終于到了寫一些代碼的時候了!這是command_parser_put_char()使測試通過所需的簡單的代碼 :

// Receive a character.

void command_parser_put_char(char next_char)

{

speaker_mute();

}

請注意,您還需要為speaker_mute().詳細信息取決于您在項目中如何使用模擬。

現(xiàn)在測試應該通過了……但請注意,我們甚至沒有檢查我們收到的是哪個字符!這可能看起來有點愚蠢,但 TDD 的目標之一是化未完成的工作量。

現(xiàn)在這是一個簡單的例子。然而,當代碼變得更加復雜時,任何您 實際上沒有 編寫的代碼都會使您的應用程序更簡單、更容易理解(咳咳……更好)。當你只做你需要做的工作時,關心日程和預算的人也會更高興。

TDD 周期的一步是重構,即 在不改變代碼行為的情況下改進代碼。此步驟的關鍵是您已經(jīng)有了驗證行為的單元測試。因此,您可以自由地嘗試更改代碼,因為失敗的測試會立即告訴您是否更改了行為。不過,由于這只是次測試,因此還沒有太多需要改進的地方。

命令解析器的其余部分是通過重復 TDD 循環(huán)來實現(xiàn)的。那么,您希望命令解析器下一步做什么?怎么樣:

當收到“u”字符時,揚聲器將取消靜音。

好吧,這又是一件好事。這是一個測試:

void test_WhenAUIsReceived_ThenTheSpeakerIsUnmuted(void)

{

// When

command_parser_put_char('u');

// Then

EXPECT_CALL(speaker_unmute());

}

當您改進命令解析器實現(xiàn)以通過測試時,它可能看起來像這樣:

void command_parser_put_char(char next_char)

{

if (next_char == 'm')

{

speaker_mute();

}

else

{

speaker_unmute();

}

}

現(xiàn)在處理錯誤情況怎么樣?如果收到意外字符怎么辦?

當接收到意外字符時,揚聲器靜音狀態(tài)不變。

void test_WhenAnUnexpectedCharIsReceived_ThenTheSpeakerMuteStateIsUnchanged(void)

{

// When

command_parser_put_char('!');

// Then

DO_NOT_EXPECT_CALL(speaker_mute());

DO_NOT_EXPECT_CALL(speaker_unmute());

}

這里的代碼足以讓這個測試通過:

void command_parser_put_char(char next_char)

{

if (next_char == 'm')

{

speaker_mute();

}

else if (next_char == 'u')

{

speaker_unmute();

}

}

這里還有什么你想重構的嗎?如果您更喜歡 switch 語句,您可以繼續(xù)更改它:

void command_parser_put_char(char next_char)

{

switch(next_char)

{

case 'm':

speaker_mute();

break;

case 'u':

speaker_unmute();

break;

default:

break;

}

 

 

 


精品国产乱码久久久久APP下载| 无码精品永久福利在线视频| 亚洲精品久久久蜜桃| 国产成人观看免费| 狠狠噜天天噜日日噜| 欧美xxxx黑人又粗又长| 凹凸视频日本特黄| 人妻在线视频4区| 久久夜色精品国产亚洲| 久久婷婷日韩电影一区二区| 中文字幕精品三区无码| 色一区色二区色三区| 四虎影视毛片免费| 无码日韩精品一区二区免费91| av中文字幕无码一区| 欧美一区一二区操| 日本人妻乱子伦视频| 亚洲伊人久久综合影院| 精品乱码久久久久久久| B无码色欲| 中文字幕高清无码免费看| 国产激情91| 国产12xxxx| 免费无码永久| 精品国产一区二区三区卡蜜| 欧美国产激情| 四虎影院澳门| 久久人人妻人人爽人人爽| 亚洲欧洲无码一区2区无码| 午夜黄色原创影院| 久久天堂无码av网站| 老色熟妇| 男人的天堂色偷女| 老色综合| 538无码精品视频一区二区| 国成人毛片| 久久精品国产色成| 苍梧县| 午夜精品视频久久久久| 久久精品国产亚洲AV麻豆~| 三上悠亚日韩精品一区在线|