理解 android instrumentation

instrumentation

先看activity的啟動(dòng)過程

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }


繼續(xù)看startActivityForResult:

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }
            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

這里我們看到activity的啟動(dòng)是由 instrumentation的execStartActivity執(zhí)行的笑窜。
這種設(shè)計(jì)即委托觀察者模式登澜。

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

自動(dòng)化測試,instrumentaiontestcase:

testactivity = (testClass)getInstrumentation().startActivitySync(intent);
text = testactivity.findViewById(R.id.text1);
assertEquals("hello,world", text.getText().toString());

...不知道源碼也可以的铐拐。
/** This testcase starts the same activity and sending a key event to that activity under test*/
private void testcase3() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(“STRING”, “Hai”);
intent.setClassName(getTargetContext(), "MyActivity.class");

MyActivity activity = (MyActivity)startActivitySync(_intent);
act = new ActivityRunnable(activity);
runOnMainSync(act);

舉個(gè)例子(from:http://stackoverflow.com/questions/1759626/how-do-you-test-an-android-application-across-multiple-activities)

package com.mycompany;

import android.app.*;
import android.content.*;
import android.test.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import android.view.*;
import android.widget.*;

import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static com.mycompany.R.id.*;

public class LoginTests extends InstrumentationTestCase {

   @MediumTest
   public void testAValidUserCanLogIn() {

      Instrumentation instrumentation = getInstrumentation();

      // Register we are interested in the authentication activiry...
      Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(AuthenticateActivity.class.getName(), null, false);

      // Start the authentication activity as the first activity...
      Intent intent = new Intent(Intent.ACTION_MAIN);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      intent.setClassName(instrumentation.getTargetContext(), AuthenticateActivity.class.getName());
      instrumentation.startActivitySync(intent);

      // Wait for it to start...
      Activity currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Type into the username field...
      View currentView = currentActivity.findViewById(username_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyUsername");

      // Type into the password field...
      currentView = currentActivity.findViewById(password_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyPassword");

      // Register we are interested in the welcome activity...
      // this has to be done before we do something that will send us to that
      // activity...
      instrumentation.removeMonitor(monitor);
      monitor = instrumentation.addMonitor(WelcomeActivity.class.getName(), null, false);

      // Click the login button...
      currentView = currentActivity.findViewById(login_button;
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(Button.class));
      TouchUtils.clickView(this, currentView);

      // Wait for the welcome page to start...
      currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Make sure we are logged in...
      currentView = currentActivity.findViewById(welcome_message);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(TextView.class));
      assertThat(((TextView)currentView).getText().toString(), is("Welcome, MyUsername!"));
   }
}

uiautomator

簡單看一下uiautomator,首先吓著,instrumentation 用來做UI測試受制于android的安全策略箱蝠,本質(zhì)上是一個(gè)APP測試微峰,需要和被測APP在相同簽名下疲酌,相同的進(jìn)程下皇忿,才能互相信任,獲取控件狀態(tài)锄蹂。因此氓仲,如果不知道源碼,不要對(duì)APP重簽名得糜;另外敬扛,instrumentaion比較底層,封裝程度不高掀亩,因此不適合直接拿來測試舔哪。從android API LEVEL 16開始,android引入了新的測試框架uiautomator,它對(duì)UI測試做了高度封裝槽棍,同時(shí)不依賴于instrumentation,不需要做簽名校驗(yàn)捉蚤,可直接通過系統(tǒng)調(diào)用抬驴,獲取被測應(yīng)用控件。非常方便缆巧。以后詳細(xì)介紹布持。

來自微信公眾帳號(hào):myersguo

參考資料

android samples
instrumentationtestcase source code
instrumentation 源碼
Activity 源碼
uiautomatorTestCase源碼
uidevice 源碼
uiautomatorbridge源碼
automationg performance tests

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市陕悬,隨后出現(xiàn)的幾起案子题暖,更是在濱河造成了極大的恐慌,老刑警劉巖捉超,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胧卤,死亡現(xiàn)場離奇詭異,居然都是意外死亡拼岳,警方通過查閱死者的電腦和手機(jī)枝誊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惜纸,“玉大人叶撒,你說我怎么就攤上這事∧桶妫” “怎么了祠够?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長粪牲。 經(jīng)常有香客問我古瓤,道長,這世上最難降的妖魔是什么虑瀑? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任湿滓,我火速辦了婚禮,結(jié)果婚禮上舌狗,老公的妹妹穿的比我還像新娘叽奥。我一直安慰自己,他們只是感情好痛侍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布朝氓。 她就那樣靜靜地躺著,像睡著了一般主届。 火紅的嫁衣襯著肌膚如雪赵哲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天君丁,我揣著相機(jī)與錄音枫夺,去河邊找鬼。 笑死绘闷,一個(gè)胖子當(dāng)著我的面吹牛橡庞,可吹牛的內(nèi)容都是我干的较坛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼扒最,長吁一口氣:“原來是場噩夢啊……” “哼丑勤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吧趣,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤法竞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后强挫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岔霸,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年俯渤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秉剑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稠诲。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖诡曙,靈堂內(nèi)的尸體忽然破棺而出臀叙,到底是詐尸還是另有隱情,我是刑警寧澤价卤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布劝萤,位于F島的核電站,受9級(jí)特大地震影響慎璧,放射性物質(zhì)發(fā)生泄漏床嫌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一胸私、第九天 我趴在偏房一處隱蔽的房頂上張望厌处。 院中可真熱鬧,春花似錦岁疼、人聲如沸阔涉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瑰排。三九已至,卻和暖如春暖侨,著一層夾襖步出監(jiān)牢的瞬間椭住,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國打工字逗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留京郑,地道東北人宅广。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像傻挂,于是被迫代替她去往敵國和親乘碑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,790評(píng)論 25 707
  • 作者:Ringoyan金拒,騰訊測試開發(fā)工程師兽肤。先后為植物大戰(zhàn)僵尸Online,糖果傳奇等游戲擔(dān)任測試經(jīng)理绪抛,其負(fù)責(zé)的“...
    飯盒閱讀 2,784評(píng)論 2 41
  • 這一期的公眾號(hào)是總結(jié)在學(xué)校學(xué)習(xí)中遇到的有關(guān)電子商務(wù)營銷模式有關(guān)的簡稱(我知道你們肯定會(huì)問學(xué)物流為什么會(huì)與電子商務(wù)有...
    樹朋友閱讀 184評(píng)論 0 0
  • 今天上午翻了辰光的歷年年終總結(jié)资铡,又翻了自己的大學(xué)日志,眼睛有一點(diǎn)點(diǎn)累幢码,不太想工作了笤休,那就索性提筆來寫一寫,寫一寫辰...
    孔雨喬閱讀 652評(píng)論 2 3
  • 就愛嗑瓜子閱讀 191評(píng)論 0 3