簡體   English   中英

CppUTest未在目標上運行-如何“偽造”注冊

[英]CppUTest not running on target - How to “fake” register

我正在對嵌入式C代碼進行單元測試而沒有在目標硬件上運行,這是代碼的一部分:

uint8 tempReadback = 0;

write_to_i2c( msg->addres, msg->value );


     tempReadback = read_from_i2c( msg->addres);

     if( tempReadback == msg->value )
     {
        somethingA;
     }

     else
     {
        somethingB;
     } 

函數write_to_i2c()將值寫入特定的寄存器。 函數read_from_i2c()從寄存器中讀回該值。 此外,我使用變量tempReadback來比較讀回的值是否與寫入的值相同。 到目前為止,一切正常,並且可以在目標硬件上使用。 現在,我正在執行Uni測試,而不在目標硬件(循環中的軟件)上運行代碼。 這意味着,表達式tempReadback == msg-> value永遠不會為true(tempReadback為0),並且每次都會在語句somethingB中運行 有沒有辦法偽造讀回的寄存器? 我正在使用CppUTest作為框架。

會感激的!

CppUTest非常適合嵌入式C開發,因為它是唯一一個可以模擬自由函數的測試框架(在您的情況下為write_to_i2c()read_from_i2c() )。

現在,您確實應該閱讀CppUTest文檔或出色的書籍《嵌入式C的測試驅動開發》

無論如何,以下代碼顯示了如何執行此操作。

您的被測單元(UUT),以可編譯的方式編寫(下次您對SO提出問題時,請付出努力):

#include "temperature.h"
#include "i2c.h"

void somethingA(void) { }
void somethingB(void) { }

void temperature_do(uint8_t address, uint8_t value) {
    write_to_i2c(address, value);
    const uint8_t tempReadback = read_from_i2c(address);
    if (tempReadback == value) {
        somethingA();
    } else {
        somethingB();
    }
}

如您所寫,我們需要“偽造”,或更確切地說,我們需要“模擬” write_to_i2c()read_from_i2c() 我們將模擬文件放在一個單獨的文件中,例如i2c_mock.cpp,以便在構建單元測試時,我們針對該模擬文件而不是針對實際的實現進行鏈接:

extern "C" {
#include "i2c.h"
};

#include "CppUTestExt/MockSupport.h"

void write_to_i2c(uint8_t address, uint8_t value) {
    mock().actualCall(__FUNCTION__)
        .withParameter("address", address)
        .withParameter("value", value);
}

uint8_t read_from_i2c(uint8_t address) {
    mock().actualCall(__FUNCTION__)
        .withParameter("address", address);
    uint8_t ret = mock().returnIntValueOrDefault(0);
    return ret;
}

有關詳細信息,請參考CppUMock文檔。 這只是經典的CppUMock樣板。

最后一部分是單元測試:

extern "C" {
#include "temperature.h" // UUT
};

#include "CppUTest/TestHarness.h"
#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTestExt/MockSupport.h"

TEST_GROUP(Temperature)
{
    void setup() {}
    void teardown() {
        mock().checkExpectations();
        mock().clear();
    }
};

TEST(Temperature, somethingA)
{
    const uint8_t value = 10;
    mock().ignoreOtherCalls();
    mock().expectOneCall("read_from_i2c").ignoreOtherParameters()
        .andReturnValue(value);
    temperature_do(10, value);
}

TEST(Temperature, somethingB)
{
    const uint8_t value = 10;
    mock().ignoreOtherCalls();
    mock().expectOneCall("read_from_i2c").ignoreOtherParameters()
        .andReturnValue(value+1);
    temperature_do(10, value);
}

int main(int argc, char** argv) {
    return CommandLineTestRunner::RunAllTests(argc, argv);
}

該UT實際上將提供100%的分支覆蓋率。 同樣,我無法解釋所有細節。 如果觀察並比較測試用例somethingAsomethingB ,您將看到使UUT進入調用somethingA()的路徑並一次進入調用somethingB()的路徑所需要的內容。

讓我們舉個例子

mock().expectOneCall("read_from_i2c")
    .ignoreOtherParameters()
    .andReturnValue(value+1);

在這里,我們說的是CppUmock期望對函數read_from_i2c()的調用,忽略參數是什么,這對返回value + 1 (或其他您喜歡的不同於value )至關重要。 這將導致UUT進入調用somethingB()的路徑。

快樂的嵌入式C開發和快樂的單元測試!

我看了一下您提議的有關TDD和模擬對象的書。 好的,據我所知,例如在這一行中(已經創建了虛擬機):

mock().expectOneCall("read_from_i2c").ignoreOtherParameters()
    .andReturnValue(value);
temperature_do(10, value);

程序使用自己定義的參數“跳”到模擬的“ read_from_i2c”函數(來自i2c_mock.cpp),而不是被測單元的真實參數? 還是我們真的從被測單元調用了我們的函數,但是我們使用模擬中定義的參數來操縱該函數?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM