Robolectric测试框架使用文档
?
Robolectric使用文檔
--keeng2008@qq.com 2016-01-07
1.測(cè)試驅(qū)動(dòng)你的Android應(yīng)用代碼
在Android模擬器或者手機(jī)上運(yùn)行單元測(cè)試是很漫長(zhǎng)的。每次編譯、部署、啟動(dòng)應(yīng)用都需要耗時(shí)1分鐘以上。有沒(méi)有更好的辦法呢?
Robolectric 是一個(gè)針對(duì)于Android SDK 的單元測(cè)試框架,使用它可以測(cè)試驅(qū)動(dòng)你的Android應(yīng)用程序的開(kāi)發(fā)。測(cè)試用例只需要在JVM基礎(chǔ)上就能運(yùn)行起來(lái),這節(jié)省了大量的時(shí)間。使用Robolectric后,你只需要寫出類似這樣的測(cè)試代碼:
| @RunWith(RobolectricTestRunner.class) public class MyActivityTest { ? @Test ? public void clickingButton_shouldChangeResultsViewTest() throws Excepiton { MyActivity activity = Robolectric.setupActivity(MyActivity.class); ? Button button = (Button) activity.findViewById(R.id.button); TextView results = (TextView) activity.findViewById(R.id.results); ? ?button.performClick(); ?assertThat(results.getText().toString()).isEqualTo("Robolectric Rocks!"); } } ? |
?
Robolectric 通過(guò)重寫Android SDK 的實(shí)現(xiàn)類,然后被加載到測(cè)試工程中,然后讓上面的測(cè)試代碼可以在JVM上運(yùn)行。
?
SDK,資源,原生方法的模擬
Robolectric 模擬了界面生成,資源加載,還有大量的基于Android設(shè)備底層C語(yǔ)言提供的原生方法提供的功能。這可以讓測(cè)試模擬到跟真實(shí)設(shè)備一樣的大部分事情。如果對(duì)于一些具體的SDK方法提供自己的實(shí)現(xiàn),也是非常容易的,所以你可以模擬真實(shí)機(jī)器上發(fā)生的一些出錯(cuò)的條件,出錯(cuò)的行為。
?
2.開(kāi)始使用Robolectric
Robolectric在Gradle和Maven上測(cè)試運(yùn)行得很好。
使用Gradle
在build.gradle里添加下面一行:
| testCompile “org.robolectric:robolectric:3.0” |
在測(cè)試用例代碼中添加Gradle Test的注解
| @RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class) public class SandwichTest { } |
?
注意,一定要配置constants屬性指向BuildConfig.class, 編譯系統(tǒng)會(huì)自動(dòng)生成這個(gè)類,不用手動(dòng)創(chuàng)建。Robolectric讀取constants配置好的輸出路徑,Gradle生成項(xiàng)目時(shí)會(huì)使用到。如果沒(méi)有這些值,Robolectric就找不到應(yīng)用的manifest, resources還有assets等資源。
在Android Studio 生成
Robolectric支持Android Studio 1.1.0及更高的版本。只需要簡(jiǎn)單的參照上面Gradle的配置。然后在”Build Variants”選項(xiàng)中下拉選擇Unit Tests就可以運(yùn)行了。
在Gradle的Task列表中選擇testDebug運(yùn)行(第一次運(yùn)行需要的時(shí)間很久)。
?
?
3.編寫第一個(gè)例子
創(chuàng)建一個(gè)Activity,顯示一個(gè)登陸界面。
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout ??? xmlns:android="http://schemas.android.com/apk/res/android" ??? android:layout_width="match_parent" ??? android:layout_height="match_parent"> ? ??? <Button ??????? android:id="@+id/login" ??????? android:text="Login" ??????? android:layout_width="wrap_content" ??????? android:layout_height="wrap_content"/> ? </LinearLayout> ? |
?
當(dāng)用戶點(diǎn)擊登陸按鈕就跳轉(zhuǎn)到歡迎界面
| public class WelcomeActivity extends Activity { ? ??? @Override ??? protected void onCreate(Bundle savedInstanceState) { ??????? super.onCreate(savedInstanceState); ??????? setContentView(R.layout.welcome_activity); ? ??????? final View button = findViewById(R.id.login); ??????? button.setOnClickListener(new View.OnClickListener() { ??????????? @Override ??????????? public void onClick(View view) { ??????????????? startActivity(new Intent(WelcomeActivity.this, LoginActivity.class)); ??????????? } ??????? }); ??? } } ? |
?
我們希望測(cè)試的是,當(dāng)用戶點(diǎn)擊登陸按鈕后,我們啟動(dòng)了正常的intent。由于Robolectric只是一個(gè)模擬的單元測(cè)試框架,LoginActivity并不會(huì)真正的啟動(dòng),但是我們可以檢查是否準(zhǔn)確的發(fā)出了WelcomActivity的intent。
| @RunWith(RobolectricTestRunner.class) public class WelcomeActivityTest { ? ??? @Test ??? public void clickingLogin_shouldStartLoginActivity() { ??????? WelcomeActivity activity = Robolectric.setupActivity(WelcomeActivity.class); ??????? activity.findViewById(R.id.login).performClick(); ? ??????? Intent expectedIntent = new Intent(activity, WelcomeActivity.class); ??????? assertThat(Shadows.shadowOf(activity).getNextStartedActivity()).isEqualTo(expectedIntent); ??? } } ? |
?
4.配置Robolectric
有幾種方法來(lái)配置Robolectric的運(yùn)行時(shí)行為。
Config注解
最簡(jiǎn)單的配置Robolectric的方法就是使用@Config注解。這個(gè)注解可以作用于類和方法,如果在類和方法都同時(shí)配置了同一個(gè)屬性,方法的配置會(huì)覆蓋類的配置。
基類的注解對(duì)于所有的子類都是有效的,所以如果你要在很多的類使用同一個(gè)配置,可以創(chuàng)建一個(gè)基類,然后把@Config注解移到基類中。
下面的例子展示了更多的配置內(nèi)容。
配置SDK版本
Robolectric會(huì)使用你在manifest中指定的targetSdkVersion版本來(lái)運(yùn)行測(cè)試代碼。如果你想測(cè)試在其它指定版本的表現(xiàn),可以通過(guò)下面的修改這個(gè)SDK版本:
| @Config(sdk = Build.VERSION_CODES.JELLY_BEAN) public classSandwichTest { ? @Config(sdk = Build.VERSION_CODES.KITKAT) ? public void getSandwich_shouldReturnHamSandwich() { ? } } |
?
配置Application類
Robolectric會(huì)根據(jù)manifest的配置自動(dòng)幫你創(chuàng)建一個(gè)Application類,如果你希望提供一個(gè)自己實(shí)現(xiàn)的類,可以這樣設(shè)置:
| @Config(application = CustomApplication.class) public class SandwichTest { ? ??? @Config(application = CustomApplicationOverride.class) ??? public void getSandwich_shouldReturnHamSandwich() { ??? } } |
?
配置Resource路徑
Robolectric為Gradle和Maven提供了默認(rèn)的設(shè)置,但是也允許你修改這些資源的路徑,包括manifest, resource目錄,assets目錄。如果你有一個(gè)自定義的生成腳本這會(huì)非常有用。示例:
| @Config(manifest = "some/build/path/AndroidManifest.xml") public class SandwichTest { ? ??? @Config(manifest = "other/build/path/AndroidManifest.xml") ??? public void getSandwich_shouldReturnHamSandwich() { ??? } } |
默認(rèn)的,Robolectric會(huì)假定你的resouces和assets都是放在目錄res和assets中。這些目錄都是配置成相對(duì)于manifest的相對(duì)目錄。你也可以在@Config注解中添加resourcesDir和assetsDir選項(xiàng)來(lái)修改資源路徑。
使用屬性配置文件
任何在@Config注解可以配置的屬性也可以寫成一個(gè)全局的properties文件。創(chuàng)建一個(gè)文件robolectric.properties,確認(rèn)在classpath中可以找到。例如:
| sdk=18 manifest=some/build/path/AndroidManifest.xml shadows=my.package.ShadowFoo,my.package.ShadowBar |
?
系統(tǒng)屬性
還有以下的選項(xiàng)可以在properties中配置
robolectric.offline設(shè)置true后為離線狀態(tài),不會(huì)在運(yùn)行時(shí)去下載jar文件
robolectric.dependency.idr在離線狀態(tài),提供一個(gè)運(yùn)行時(shí)查找依賴文件的目錄
robolectric.logging.enable設(shè)為true后打開(kāi)debug日志
?
5.控制Activity的生命周期
在Robolectric 2.2版本以前,創(chuàng)建Activity多數(shù)都是直接調(diào)用構(gòu)造函數(shù)(new MyActivity())然后手動(dòng)調(diào)用生命周期的函數(shù)如onCreate()。還有廣泛使用的ShadowActivity(例如ShadowActivity.callOnCreate()),這就是現(xiàn)在ActivityController的設(shè)計(jì)原型。
為了解決調(diào)用的混亂,Robolectric API提供了ActivityController,用它來(lái)簡(jiǎn)化創(chuàng)建Activity還有控制Activity的生命周期。
不需要調(diào)用onCreate這些函數(shù),ActivityController確保了Activity生命周期的一致性,這包括把Activity顯示到Window,還提供了如LayoutInflater這些系統(tǒng)服務(wù)。
具體怎么做
你不要直接new一個(gè)ActivityController,使用Robolectric.buildActivity()來(lái)獲得一個(gè)實(shí)例。對(duì)于大多數(shù)基本的測(cè)試用命 ,你只需要一行代碼就能初始化一個(gè)Activity:
| Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).create().get(); |
這會(huì)創(chuàng)建一個(gè)MyAwesomeActivity,并且已經(jīng)調(diào)用了onCreate()。
想去檢查一些在onCreate()和onResume()之間發(fā)生的事件,這很簡(jiǎn)單:
| ActivityController controller = Robolectric.buildActivity(MyAwesomeActivity.class).create().start(); Activity activity = controller.get(); // assert that something hasn't happened activityController.resume(); // assert it happened! |
還有類似的函數(shù)包括start(), pause(), stop()和destroy()。所以如果你想測(cè)試一個(gè)完整的創(chuàng)建周期:
| Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).create().start().resume().visible().get(); |
模擬使用intent來(lái)啟動(dòng)一個(gè)Activity:
| Intent intent = new Intent(Intent.ACTION_VIEW); Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).withIntent(intent).create().get(); |
還有保存狀態(tài)等調(diào)用:
| Bundle savedInstanceState = new Bundle(); Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class) ??? .create() ??? .restoreInstanceState(savedInstanceState) ??? .get(); |
需要更多關(guān)于ActivityController的功能可以查看Java Doc文檔。
?
奇怪!調(diào)用visible() 有用嗎?
實(shí)際上一個(gè)Android app, 一個(gè)Activity的視圖會(huì)在onCreate()被調(diào)用后一段時(shí)間才添加到Window上的,在此之前,Activity的控件是不可見(jiàn)的,這意味著你沒(méi)法處理任何的點(diǎn)擊交互。Activity的控件樹在onPostResume()被調(diào)用后才添加到設(shè)備或者模擬器的Window上。與其在猜測(cè)不確定的時(shí)候點(diǎn)Activity會(huì)被顯示,Robolectric給單元測(cè)試的開(kāi)發(fā)者提供了直接控制Activity顯示的功能。
然而我們什么時(shí)候調(diào)用它呢?就是當(dāng)你在Activity內(nèi)部需要操作控件的交互時(shí),調(diào)用類似于Robolectric.clickOn()需要視圖是可見(jiàn)的。注意調(diào)用順序是先onCreate()再調(diào)用visible()。
6.使用額外的子模塊
為了減少測(cè)試時(shí)需要依賴的數(shù)量,Robolectric對(duì)Android的模擬也拆分為很多附加的子模塊,只有在Android SDK的基本包里面的類才包含在Robolectric的主模塊中。其它的如appcompat或者support 依賴庫(kù)是作為附加子模擬提供的。下面的表格中列出了可用的附加包:
| SDK包 | Robolectric附加包 |
| com.android.support.support-v4 | org.robolectric:shadows-support-v4 |
| com.android.support.multidex | org.robolectric:shadows-multidex |
| com.google.android.gms:play-services | org.robolectric:shadows-play-services |
| com.google.android.maps:maps | org.robolectric:shadows-maps |
| org.apache.httpcomponents:httpclient | org.robolectric:shadows-httpclient |
注意附加包的功能需要在build.gradle或者pom.xml里添加后才能使用。
?
?
參考:http://robolectric.org/getting-started/
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的Robolectric测试框架使用文档的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 抽取数据shell
- 下一篇: Flutter实现帧动画