🌱 Triển khai Unit Test đơn giản (3)

🌱 Triển khai Test đơn giản (3)

    Ở post trước, mình đã giới thiệu với các bạn về Unit TestCode Coverage. Vậy, đã bao giờ các bạn tự triển khai một Unit Test đơn giản trong chương trình của mình? 

    ➤ Câu trả lời chắc chắn là có rồi, việc kiểm tra các biến đầu vào của một hàm có hợp lệ hay không, cũng có thể coi là một phần trong Unit Test, với đơn vị nhỏ nhất chính là một function.

    👉 Unit Test trong Thư viện STD STM32

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
    uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
    uint32_t tmpreg = 0x00, pinmask = 0x00;
    /* Check the parameters */
    assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
    assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  
    ...
}

    Đoạn code trên đây rất thường gặp khi các bạn đọc và tìm hiểu thư viện STD của Vi điều khiển STM32, đó là assert_param(condition). Hàm này triển khai để kiểm tra tham số đầu vào của các function trong thư viện STD. 

    Chẳng hạn, đối với hàm GPIO_Init trên, các tham số đầu vào cần nằm trong khoảng cho phép, ví dụ: 

  • GPIOx phải nằm trong khoảng GPIOA → GPIOH, 
  • GPIO_Mode cần nằm trong khoảng 4 mode của GPIO, 
  • GPIO_Pin cần nằm trong khoảng 0 → 15.
    ⇒ Đây là loại kiểm tra tham số đơn giản nhất, điểm mạnh là triển khai nhanh và đơn giản. Các bạn có thể xem triển khai của hàm assert_param dưới đây (Thực chất là một Function-like Macro):
#define assert_param(condition)    if(condition == 0){ for(;;); }

    ➤ Như hàm assert_param, nếu tham số đầu vào không hợp lệ, chương trình sẽ dừng ngay ở hàm assert và không có thông báo cho người dùng, cũng như không thể kiểm tra được những điều kiện phía sau, đây cũng là điểm yếu của kiểu test nhanh này. 

    👉 Triển khai một Test đầy đủ?

    Đối với một test đơn giản, chúng ta có thể dùng hàm assert như trên, khi nào sai thì chương trình dừng lại. Tuy nhiên, đối với một chương trình test đầy đủ, việc test không chỉ dừng lại ở đó. Ở đây mình chỉ nói đến việc Test 1 Function (thực tế có rất nhiều mức độ, phương pháp, ... của việc Testing). Chúng ta cần đảm bảo chương trình test function có đầy đủ các yếu tố sau:

  • Kiểm tra tham số đầu vào (nếu có)
  • Kiểm tra hoạt động của hàm, xem kết quả trả về có đúng với mong muốn hay không (Black Box)
  • Kiểm tra giá trị các thanh ghi sau khi được khi, và hoạt động từng phần của hàm (White Box)
  • Trả về thông báo kết quả test cho người dùng (Ghi kết quả vào flash hoặc thông báo bằng các biến toàn cục). 
    Tất cả những việc trên có thể được thực hiện bởi một Testing Framework. Như mình đã giới thiệu ở những post trước, trong lập trình C nhúng, chúng ta có khá nhiều Testing Framework hỗ trợ: Google Test, EUnit, CUnit, Check, AcuTest, ...

    ➤ Ở đây chúng ta có thể tự triển khai cho mình một chương trình test đơn giản, sử dụng thư viện chuẩn "assert.h" trong C, hoặc tự viết các hàm test giống như hàm assert_param ở trên đây. 

    👉 Ở đây mình sẽ thử triển khai một hàm assert_param cho riêng mình (Không dừng chương trình khi có lỗi sai, trả về kết quả là một biến global).

    Define một biến Global lưu kết quả test

#include <stdbool.h>
bool TEST_RESULT = true;

    Viết hàm assert_param (Hàm này trả về 0 nếu điều kiện là sai, trả về 1 nếu điều kiện đúng)

bool assert_param ( bool Condition )
{
    bool retVal = true;    /* Default NO Error */
    if ( Condition == false )
    {
        retVal = false;    /* Condition is false */
    }
    return retVal;
}

     Gọi hàm assert_param (Thử viết một hàm GPIO_Init())

bool GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
    bool retVal = true;
    uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
    uint32_t tmpreg = 0x00, pinmask = 0x00;
    /* Check the parameters */
    retVal &= assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    retVal &= assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
    retVal &= assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  
    ...
    return retVal;
}

    Gọi hàm GPIO_Init()

GPIO_InitTypeDef GPIO_InitStructure;

/* Enable GPIOA Clock */
RCC_AHB1PeriphClockCmd(LEDControl_SetClock, ENABLE);

/* Select GPIO Push-Pull Output Mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;

TEST_RESULT &= GPIO_Init(LED_PORT, &GPIO_InitStructure);

     Xử lý kết quả Test

if ( TEST_RESULT == false )
{
    /* Ghi vào Flash là 00 - false */
}
else 
{
    /* Ghi vào Flash là 01 - true */
}

    👉 Trên đây là một khai triển chương trình Testing đơn giản mà mình tự triển khai, thực tế các framework cũng hoạt động và sử dụng theo cách tương tự. Ở những post sau mình sẽ cố gắng cùng các bạn tiếp cận các Framework thường được sử dụng trong Embedded C. 

>>>= Follow ngay =<<<

💚 Kênh Youtube Lập trình - Điện tử 💚

Để nhận được những bài học miễn phí mới nhất nhé 😊

    Chúc các bạn học tập tốt 😊

Xem Bài (2) Code Coverage             Xem Bài (4)

Nguyễn Văn Nghĩa

Mình là một người thích học hỏi và chia sẻ các kiến thức về Nhúng IOT.

4 Nhận xét

  1. ra tiếp đi a ơi

    Trả lờiXóa
  2. ra tiếp về phần test này đi anh. Hay quá ạ

    Trả lờiXóa
Mới hơn Cũ hơn