用测试驱动开发状态机
用測試驅動開發狀態機
Developing state machines with test-driven development
由于狀態機模型在嵌入式系統中的廣泛應用,本文探討了在測試驅動開發(TDD)方法下開發狀態機軟件的幾種策略。本出版物首先解釋基本的狀態機概念和TDD技術。最后介紹了用TDD方法開發C語言編寫的狀態機軟件的簡單而有序的方法。
SM模型由狀態、轉換和動作組成。雖然狀態是系統或元素的條件,但轉換是從一個狀態到另一個狀態的路徑,通常由將前一個(源)狀態與后續(目標)狀態連接起來的相關事件啟動。元素執行的實際行為在actions中表示。
在UML狀態機中,動作可能與進入狀態、退出狀態、轉換本身或所謂的“內部轉換”或“反應”相關聯。所有狀態機的形式(包括UML狀態機)普遍假設狀態機在開始處理下一個事件之前完成了對每個事件的處理。這個模型稱為運行完成(RTC)。在這個模型中,操作可能需要時間,但是任何掛起的事件都必須等到狀態機完成——包括整個退出操作、轉換操作和按順序排列的進入操作序列。
在討論使用TDD開發狀態機的策略之前,值得一提的是它的定義、重要性和應用。
首先,TDD是一種逐步構建軟件的技術。簡單地說,沒有首先編寫失敗的單元測試,就不會編寫任何生產代碼。測試很小。測試是自動化的。測試驅動是合乎邏輯的,也就是說,TDD從業者在測試中表達了代碼的期望行為,而不是深入到產品代碼中(稍后再進行測試)。一旦測試失敗,TDD實踐者編寫代碼,使測試通過。在TDD過程的核心,有一個由短步驟組成的重復循環,稱為“TDD微循環”。
下表中TDD循環的步驟基于James Grenning的“嵌入式C的測試驅動開發”一書:
增加一個小測試。
運行所有測試,如果新的測試失敗,它甚至可能無法編譯。
做一些小的改變來通過測試。
運行所有的測試并證明新的測試是否通過。
重構以消除重復并提高表現力。
讓我們使用圖1中的圖來找到一種使用TDD開發狀態機的簡單方法。當狀態機初始化時,它從StateA狀態開始。接收到Alpha事件后,狀態機將通過按順序執行xStateA()、effect()和nStateB()操作轉換到StateB狀態。那么,如何測試圖1中的SM來確定它的行為是否恰當呢?
Figure 1. Basic state machine
測試SM(如圖1所示)的最傳統和最簡單的方法主要是驗證SMUT(被測狀態機)的狀態轉換表。這使得每個狀態都有一個測試用例,其中SMUT被感興趣的事件刺激,以驗證觸發了哪些轉換。同時,這意味著要檢查每個激發的轉換的目標狀態和執行的操作。如果一個操作足夠復雜,則更適合為此創建一個特定的測試用例。(本文使用單元測試測試狀態機深入解釋了這種策略)。
根據xUnit模式,每個測試用例分為四個不同的階段:
安裝程序建立測試的先決條件,例如SM的當前狀態(StateA)、要處理的事件(Alpha)和預期的測試結果,這些結果是轉換目標狀態(StateB)和要執行的操作的排序列表(xStateA()、effect()和nStateB())。
運用Alpha事件刺激狀態機。
驗證檢查獲得的結果。
Cleanup在測試之后將被測試的狀態機返回到其初始狀態。它是可選的。
上面提到的策略已經足夠開發一個使用TDD的SM。但是,在某些情況下,需要不止一個轉換來檢查功能。這是因為效果只有在后續轉換的一系列動作中才可見,這意味著功能涉及一組狀態、事件和SMUT的轉換。在這些情況下,測試一個完整的功能場景比測試孤立的狀態轉換更合適。因此,與前面提到的策略相比,測試用例更具功能性,更不抽象。
讓我們使用圖2中的狀態機來研究這個概念。
Figure 2. The DoWhile state machine
圖2顯示了一個名為DoWhile的狀態機,它為一個類似于“do while”的執行循環建模。DoWhile是使用Yakindu Statechart工具繪制的。創建DoWhile時調用“x=0”和“output=0”操作,這些操作設置所有DoWhile屬性的默認值。循環迭代次數必須通過“x++”或“x=(x>0)設置?x–:x’操作。“i=0”操作為循環建立初始條件。循環體由“i++”操作執行,它保持循環迭代,然后通過“i==x”保護通過choice偽狀態來計算終止條件。如果為真,則再次計算循環體,依此類推。當終止條件變為false時,循環終止執行“output=i”操作。
在開發新功能之前創建一個測試列表是很有幫助的。測試列表源于規范,它定義了應該做什么的最佳遠景。由于不需要完美,前一個列表只是一個臨時文檔,以后可以修改。DoWhile的初始測試列表如下所示:
初始化SM后,默認設置所有數據
增量X屬性
遞減X屬性
可以執行單個迭代循環
可以執行多次迭代循環
可以執行非迭代循環
檢查越界值
為了開發新的狀態機,Ceedling和Unity將與最簡單但清晰的編程技術一起使用:使用“switch case”語句。Ceedling是一個為C項目生成整個測試和構建環境的構建系統;Unity是一個用于C項目的輕量級、可移植的表達性C語言測試工具。
兩個文件表示這個狀態機,DoWhile.h和DoWhile.c,因此它們是正在測試的源代碼。代碼清單1顯示了test_DoWhile.c文件的一個片段,該文件通過應用前面提到的策略來實現上面的測試列表。為了使本文保持簡單,代碼清單1只顯示了測試用例:“可以執行單個迭代循環”,它由test_SingleIteration()實現。
代碼和模型均在中提供https://github.com/leanfrancucci/sm-tdd.git存儲庫。
Code Listing 1: Single iteration test
這個測試驗證了DoWhile可以正確地執行一次迭代。為此,test_SingleIteration()通過調用DoWhile_init()初始化DoWhile狀態機(第96行)。它為零設置循環執行的迭代次數。之后,DoWhile就可以通過調用DoWhile_dispatch()來處理事件。要執行一次迭代,test_SingleIteration()將Up事件發送到DoWhile(第97行)。此事件將迭代次數增加到1。測試通過發送開始事件(第98行)來啟動循環,然后發送Alpha事件,以便在執行單個迭代時(第99行)。通過驗證out屬性的值是否等于執行的迭代次數(第101行)來檢查這一點。最后,DoWhile必須保持StateC狀態(第102行)。
為了證明DoWhile可以執行多個迭代,test_ngleIteration()被擴展,如代碼清單2所示。
Code Listing 2: Multiple iteration test
代碼清單3中顯示的test_noneetribution()測試檢查DoWhile在接收Alpha事件時不執行任何迭代,而沒有事先通過Up事件設置迭代次數。
Code Listing 3: Non iteration test
盡管DoWhile的實現細節不是本文的目標,但代碼清單4和代碼清單5顯示了DoWhile.c和DoWhile.h文件的一部分。這些文件實際上代表了一個在C語言中使用“switch case”語句的DoWhile實現。
Code Listing 4: Fragment of DoWhile implementation
Code Listing 5: Fragment of DoWhile specification
上面介紹的兩種策略都提供了使用TDD開發狀態機軟件的簡單有序的方法,TDD是提高軟件質量的最重要方法之一。
第一種策略主要是驗證SMUT的狀態轉移表。此方法為每個狀態生成一個測試用例。另一種策略是為一個完整的功能場景實現一個測試用例,該場景通常涉及SMUT的一組狀態、事件和動作。第二種策略使測試比第一種更具功能性,更不抽象。盡管這些策略獨立于特定類型的系統、編程語言或工具,但它們在嵌入式系統中非常有用,因為它們中的許多策略都具有通常在一個或多個狀態機中定義的基于狀態的行為。
選擇C語言是因為它是嵌入式軟件開發中最流行的語言之一。因此,為了在該語言中應用TDD,選擇了Ceedling和Unity。總之,與傳統方法相比,這些方法無疑允許開發人員以更簡單和有序的方式構建更靈活、可維護和可重用的軟件。
總結
以上是生活随笔為你收集整理的用测试驱动开发状态机的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 选择最合适的预测性维护传感器
- 下一篇: 软件驱动的功率分析