本教程將指導您創建“hello world”Trade Federation (TF) 測試配置,並向您介紹 TF 框架。從開發環境開始,您將創建一個簡單的配置並添加功能。
本教程將測試開發過程呈現為一組練習,每個練習都包含幾個步驟,演示如何構建和逐步完善您的配置。提供了完成測試配置所需的所有示例代碼,並且每個練習的標題都帶有一個描述該步驟中涉及的角色的字母:
- D開發人員
- 我為積分器
- R代表測試運行器
完成本教程後,您將擁有一個正常運行的 TF 配置,並了解 TF 框架中的許多重要概念。
成立貿易聯盟
有關設置 TF 開發環境的詳細信息,請參閱機器設置。本教程的其餘部分假設您打開了一個已初始化為 TF 環境的 shell。
為簡單起見,本教程演示瞭如何將配置及其類添加到 TF 框架核心庫。通過編譯 tradefed JAR,然後針對該 JAR 編譯您的模塊,這可以擴展到在源代碼樹之外開發模塊。
創建測試類 (D)
讓我們創建一個 hello world 測試,它只是將消息轉儲到標準輸出。 tradefed 測試通常實現IRemoteTest接口。這是 HelloWorldTest 的實現:
package com.android.tradefed.example; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.invoker.TestInformation; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.result.ITestInvocationListener; import com.android.tradefed.testtype.IRemoteTest; public class HelloWorldTest implements IRemoteTest { @Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World!"); } }
將此示例代碼保存到<tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java
並從您的 shell 重建 tradefed:
m -jN
請注意,上面示例中的CLog.i
用於將輸出定向到控制台。登錄 (D, I, R)中描述了有關登錄 Trade Federation 的更多信息。
如果構建不成功,請諮詢機器設置以確保您沒有錯過任何步驟。
創建配置(一)
Trade Federation 測試通過創建Configuration來執行,這是一個 XML 文件,指示 tradefed 運行哪個(或多個)測試,以及要執行的其他模塊以及以什麼順序執行。
讓我們為 HelloWorldTest 創建一個新配置(注意 HelloWorldTest 的完整類名):
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> </configuration>
將此數據保存到本地文件系統上任何位置的helloworld.xml
文件中(例如/tmp/helloworld.xml
)。 TF 將解析配置 XML 文件(又名config ),使用反射加載指定的類,實例化它,將其轉換為IRemoteTest
,並調用其run
方法。
運行配置(R)
在您的 shell 中,啟動 tradefed 控制台:
tradefed.sh
確保設備已連接到主機並且對 tradefed 可見:
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
可以使用run <config>
控制台命令執行配置。嘗試:
tf> run /tmp/helloworld.xml 05-12 13:19:36 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548 Hello, TF World!
您應該會看到“Hello,TF World!”在終端上輸出。
您可以通過在控制台提示符中使用list invocations
或li
來確認命令已完成運行,並且它應該不打印任何內容。如果命令當前正在運行,它們將顯示如下:
tf >l i Command Id Exec Time Device State 10 0m:00 [876X00GNG] running stub on build(s) 'BuildInfo{bid=0, target=stub, serial=876X00GNG}'
將配置添加到類路徑(D、I、R)
為了方便部署,您還可以將配置捆綁到 tradefed JAR 本身。 Tradefed 自動識別放置在類路徑上的配置文件夾中的所有配置。
為了說明,將helloworld.xml
文件移動到 tradefed 核心庫 ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml
)。重建 tradefed,重啟 tradefed 控制台,然後讓 tradefed 顯示類路徑中的配置列表:
tf> list configs […] example/helloworld: Runs the hello world test
您現在可以使用以下命令運行 helloworld 配置:
tf> run example/helloworld 05-12 13:21:21 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548 Hello, TF World!
與設備交互(D、R)
到目前為止,我們的 HelloWorldTest 沒有做任何有趣的事情。 Tradefed 的專長是使用 Android 設備運行測試,因此讓我們將 Android 設備添加到測試中。
測試可以通過調用IRemoteTest#run
方法時由框架提供的TestInformation
獲取對 Android 設備的引用。
讓我們修改 HelloWorldTest 打印消息以顯示設備的序列號:
@Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber()); }
現在重建 tradefed 並檢查設備列表:
tradefed.sh
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
記下列為可用的序列號;那就是應該分配給 HelloWorld 的設備:
tf> run example/helloworld 05-12 13:26:18 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548 Hello, TF World! I have device 004ad9880810a548
您應該會看到顯示設備序列號的新打印消息。
發送測試結果 (D)
IRemoteTest
通過調用提供給#run
方法的ITestInvocationListener實例上的方法來報告結果。 TF 框架本身負責報告每個調用的開始(通過ITestInvocationListener#invocationStarted )和結束(通過ITestInvocationListener#invocationEnded )。
測試運行是測試的邏輯集合。為了報告測試結果, IRemoteTest
負責報告測試運行的開始、每個測試的開始和結束以及測試運行的結束。
這是 HelloWorldTest 實現在單個失敗測試結果時可能看起來的樣子。
@Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber()); TestDescription testId = new TestDescription("com.example.TestClassName", "sampleTest"); listener.testRunStarted("helloworldrun", 1); listener.testStarted(testId); listener.testFailed(testId, "oh noes, test failed"); listener.testEnded(testId, Collections.emptyMap()); listener.testRunEnded(0, Collections.emptyMap()); }
TF 包含幾個可以重用的IRemoteTest
實現,而不是從頭開始編寫自己的實現。例如, InstrumentationTest可以在 Android 設備上遠程運行 Android 應用程序的測試,解析結果,並將這些結果轉發給ITestInvocationListener
)。有關詳細信息,請參閱測試類型。
存儲測試結果(一)
TF 配置的默認測試偵聽器實現是TextResultReporter ,它將調用結果轉儲到標準輸出。為了說明,運行上一節中的 HelloWorldTest 配置:
./tradefed.sh
tf> run example/helloworld 04-29 18:25:55 I/TestInvocation: Invocation was started with cmd: /tmp/helloworld.xml 04-29 18:25:55 I/TestInvocation: Starting invocation for 'stub' with '[ BuildInfo{bid=0, target=stub, serial=876X00GNG} on device '876X00GNG'] 04-29 18:25:55 I/HelloWorldTest: Hello, TF World! I have device 876X00GNG 04-29 18:25:55 I/InvocationToJUnitResultForwarder: Running helloworldrun: 1 tests 04-29 18:25:55 W/InvocationToJUnitResultForwarder: Test com.example.TestClassName#sampleTest failed with stack: oh noes, test failed 04-29 18:25:55 I/InvocationToJUnitResultForwarder: Run ended in 0 ms
要將調用結果存儲在其他位置(例如文件中),請使用配置中的result_reporter
標記指定自定義ITestInvocationListener
實現。
TF 還包括XmlResultReporter偵聽器,它將測試結果以類似於ant JUnit XML 編寫器使用的格式寫入 XML 文件。要在配置中指定 result_reporter,請編輯…/res/config/example/helloworld.xml
配置:
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> <result_reporter class="com.android.tradefed.result.XmlResultReporter" /> </configuration>
現在重建 tradefed 並重新運行 hello world 示例:
tf> run example/helloworld 05-16 21:07:07 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548 Hello, TF World! I have device 004ad9880810a548 05-16 21:07:07 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_2991649128735283633/device_logcat_6999997036887173857.txt 05-16 21:07:07 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_2991649128735283633/host_log_6307746032218561704.txt 05-16 21:07:07 I/XmlResultReporter: XML test result file generated at /tmp/0/inv_2991649128735283633/test_result_536358148261684076.xml. Total tests 1, Failed 1, Error 0
請注意說明已生成 XML 文件的日誌消息;生成的文件應如下所示:
<?xml version='1.0' encoding='UTF-8' ?> <testsuite name="stub" tests="1" failures="1" errors="0" time="9" timestamp="2011-05-17T04:07:07" hostname="localhost"> <properties /> <testcase name="sampleTest" classname="com.example.TestClassName" time="0"> <failure>oh noes, test failed </failure> </testcase> </testsuite>
您還可以編寫自己的自定義調用偵聽器——它們只需要實現ITestInvocationListener接口。
Tradefed 支持多個調用偵聽器,因此您可以將測試結果發送到多個獨立的目的地。為此,只需在配置中指定多個<result_reporter>
標記。
日誌記錄(D、I、R)
TF 的日誌記錄功能包括:
- 從設備捕獲日誌(又名設備 logcat)
- 記錄主機上運行的 Trade Federation 框架的日誌(又名主機日誌)
TF 框架會自動從分配的設備中捕獲 logcat,並將其發送給調用監聽器進行處理。然後XmlResultReporter
將捕獲的設備 logcat 保存為文件。
使用 ddmlib Log 類的CLog 包裝器報告 TF 主機日誌。讓我們將之前在 HelloWorldTest 中的System.out.println
調用轉換為CLog
調用:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());
CLog
直接處理字符串插值,類似於String.format
。當您重建並重新運行 TF 時,您應該會在標準輸出上看到日誌消息:
tf> run example/helloworld … 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548 …
默認情況下,tradefed 將主機日誌消息輸出到 stdout 。 TF 還包括一個將消息寫入文件的日誌實現: FileLogger 。要添加文件日誌記錄,請在配置中添加一個logger
標記,指定FileLogger
的完整類名:
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> <result_reporter class="com.android.tradefed.result.XmlResultReporter" /> <logger class="com.android.tradefed.log.FileLogger" /> </configuration>
現在,再次重建並運行 helloworld 示例:
tf >run example/helloworld … 05-16 21:38:21 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_6390011618174565918/device_logcat_1302097394309452308.txt 05-16 21:38:21 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt …
日誌消息指示主機日誌的路徑,查看時應包含您的 HelloWorldTest 日誌消息:
more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
示例輸出:
… 05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
處理選項(D、I、R)
從 TF 配置加載的對象(又名配置對象)也可以通過使用@Option
註釋從命令行參數接收數據。
為了參與,Configuration 對像類將@Option
註釋應用於成員字段並為其提供唯一名稱。這使得該成員字段值可以通過命令行選項填充(並且還會自動將該選項添加到配置幫助系統中)。
注意:並非所有字段類型都受支持。有關支持的類型的描述,請參閱OptionSetter 。
讓我們向 HelloWorldTest 添加一個@Option
:
@Option(name="my_option", shortName='m', description="this is the option's help text", // always display this option in the default help text importance=Importance.ALWAYS) private String mMyOption = "thisisthedefault";
接下來,讓我們添加一條日誌消息以顯示 HelloWorldTest 中選項的值,以便我們可以證明它已正確接收:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { … CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);
最後重建TF,運行helloworld;您應該會看到一條帶有my_option
默認值的日誌消息:
tf> run example/helloworld … 05-24 18:30:05 I/HelloWorldTest: I received option 'thisisthedefault'
從命令行傳遞值
為my_option
一個值;您應該看到my_option
填充了該值:
tf> run example/helloworld --my_option foo … 05-24 18:33:44 I/HelloWorldTest: I received option 'foo'
TF 配置還包括一個幫助系統,它會自動顯示@Option
字段的幫助文本。立即嘗試,您應該會看到my_option
的幫助文本:
tf> run example/helloworld --help Printing help for only the important options. To see help for all options, use the --help-all flag cmd_options options: --[no-]help display the help text for the most important/critical options. Default: false. --[no-]help-all display the full help text for all options. Default: false. --[no-]loop keep running continuously. Default: false. test options: -m, --my_option this is the option's help text Default: thisisthedefault. 'file' logger options: --log-level-display the minimum log level to display on stdout. Must be one of verbose, debug, info, warn, error, assert. Default: error.
請注意有關“僅打印重要選項”的消息。為了減少選項幫助的混亂,TF 使用Option#importance
屬性來確定是否在指定--help
時顯示特定的@Option
字段幫助文本。 --help-all
始終顯示所有@Option
字段的幫助,無論其重要性如何。有關詳細信息,請參閱Option.Importance 。
從配置中傳遞值
您還可以通過添加<option name="" value="">
元素在配置中指定選項值。使用helloworld.xml
對其進行測試:
<test class="com.android.tradefed.example.HelloWorldTest" > <option name="my_option" value="fromxml" /> </test>
重新構建和運行 helloworld 現在應該會產生以下輸出:
05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'
配置幫助也應該更新以指示my_option
的默認值:
tf> run example/helloworld --help test options: -m, --my_option this is the option's help text Default: fromxml.
helloworld 配置中包含的其他配置對象,例如FileLogger
,也接受選項。選項--log-level-display
很有趣,因為它過濾顯示在標準輸出上的日誌。在本教程的前面部分,您可能已經註意到“Hello,TF World!我有設備……”日誌消息在我們切換到使用FileLogger
後停止顯示在標準輸出上。您可以通過傳入--log-level-display
參數。
現在試試這個,除了被記錄到文件之外,您應該會在標準輸出上看到“我有設備”日誌消息:
tf> run example/helloworld --log-level-display info … 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
就是這樣,伙計們!
提醒一下,如果您遇到問題, Trade Federation 源代碼包含許多文檔中未公開的有用信息。如果一切都失敗了,請嘗試在android-platform Google Group 上詢問消息主題中的“Trade Federation”。