自動化測試
Ren’Py 允許創作者在遊戲中加入自動化測試,以確保對遊戲的修改不會破壞已有功能;對大型遊戲或經常更新的遊戲尤其有用。
測試系統的兩個主要組成部分是 testcase 和 testsuite 語句。
renpy.is_in_test() 函數可用於判斷當前是否正在執行測試。
快速入門
本節內容通過一個最小化樣例,展示在實際測試用例中常用的語句:
run Jump("...")從一個已知的腳本標籤開始運行。advance until screen "choice"自動前進直到出現某個分支選項菜單。click "Choice Text"選擇某個選項。click id "..." until not screen "..."不斷滑鼠點擊直到某個界面消失。.
首先,將下列程式碼加入到需要自動化測試的遊戲腳本中:
screen quickstart_popup():
modal True
frame:
xalign 0.5
yalign 0.5
vbox:
spacing 12
text "快速入門彈出視窗"
textbutton "關閉":
id "quickstart_close"
action Hide("quickstart_popup")
label quickstart_demo:
"歡迎來到測試用例快速入門demo。"
show screen quickstart_popup
"關閉彈出視窗並繼續。"
menu:
"拿走地圖":
"你拾取了地圖。"
"離開":
"你不管地圖並走開了。"
"demo結束。"
return
接著,添加測試用例(testcase)並執行上述代碼,驗證運行結果是否符合預期。
可以把測試用例跟前面的代碼放在同一個腳本文件中,也可以放在不同文件中。例如,新建一個名為 testcases.rpy 的文件放測試用例。
testsuite quick_start:
before testcase:
run Jump("quickstart_demo")
advance until screen "quickstart_popup"
testcase choose_map:
pause 0.5
click id "quickstart_close" until not screen "quickstart_popup"
pause 0.5
advance until screen "choice"
click "拿走地圖"
advance until "你拾取了地圖。"
pause 0.5
testcase leave_map:
pause 0.5
click id "quickstart_close" until not screen "quickstart_popup"
pause 0.5
advance until screen "choice"
click "離開"
advance until "你不管地圖並走開了。"
pause 0.5
Note
demo中的pause語句不是必須的,它們只是能讓人看清測試過程。實際使用時完全可以刪除pause部分讓測試運行更快。
保存文件後,參考 運行測試用例 的內容在啟動器或命令行中運行測試用例。 如果選擇在命令行中運行測試,可以使用下列命令:
./renpy.sh /path/to/game test quick_start
.\lib\py3-windows-x86_64\python.exe renpy.py C:\path\to\game test quick_start
運行測試用例
啟動器運行
如果從啟動器運行測試,選中需要測試的項目,然後點擊“Run Testcases”按鈕。 如果看不到這個按鈕,請嘗試下列操作:
確保遊戲項目中至少包含一個測試用例(testcase)。
點擊“啟動項目”按鈕正常啟動遊戲。
點擊啟動器的“刷新”按鈕。
默認情況下,這樣會運行“global”測試套件。
命令行運行
如果在命令行模式運行測試,就打開一個終端窗口並把路徑切換到Ren’Py的SDK目錄,然後使用 test命令 :
cd /path/to/renpy
./renpy.sh <basedir> test [<testcase>] [options...]
cd C:\path\to\renpy
.\lib\py3-windows-x86_64\python.exe renpy.py <basedir> test [<testcase>] [options...]
- <basedir>
指定項目路徑。
- <testcase>
指定運行的測試用例或測試套件。若未指定,默認運行“global”測試套件。
- --enable_all
運行所有測試用例和測試套件,忽略它們的
enabled設置。
- --overwrite_screenshots
執行 screenshot語句 將覆蓋已存在的同名截圖圖片。
- --hide-header
開始運行測試時禁用header部分。
- --hide-execution {no|hooks|testcases|all}
隱藏測試執行的相關資訊。
--hide-execution hooks隱藏hook資訊,--hide-execution testcases隱藏測試用例和hook,--hide-execution all隱藏所有測試資訊。
- --hide-summary
禁用測試運行完的總結。
- --report-detailed
運行測試時顯示每項測試的詳細資訊。
- --report-skipped
顯示跳過的測試的資訊。該選項應與
--report-detailed一起使用。
testcase語句
testcase 語句用於創建一個具名測試用例,每個用例包含一塊測試語句(見下文)。
測試用例與 Ren’Py 的 label 類似,但有如下區別:
Ren’Py 的 label 語句包含 Ren’Py 代碼,而testcase語句包含的是本頁所列的測試語句,二者互斥。
testcase沒有與return語句等價的語句。
測試語句不能出現在測試塊之外,而label之外可以有其他Ren’Py代碼。
testcase 語句可包含以下特性:
- description
描述該測試用例的字串,會出現在測試報告中。
- enabled
若該表達式結果為
False,則跳過該測試。預設值為True。該特性可用於按條件禁用測試,例如在不支持的平台上。
testcase windows: enabled renpy.windows ... testcase not_on_mobile: enabled not renpy.mobile ...
詳見 跳過測試用例。
- xfail
若該表達式結果為
True,表示預期該測試會失敗;若確實失敗,會將結果標記為xfailed而非failed。預設值為False。
testsuite語句
testsuite 語句用於將測試用例分組。測試套件可包含測試用例、其它測試套件以及hooks(見一下)。
默認測試套件名為 global,若用戶未指定則由Ren’Py自動創建。其中包含遊戲中所有其它頂層測試套件和測試用例。
其特性與 testcase語句 相同。
hooks
testsuite 語句可包含以下hook:
- setup
在當前套件內所有其他測試運行之前執行的test語句塊。
- before testsuite
在當前套件內每個測試套件運行之前重複執行的test語句塊。
- before testcase
在當前套件內每個測試用例運行之前重複執行的test語句塊。
- after testcase
在當前套件內每個測試用例運行之後重複執行的test語句塊;即使測試用例失敗或拋出異常也會執行。
- after testsuite
在當前套件內每個測試套件運行之後重複執行的test語句塊;即使測試套件失敗或拋出異常也會執行。
- teardown
在當前套件內所有測試運行之後執行一次的test語句塊;即使有測試失敗或拋出異常也會執行。
before * 與 after * 類型的hook可能包含以下特性:
一次測試的生命週期
本節說明測試用例與測試套件的執行順序以及hook的調用方式。 以下範例用於說明:
代碼 |
執行順序 |
|---|---|
label test_demo:
"This is a demo for testcases."
"It has a few messages and a menu."
menu:
"First Choice":
"Selected first choice."
"Second Choice":
"Selected second choice."
"Third Choice":
"Selected third choice."
return
testsuite global:
setup:
$ print("global :: setup")
skip until screen "main_menu"
before testsuite:
$ print("global :: before testsuite")
if not screen "main_menu":
run MainMenu(confirm=False)
click "Start"
before testcase:
$ print("global :: before testcase")
after testcase:
$ print("global :: after testcase")
after testsuite:
$ print("global :: after testsuite")
teardown:
$ print("global :: teardown")
exit
testsuite basic:
testcase first_testcase:
$ print("basic :: first_testcase")
advance
testsuite test_choices:
setup:
$ print("test_choices :: setup")
before testcase:
$ print("test_choices :: before testcase")
run Jump("test_demo")
advance until screen "choice"
after testcase:
$ print("test_choices :: after testcase")
teardown:
$ print("test_choices :: teardown")
testcase choice1:
$ print("test_choices :: choice1")
click "First Choice"
testcase choice2:
enabled False
$ print("test_choices :: choice2 (disabled)")
click "Second Choice"
testcase choice3:
$ print("test_choices :: choice3")
click "Third Choice"
|
global :: setup global :: before testsuite global :: before testcase basic :: first_testcase global :: after testcase global :: after testsuite global :: before testsuite test_choices :: setup global :: before testcase test_choices :: before testcase test_choices :: choice1 test_choices :: after testcase global :: after testcase global :: before testcase test_choices :: before testcase test_choices :: choice3 test_choices :: after testcase global :: after testcase test_choices :: teardown global :: after testsuite global :: teardown |
注意,即使測試用例位於嵌套的測試套件中, global :: before testcase 和 global :: after testcase 仍會在每個測試用例之前或之後執行。
要限制hook的作用範圍,可設置其 depth 特性。設為 0 時,hook僅對包含自身的測試套件內的測試生效。
例如:
testsuite global:
before testcase:
depth 0
$ print("Starting a testcase.")
若要讓hook作用於嵌套的測試套件和測試用例,可將其 depth 設為 -1 (無限深度)或正整數(指定深度)。
Note
測試套件執行完畢後,遊戲不會自動關閉,而是將控制權交還玩家並等待輸入。
若要在測試套件結束後關閉遊戲,可在該測試套件的 after hook中使用 exit 測試語句。例如:
testsuite global:
after:
exit
跳過測試用例
若某測試用例被設置為跳過,則不會執行該測試;該測試套件的 before testcase 和 after testcase 設置的hook也不會執行對應的測試用例。
若某測試套件內 所有 測試均被設置為跳過,則其 setup 和 teardown hook也不會執行。
父測試套件的 before testsuite 和 after testsuite 也不會執行。
參數化測試
通過 parameter 特性,可使一個測試用例使用不同參數多次運行。
方法為,指定變數名和對應的取值列表。測試時會使用列表中每個值各運行一次。例如:
testcase click_buttons:
parameter button_name = ["Load", "Save"]
click expression button_name
上述測試將運行兩次,先點擊“Load”按鈕,再點擊“Save”按鈕。
設計參數時需要考慮多個測試套件或測試用例的情況,以及對應的多個hook(包括 setup 和 teardown)每次執行使用的值。
每次執行都會執行 before testcase 和 after testcase hook,且測試報告中會分別列出運行結果。
參數分組
可將多個變數用括號分組,並給出多組取值列表,從而一次指定多個變數。例如:
testcase addition:
parameter (x, y, z) = [ (1, 2, 3), (2, 3, 5), (3, 5, 8) ]
assert eval (x + y == z)
將運行三次,每次使用一組值: ``(x=1, y=2, z=3)`` 、 `(x=2, y=3, z=5) 、 (x=3, y=5, z=8)。
多個參數的組合
若存在多個 parameter 特性,測試將使用所有取值可能的組合各運行一次。例如:
testcase combinations:
parameter a = [1, 2]
parameter b = [3, 4]
parameter c = [5, 6]
assert eval (a + b + c in [9, 10, 11, 12])
將運行八次,對應 (a, b, c) 的所有組合。
可混合使用分組參數與單變數參數。例如:
testcase mixed:
parameter a = [1, 2]
parameter (b, c) = [ (3, 5), (4, 6) ]
assert eval (a + b + c in [9, 10, 11, 12])
會運行四次,分別使用這些組合:
(1, (3, 5)),(1, (4, 6)),(2, (3, 5)),(2, (4, 6))
使用表達式中的參數
創作者可以在任何接受表達式的測試特性中使用參數。
例如,以下測試會運行三次,每次使用不同的 x 值。
當 x 為0或1時測試通過,當 x 為2時預計失敗(xfail):
testcase choice_test:
parameter x = [0, 1, 2]
xfail x == 2
assert eval (x < 2)
也可使用參數名稱來選擇界面或按鈕。例如以下測試會根據 choice_text 的值點擊“first”或“second”選項:
testcase show_menu:
parameter screen_name = ["preferences", "load"]
$ print(f"Showing screen '{screen_name}'")
run ShowMenu(screen_name)
pause until screen screen_name
run Return()
參數可以跟在 expression 後面,用於根據參數名選擇一個按鈕。
testcase click_buttons:
parameter button_name = ["Load", "Save"]
click expression button_name
參數也可在Python代碼塊中使用。例如,以下測試會列印當前 x、 y 的值,並以此作為坐標並點擊對應位置:
testcase param_test:
parameter (x, y) = [(0.0, 0.0), (0.5, 0.5), (1.0, 1.0)]
$ print(f"Clicking at position ({x}, {y})")
click pos (x, y)
參數化的測試套件
測試參數也可以直接應用於整個測試套件。此時套件內所有hook和測試用例會使用每組參數各運行一次。
每次參數化運行都會執行 setup、 before/after testsuite 和 teardown hook。
例如:
testsuite math_tests:
parameter (x, y, z) [ (1, 2, 3), (2, 3, 5), (3, 5, 8) ]
setup:
$ print(f"Running math tests with x={x}, y={y}, z={z}")
testcase addition:
assert eval (x + y == z)
testcase multiplication:
assert eval (x*y == z*y - y*y)
參數可以嵌套,所有參數組合都會被測試。例如:
testsuite parameter_field:
parameter choice_text = ["first", "second"]
testcase param_test2:
parameter (x, y) = [(0.0, 0.0), (0.5, 0.5)]
advance until screen "choice"
click choice_text
click pos (x, y)
Warning
參數按引用傳遞。若修改可變參數(例如列表或字典),則共享同一對象的其他測試也會受到影響。
異常和失敗處理
若在運行測試用例期間發生錯誤:
該測試用例立即停止執行。
包含該測試用例的測試套件的
after testcasehook 會執行。若還有其它測試用例,會繼續執行其他測試用例(包括
before testcasehook)。若沒有更多測試用例,則執行該測試套件的
afterhook。
若在hook執行期間發生錯誤(例如 before testcase):
該測試套件立即停止執行。
若該套件由另一套件調用,父套件會繼續執行。
若不存在父套件,遊戲將結束測試。
測試報告
測試結束後,將結果輸出到控制台,列出所有測試用例及其結果。若啟用了 --print_details 選項,報告中會包含每項測試的詳細資訊。
以下是成功測試“The Question”後的測試報告範例:
測試結果
一項測試可能得到的結果如下:
Passed:測試成功,沒有任何錯誤。
Failed:已執行測試,但某條語句失敗。
XFailed:測試按預期失敗(其
xfail為True且確實失敗)。XPassed:測試預期失敗(
xfail為True),卻通過測試。Skipped:測試被跳過(
enabled為False或存在only True的其它測試)。
通常,passed或xfailed視為成功,failed或xpassed視為不成功。
測試設置
以下配置項可用於修改測試行為:
- _test.maximum_framerate
該項是一個布林值。用於指定是否在測試時使用最大幀率模式。解鎖後幀率可能會超過顯示器刷新率。 預設值為
True。
- _test.timeout
該項是一個浮點數。用於指定單條測試語句等待條件滿足的最長時間(秒)。預設值為
10.0。可在支持
timeout的語句(如assert、until)上單獨指定超時時間並覆蓋該值。
- _test.force
該項是一個布林值。用於指定是否在
renpy.config.suppress_underlay為True時仍強制繼續測試。預設值為False。
- _test.transition_timeout
該項是一個浮點數。用於指定等待轉場完成的最長時間,單位為秒。若超時則跳過轉場繼續測試。預設值為
5.0。
- _test.screenshot_directory
該項是一個字串。用於指定截圖保存目錄。預設值為
tests/screenshots。
測試語句
測試語句是測試用例的基礎結構,大致分為三類:命令語句、條件/選擇器語句、控制語句。
基本命令
advance語句
Type: Command
- advance
使遊戲推進到下一行對話。
advance
advance until screen "choice"
exit語句
Type: Command
- exit
不彈出確認界面直接退出遊戲,退出前不存檔。
if eval need_to_confirm:
# 彈出確認界面,若 config.autosave_on_quit 為 True 則自動存檔。
run Quit(confirm=True)
if eval persistent.quit_test_using_action:
# 不彈出確認界面,若 config.autosave_on_quit 為 True 則自動存檔。
run Quit(confirm=False)
exit # 不彈出確認界面,退出前不存檔。
pass語句
Type: Command
- pass
不執行任何操作。占位用,用於空測試用例。
testcase not_yet_implemented:
pass
pause語句
Type: Command
- pause [time (float)]
根據指定的時間暫停測試。暫停持續時間單位為秒。與 pause語句 類似,但必須給出時間。作為 until 子句時可以不寫時間。
pause 5.0
pause until screen "inventory"
run語句
Type: Command
- run <action>
執行指定的 界面行為 (或行為列表)。
當包含該行為(或行為列表)的按鈕變為可點擊時繼續。
testcase chapter_3:
run Jump("chapter_3")
skip語句
Type: Command
- skip [fast]
使遊戲開始快進。若當前在菜單中則先返回遊戲,否則僅開啟快進。
If fast is provided, the game will skip directly to the next menu choice.
若啟用 fast,將直接快進到下一個選項。
skip
skip fast
skip until screen "choice"
滑鼠命令
click語句
Type: Command
- click [button (int)] [selector] [expression (expr)] [pos (x, y)]
在螢幕上執行模擬點擊。可選特性如下:
button指定模擬滑鼠的哪個按鍵。該項是一個整數,預設值為1。 1為左鍵,2為右鍵,3為中鍵,4和5為部分滑鼠的額外按鍵。Ren’Py通常只響應 1 和 2。expression指定一個測試表達式,用於標記點擊目標。其遵守 pattern使用從句的規則。
若指定了 selector 和/或 pos,會先按 move語句 的規則移動虛擬測試滑鼠再發送點擊。
使用 通配符 子句的點擊行為,若未指定 pos,click語句會尋找“中性”位置,使點擊不會落在可獲得焦點的元素上。
# 點擊滑鼠當前位置。
click
# 點擊帶有指定文本的按鈕。
click "Start"
# 滑鼠右鍵點擊指定目標。
click id "inventory_button" button 2
# 點擊指定目標中心。
click id "inventory_button" pos (0.5, 0.5)
# 點擊使用表達式指定的目標。
$ button_name = "Load"
click expression button_name
drag語句
Type: Command
- drag <[selector] [pos (x, y)]> to <[selector] [pos (x, y)]> [button (int)] [steps (int)]
模擬螢幕上的滑鼠拖拽。可選特性如下:
拖拽起點(
to之前),可使用selector和/或pos特性,與 move語句 的規則相同。拖拽終點(
to之後),同樣可使用selector和/或pos特性,與 move語句 的規則相同。button指定拖拽使用的滑鼠按鍵。該項是一個整數,預設值為1。steps指定拖拽中間步數。該項是一個整數,預設值為10。步數越多越平滑但更耗時。
drag id "item_icon" to id "inventory_slot_3" button 1 steps 20
drag pos (100, 200) to pos (400, 500) button 1
drag id "item_icon" pos (0.5, 0.5) to pos (300, 400) steps 5
drag pos (50, 50) to id "inventory_slot_1"
drag pos (50, 50) to pos (150, 150)
move語句
Type: Command
- move [selector] [pos (x, y)]
將虛擬測試滑鼠移動到螢幕上的指定位置。
If a selector is given, and:
若指定了 selector:
若指定
pos,該項是一個相對於選擇器的坐標,滑鼠移動對應坐標。若未指定
pos,會嘗試找到點擊後可使該選擇器獲得焦點的像素。該項也會參考其他東西,比如focus_mask等。
If no selector is given, and:
若未指定 selector:
若指定
pos,該項是一個相對於螢幕的坐標,滑鼠移動對應坐標。若未指定
pos,會拋出錯誤。
# 移動到 `back_btn` 內的隨機可點擊點
move id "back_btn"
# 移動到 `back_btn` 的中心點
move id "back_btn" pos (0.5, 0.5)
# 移動到距離 `back_btn` 的左上角右側20像素下方10像素的位置。
move id "back_btn" pos (20, 10)
# 移動到螢幕的右上角
move pos (1.0, 0.0)
# 移動到距離螢幕的左上角右側20像素下方10像素的位置。
move pos (20, 10)
scroll語句
Type: Command
- scroll [amount (int)] [selector] [pos (x, y)]
Simulates a scroll event. It takes the following optional properties:
模擬滾動事件。可選特性如下:
amount表示滾動“格數”。該項是一個整數,預設值為1。正數向下滾動,負數向上滾動。若指定了
selector和/或pos,會先執行 move語句 移動滑鼠再發送滾動。scroll “bar” scroll id “inventory_scroll” scroll amount 10 id “inventory_scroll” pos (0.5, 0.5) scroll # 向下滾動到當前滑鼠位置
Note
僅模擬滑鼠滾輪事件;也可考慮使用 界面行為(action)、值(value)和函數 中的Scroll行為。
run Scroll("inventory_scroll", "increase", amount="step", delay=1.0)
鍵盤命令
keysym語句
Type: Command
- keysym <keysym> [selector] [pos (x, y)]
模擬鍵盤按鍵事件,包括 config.keymap 中的鍵位。
若指定了 selector 和/或 pos,會先執行 move 語句 移動滑鼠再發送按鍵命令。
keysym "skip"
keysym "help"
keysym "ctrl_K_a"
keysym "K_BACKSPACE" repeat 30
keysym "pad_a_press"
type語句
Type: Command
- type <string> [selector] [pos (x, y)]
將給定字串模擬鍵盤方式輸入。
若指定了 selector 和/或 pos,會先執行 move 語句 移動滑鼠再發送文本。
type "Hello, World!"
條件語句
條件語句用於判斷某前提條件是否成立。用於接受條件的測試語句有 if、 assert、 until。
布林值
測試中可根據需要使用布林值 True 和 False。這兩個值可以直接使用。
if True:
click "Start"
if False:
click "Settings" # 不會執行這條,因為判斷條件始終為False。
布爾運算
條件語句支持 not、 and、 or 運算符。表達式外層可加括號也可不加。
assert eval (renpy.is_in_test() and screen "main_menu")
advance until "ask her right" or label "chapter_five"
click "Next" until not screen "choice"
eval語句
Type: Condition
- eval <expression>
根據給定的Python表達式計算結果。僅用於 assert、 if、 until 等接受條件的測試語句內部。
assert eval (renpy.is_in_test() and ("Ren'Py" in renpy.version_string))
Note
帶$符號的單行Python語句與eval語句的區別:
eval 不能單獨成行,必須放在
if、until等語句內;帶$符號的單行Python語句是單獨一行。帶$符號的單行Python語句可執行任意Python語句(不必有返回值,如
$ import math);eval語句必須有返回值。
label語句
Type: Condition
- label <labelname>
檢查上一條執行的測試語句是否已到達指定的Ren’Py腳本標籤。
參考一下例:
run Jump("chapter_1")
assert label chapter_1 # 成功
assert label chapter_1 # 失敗
第一條 assert 語句成功是因為上一條語句 run Jump("chapter_1") 正好到達指定腳本標籤 chapter_1。
第二條 assert 失敗是因為自第一條 assert 語句之後,未再次到達指定腳本標籤 chapter_1。
因此下例不會得到成功的結果:
run Jump("chapter_1")
advance repeat 3
assert label chapter_1 # 失敗
(譯者註:測試label語句應該只在具有分支選項的測試後面使用,用於確認分支走向符合預設劇本。)
選擇器語句
選擇器語句用於判斷某元素是否顯示在當前螢幕上,並用於後續操作。
選擇器是一類特殊的條件語句。
可視組件選擇器
Type: Condition, Selector
檢查指定id的界面或元素是否當前已顯示。
其可使用以下特性:
- screen <name>
要檢查的界面名稱。
- id
要檢查的元素id。
- layer
界面所在圖層。未指定圖層時根據界面名稱自動確定。
if screen "main_menu":
click "Start"
advance until id "inventory_viewport" layer "overlay"
click "Close" until not id "close_button"
文本選擇器
Type: Condition, Selector
- "<text>" [raw]
- expression <expression> [raw]
text 選擇器接受一個字串,表示需要在螢幕上尋找的目標。
尋找方式是遍歷所有可獲得焦點的元素(通常是按鈕和主對話文本框),尋找它們自身的文本和 alt 特性文本。
搜索時不區分大小寫且取最短匹配。
例如,搜索 "log" 且螢幕裡有 "CATALOG" 和 "illogical" 時,會匹配到 "CATALOG"。
若指定了 raw,在腳本原文內搜尋,在多語言處理與 插值 之前。
否則在螢幕上顯示的文字上搜尋,在多語言處理與 插值 之後。
若指定了 expression,則會根據表達式計算結果字串決定待搜索目標。
# 這段文字可能會出現在按鈕上
skip until "Start Game"
# 這段文字可能會出現在主對話文本框中
advance until "Hey, that's not fair!"
# 不區分大小寫的搜索
assert "AsK HeR RighT AwaY"
# 搜尋未插值的文本
assert "Welcome, Eileen!"
assert "Welcome, [player_name]!" raw
# 在修改語言後,搜尋未經多語言處理的文本
run Language("japanese")
assert "スタート"
assert "Start" raw
控制語句
控制語句用於控制測試執行流程。
assert語句
Type: Control
- assert <condition> [timeout (float)] [xfail (bool)]
該語句接受一個條件語句,執行時遇到前提條件不滿足則拋出 enpyTestAssertionError。
若指定了 timeout,等待對應秒數就是前提條件;如果前提條件超時則該語句執行失敗。
若 xfail 設置為 True,表示預期assert語句執行失敗。
這樣該語句是否成功與條件是否滿足的結果相反。如果前提條件滿足,該語句執行失敗;反之則成功。
assert screen "main_menu"
assert eval some_function(args)
assert id "start_button" timeout 5.0
See also
for語句
Type: Control
- for <variable> in <iterable>
該語句會對所提供可迭代對象中的每一項執行一段test語句塊。
使用 break 和 continue 語句可以控制循環流程。
樣例:
# 點擊“Next”三次。
for _ in range(3):
click "Next"
# 依次點擊每一個選項。如果商店未解鎖則跳過點擊“Trade”選項。
for choice in ["Talk", "Trade", "Leave"]:
if eval (choice == "Trade" and not persistent.shop_unlocked):
continue
click expression choice
if screen "shop":
break
# 點擊stats界面的每一個標籤。如果任務系統未解鎖則跳過點擊“Quests”標籤。
for stat_tab in ["Stats", "Skills", "Quests"]:
click expression stat_tab
if eval (stat_tab == "Quests" and not persistent.quests_enabled):
continue
assert text stat_tab timeout 2.0
if語句
Type: Control
- if <condition>
當指定條件滿足時執行其內部的測試語句塊。
樣例:
if label "chapter_five":
exit
if eval (persistent.should_advance and i_should_advance["now"]):
advance
elif 和 else 語句可為 if 語句添加分支。
if eval persistent.should_advance:
advance
elif eval i_should_advance["now"]:
advance
else:
click "Start"
repeat語句
Type: Control
- <command> repeat <number> [timeout (float)]
將某條語句重複執行指定次數。
repeat 左側的內容是需要執行的語句,右側是重複次數。
click "+" repeat 3
keysym "K_BACKSPACE" repeat 10
advance repeat 3
screenshot語句
Type: Command
- screenshot <path> [max_pixel_difference (int or float)] [crop (x, y, width, height)]
對當前畫面截圖並保存到指定路徑。
path:保存路徑(與_test.screenshot_directory的相對路徑),可含副檔名,僅支持.png。max_pixel_difference:與已有截圖允許的像素差異數(整數)或比例(浮點),預設為0。crop:裁剪區域(x, y, width, height),坐標必須為整數。
若項目在git倉庫中,當前提交的哈希會自動追加到檔案名(如 @{hash}.png),便於開發者追蹤截圖變化。
若文件已存在,會將當前截圖與已有文件比較。差異超過 max_pixel_difference 設置的像質數會拋出RenpyTestScreenshotError。
若要覆蓋已有截圖,可刪除文件或在運行測試時使用 --overwrite_screenshots 命令行選項。
screenshot "screens/main_menu.png"
screenshot "screens/inventory" max_pixel_difference 0.01
screenshot "button.png" crop (10, 10, 100, 50)
參數化測試中可以使用多張截圖:
testcase screen_tester:
parameter screen_name = ["inventory", "stats", "map"]
run Show(screen_name)
screenshot f"screens/{screen_name}.png"
until語句
Type: Control
- <command> until <condition> [timeout (float)]
重複執行某個語句直到滿足預設條件。由 until 連接左右兩側,左側是重複執行的語句,右側是條件。
當右側條件滿足時進入下一條語句。否則不斷重複執行左側語句直到條件滿足。
若指定了 timeout,會在對應時間內等待條件滿足,單位為秒。
如果超時仍未滿足則拋出RenpyTestTimeoutError。
該超時會暫時覆蓋全局 _test.timeout。
advance until screen "choice"
click "Next"
advance until label "chapter_5"
skip until screen "inventory" timeout 20.0
while語句
Type: Control
- while <condition>
該語句會在提供的條件(condition)滿足時不斷執行一段test語句塊。
使用 break 和 continue 語句可以控制循環流程。
樣例:
$ should_advance = True
while eval should_advance:
advance
if screen "main_menu":
break
$ should_advance = some_evaluation_function()
Python語句塊和帶$符號的單行語句
測試用例內可加入 python語句塊 或 帶$符號的單行語句。
與普通Ren’Py代碼不同,這裡的Python語句塊不接受 in substore 參數,但可使用 hide 關鍵字。
二者都可執行任意Python語句。
init代碼會在測試運行前執行,因此 init python 中定義的函數和類可在測試的Python語句塊和帶$符號的單行語句中調用。例如:
init python in test:
def afunction():
if renpy.is_in_test():
return "test"
return "not test"
testcase default:
$ print(test.afunction()) # 在控制台輸出結果