模擬(Mocking)是嵌入式系統(tǒng)開中用于單元測試和測試驅(qū)動的一種技術(shù)。模擬的好處在于通過刪除不相關(guān)的從屬代碼,它允許開發(fā)者很容易地隔離和單元測試其中一部分代碼。在嵌入式系統(tǒng)中,這些從屬代碼可能被當作由設(shè)備驅(qū)動和硬件運行的功能集合而存在。在這篇博客中,我們將討論關(guān)于模擬設(shè)備驅(qū)動和硬件以及使用GoogleTest和GoogleMock來測試應用程序代碼。
在我們的例子中,目標硬件是Zynq-7000 SoC的ZedBoard。之所以挑選Zedboard是因為它有大量擁躉和很多簡便可用的參考設(shè)計。不過其不足是,一板難求!——由于需求量大,這塊開發(fā)板的運送時間超過2個月。不過目標硬件送達延遲對嵌入式開發(fā)者來說是個普遍問題。沒有目標硬件,嵌入式開發(fā)者通常只有兩種選擇:
等待,或…
在沒有硬件的情況下開發(fā)并測試高質(zhì)量應用
我們的選擇是不等待:-)
我們的應用是一個視頻演示平臺。在這個平臺上,Zynq SoC中ARM Cortex-A9 MPCore CPU上運行的軟件創(chuàng)建一系列視頻幀,將它們通過由Xilinx IP核組建的硬件流水線傳輸?shù)?a target="_blank">HDMI視頻輸出。我們的目的就是通過模擬設(shè)備驅(qū)動來寫入和測試整個應用程序。然后,當收到ZedBoard后,我們就可以用真正的設(shè)備驅(qū)動來重新部署我們的應用了。
模擬設(shè)備驅(qū)動功能
我們選擇一個對我們單元測試很好的粒度(granularity)水平,因此我們能夠從一系列很小的步驟開始建立和測試我們的應用程序, 每一步測試完畢后再進行下一步。例如,軟件功能是用xiic_l.中的Xiic_DynInit()函數(shù)對目標硬件I2C 控制器基地址進行適當?shù)某跏蓟?/p>
為了模擬像Xiic_DynInit()這樣的設(shè)備驅(qū)動程序,我們的應用包含賽靈思設(shè)備驅(qū)動頭文件xiic_l.h,正如你通常做的一樣。然后我們新建我們自己的該函數(shù)實現(xiàn)的存根來代替從libxil.a中鏈接真正的設(shè)備驅(qū)動代碼。這存根始于空的實現(xiàn)體,由此我們可以流水清除Makefile然后build,確保編譯器和鏈接器都沒問題(大多數(shù)有經(jīng)驗的開發(fā)者也許不會有這個習慣,但是這是個開始階段,我們覺得有幫助)。
下一步是在存根下插入真正的功能,我可以使用這些功能來模擬設(shè)備驅(qū)動的功能。這就是要用到GoogleMock。GoogleMock 是一個C++框架,我們用它來模擬C函數(shù)(實際上GoogleMock只是個C++框架,對C++不熟悉的開發(fā)者不必為此擔心。所有的事都會由單內(nèi)襯(one-liner)的宏和函數(shù)完成,所以你會感到在沒C++知識下創(chuàng)建和使用模擬也很容易)。
我們選擇模擬GoogleMock中的一些設(shè)備驅(qū)動,叫做xdriverMock。在xdriverMock中,有我們感興趣的每個函數(shù)的聲明。例如 Xiic_DynInit(),我們使用MOCK_METHOD1,用來定義一個輸入變量的函數(shù)。(類似的,也有MOCK_METHOD2,MOCK_METHOD3之類的來聲明有任意個輸入變量的函數(shù)。)MOCK_METHOD宏定義了很多一般特性使得編寫單元測試和測試相互作用變得非常容易。更多好處還在后面。
作為我們 xdriverMock的一部分,我們也有叫做getXdriverMock()和destroyXdriverMock()的全局函數(shù)分別來實例化和析構(gòu)這個mock對象。如果你熟悉C++,getXdriverMock()和destroyXdriverMock()函數(shù)很像是構(gòu)造函數(shù)和析構(gòu)函數(shù);它們構(gòu)造一個xdriverMock的實例用于測試,然后析構(gòu)作為碎片收集。在具體實現(xiàn)的文件里這些方法看上去如下:
這個難題的最后一塊就是調(diào)用GoolgeMock來填充Xiic_DynInit()函數(shù)的存根應用 。如下所示:
有了這個,我們就成功地用GoogleMock代替了真正設(shè)備驅(qū)動的功能。對單元測試來說,我們現(xiàn)在有了對設(shè)備驅(qū)動功能完整的控制,也擺脫了對硬件的依賴。記住我們的首要任務是驗證分配給 I2C控制器的基地址,現(xiàn)在我們可以開始編寫驗證應用程序正常工作的單元測試了。
測試你的第一個功能
為了用我們新的設(shè)備驅(qū)動模擬器開始編寫測試,我們首先需要建立GoogleTest線束,這相對簡單,涉及新建測試單元實例,叫做licCtrl的類和xdriverMock的一個副本。如果你的測試單元是純粹的C函數(shù)而不是像我們這樣的C++類,當然你就只要直接調(diào)用C函數(shù),不必管該類實例了。
對單元測試來說,大多數(shù)有關(guān)GoogleMock的相互作用是通過EXPECT_CALL宏發(fā)生的。有了這個宏,我們能夠驗證我們的應用程序滿足所有期望的與設(shè)備驅(qū)動的相互作用。通常的做法例如,我們的應用程序通過叫做init()的licCtrl的一個成員函數(shù)來調(diào)用Xiic_DynInit()。確認運行的測試如下:
測試程序的第一行中,我們使用EXPECT_CALL來說明,當測試期間輸入值等于HDMI I2C基地址時,XIic_DynInit()函數(shù)應當被調(diào)用一次。第二行是調(diào)用我們iicCtrl應用代碼里的init()方法。如果init()方法無法用我們期望的地址調(diào)用Xiic_DynInit()函數(shù),一個斷言將在該模擬中發(fā)生,并且測試以失敗告終。否則,測試通過。
這個測試的模式很通用,這也是我們選擇在這兒展示的原因。不過,你可以設(shè)置更為復雜的預期來測試,例如返回值,指針的值和參考變量等等。
這些是使用GoogleMock來模擬設(shè)備驅(qū)動和模仿與真實硬件間的相互作用,運行于Zynq-7000 SoC 的ARM Cortex-A9 MPCore CPU上的測試應用程序的基本原理。然而這個例子只展示了GoogleMock模擬的一個設(shè)備驅(qū)動函數(shù),我們有超過75個函數(shù)以相同的方式被模擬,以便讓我們在應用程序中運行超過100種測試;全都不用目標硬件。
-
驅(qū)動
+關(guān)注
關(guān)注
12文章
1821瀏覽量
85121 -
模擬
+關(guān)注
關(guān)注
7文章
1420瀏覽量
83876 -
變量
+關(guān)注
關(guān)注
0文章
613瀏覽量
28311
發(fā)布評論請先 登錄
相關(guān)推薦
評論