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

文章詳情

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

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

什么是TDD?

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

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

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

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

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

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

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

不要浪費(fèi)時間編寫不可測試的代碼

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

例如,也許您需要訪問一些內(nèi)部狀態(tài),但您不想公開它?;蛘撸谋粶y單元可能有許多難以模擬的復(fù)雜依賴項。

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

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

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

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

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

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

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

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

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

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

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

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

當(dāng)收到“m”字符時,揚(yáng)聲器將靜音。

好吧,這是一個簡單、小且定義明確的功能。讓我們編寫一個單元測試,如果執(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è)計決策。

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

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

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

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

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

// Receive a character.

void command_parser_put_char(char next_char)

{

speaker_mute();

}

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

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

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

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

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

當(dāng)收到“u”字符時,揚(yáng)聲器將取消靜音。

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

void test_WhenAUIsReceived_ThenTheSpeakerIsUnmuted(void)

{

// When

command_parser_put_char('u');

// Then

EXPECT_CALL(speaker_unmute());

}

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

void command_parser_put_char(char next_char)

{

if (next_char == 'm')

{

speaker_mute();

}

else

{

speaker_unmute();

}

}

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

當(dāng)接收到意外字符時,揚(yáng)聲器靜音狀態(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();

}

}

這里還有什么你想重構(gòu)的嗎?如果您更喜歡 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;

}

 

 

 


农民工嫖妓50岁老熟女| 一道本美女在线观看| 国产精品久久一区二区无卡 | 四虎影视在线观| 亚洲2020天天堂在线观看| 国产精品福利在线观看无码卡一| 久久无码热| 久久久久无码精品亚洲日韩| 久久五月丁香激情综合| 97福利| 欧美黄片视频大全| 精品久久人妻av中文字幕观看| 亚洲人成人无码网在线观看| 亚洲欧美不卡在线| 国模无码在线视频| h动漫精品一区二区三区| 超碰小明| 丁香五月 婷婷久久| 四虎影视国产精品永久免费| 久久综合伊人青青草原| 欧美狠狠干| 亚洲欧洲免费看视频| 色欲av我不卡一区二区| 亚洲愉拍二区一区三区| 久久香蕉门国产免费天天| 精品亚洲欧美综合一区二区三区| 崇阳县| av中文字幕一区二区| 国产成人久久婷婷| 午夜精品久久久久久99热| 囯产欧美超爽歪歪| 精品人妻嫩草A√| 精品三级久久久久久久电影| 手机在线观看永久AV片免费| 亚洲无码APP久久久| 久久天天躁狠狠躁夜夜| 中国美女A级毛片| 夜啪人妻少妇AV| 欧美日韩4区| 天天干论坛中文字幕| 国产精品对白一区二区三区|