Android和Appium自學(xué)自測(一)

20161220

背景

本來今年計(jì)劃是要把Android和iOS都學(xué)一下,結(jié)果現(xiàn)在到年底了,還是沒點(diǎn)苗頭,再怎樣也應(yīng)該開個(gè)頭,于是就買了《第一行代碼(第二版)》開始學(xué)習(xí)Android,在學(xué)習(xí)的過程中,利用Appium來測試自己寫出來的東西,也不失為一個(gè)學(xué)習(xí)的方法。

項(xiàng)目Github地址:https://github.com/diandianhanbin/LearnAndroidDoAppium

TextView

這個(gè)控件對(duì)應(yīng)著HTML的Label,其實(shí)可以理解為一個(gè)標(biāo)簽,用來展示文本內(nèi)容,當(dāng)然,它也可以監(jiān)聽點(diǎn)擊,所以在某種程度上來說,也可以發(fā)揮一個(gè)按鈕的作用。

<TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:gravity="center|center_horizontal"
        android:text="This is a TextView!" />

TextView控件可以定義id,這個(gè)id無論對(duì)于開發(fā)還是測試,都是很有用的東西,開發(fā)的時(shí)候,可以通過id來對(duì)控件監(jiān)聽、設(shè)置等操作。

比如下面的代碼就是用來關(guān)聯(lián)和設(shè)置TextView。

textView = (TextView) findViewById(R.id.text_view);

case R.id.changeTextView:
                if (textView.getText().toString() == "Change Text") {
                    textView.setText("This is a TextView");
                } else {
                    textView.setText("Change Text");
                }
                break;

對(duì)于測試來說,這個(gè)控件就是獲取文本最方便的地方。利用uiautomatorviewer我們可以很直接的獲取這個(gè)控件的resource-id

注:resource-id的組成是 包名 + / + id

在使用Appium測試的過程中獲取的方式就是這樣的

TextView = methodConfig.SIMPLEREAD['TextView']

def getTextView(self):
        """
        獲取TextView
        :return:str, 標(biāo)題文本
        """
        return self.getTextOfElement(*self.TextView)
        
class MessageSimpleRead(AppiumBaseTest, SimpleRead):
    def test001_checkTextView(self):
        """測試TextView內(nèi)容是否正確"""
        textcontent = self.getTextView()
        self.assertEqual('This is a TextView!', textcontent)

Activity

Activity應(yīng)該是Android的非常重要的東西,屬于Android的四大組件之一。

簡單的來理解,Activity可以理解為一個(gè)HTML頁面,Android所有的顯示,都是由Activity來承載,關(guān)于Activity的信息就不多寫了,在網(wǎng)絡(luò)上很多,主要需要熟悉它的生命周期.

這里我用按鈕來觸發(fā)跳轉(zhuǎn)第二個(gè)Activity,所以在測試的時(shí)候需要測試這個(gè)跳轉(zhuǎn)。

Android部分是一個(gè)顯式啟動(dòng),代碼如下:

@Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        startActivity(intent);
    }

對(duì)應(yīng)的Appium的操作方法如下:

SecondActivity = methodConfig.SIMPLEREAD['SecondActivity']

def clickButtonStartActivity(self):
        """
        點(diǎn)擊按鈕啟動(dòng)Activity
        :return:
        """
        self.clickElement(*self.ButtonStartActivity)

    def checkSecondActivity(self):
        """
        檢查是否啟動(dòng)第二個(gè)Activity
        :return: True or False
        """
        return self.waitActivity(self.SecondActivity)
        
def test003_changeActivity(self):
        """跳轉(zhuǎn)Activity測試"""
        self.clickButtonStartActivity()
        self.assertEqual(self.checkSecondActivity(), True)
        self.assertEqual(self.getSecondActivityTextView(), u'This is Second Activity')

當(dāng)然,通過Appium還有其他方法啟動(dòng)Activity,源碼中對(duì)應(yīng)的還有這個(gè)方法:

def start_activity(self, app_package, app_activity, **opts):
        """Opens an arbitrary activity during a test. If the activity belongs to
        another application, that application is started and the activity is opened.

        This is an Android-only method.

        :Args:
        - app_package - The package containing the activity to start.
        - app_activity - The activity to start.
        - app_wait_package - Begin automation after this package starts (optional).
        - app_wait_activity - Begin automation after this activity starts (optional).
        - intent_action - Intent to start (optional).
        - intent_category - Intent category to start (optional).
        - intent_flags - Flags to send to the intent (optional).
        - optional_intent_arguments - Optional arguments to the intent (optional).
        - stop_app_on_reset - Should the app be stopped on reset (optional)?
        """
        data = {
            'appPackage': app_package,
            'appActivity': app_activity
        }
        arguments = {
            'app_wait_package': 'appWaitPackage',
            'app_wait_activity': 'appWaitActivity',
            'intent_action': 'intentAction',
            'intent_category': 'intentCategory',
            'intent_flags': 'intentFlags',
            'optional_intent_arguments': 'optionalIntentArguments',
            'stop_app_on_reset': 'stopAppOnReset'
        }
        for key, value in arguments.items():
            if key in opts:
                data[value] = opts[key]
        self.execute(Command.START_ACTIVITY, data)
        return self

不過正常來說,不建議這么處理,因?yàn)檫@樣處理,就沒有覆蓋到點(diǎn)擊按鈕跳轉(zhuǎn)Activity這個(gè)業(yè)務(wù)場景了。

Menu

系統(tǒng)的Menu對(duì)測試來說絕對(duì)一個(gè)大坑。

從開發(fā)上來看,是一個(gè)非常正常的開發(fā)流程,在xml中寫一個(gè)menu的布局、控件名稱和控件ID。然后在需要展示MenuActivity中調(diào)用如下方法即可生成Menu

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

這是一個(gè)再正常不過的開發(fā)方式了,但是到了測試這里,完全就是一個(gè)深坑。

比如,我在定義這個(gè)Menu的時(shí)候,用的id是這樣的:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_menu"></menu>

但是用uiautomatorviewer來查看的時(shí)候,它給我顯示的卻是這個(gè)

WTF!!!這個(gè)菜單竟然是一個(gè)ImageView,然后,我定義的id呢?被Android吃了么????

然后再看這個(gè)菜單欄。。。。從uiautomatorviewer上來看,是由一個(gè)一個(gè)的ListView組成的,這些我不關(guān)心,作為測試,我關(guān)心的依然是它的id。

uiautomatorviewer探測的結(jié)果是com.example.svenweng.uiwidgettest:id/title??墒?,我特么開發(fā)的時(shí)候,定義的是這樣的啊啊啊?。。?!

<item
        android:id="@+id/getTextView"
        android:title="@string/main_menu_getTextView"/>
    <item
        android:id="@+id/changeTextView"
        android:title="@string/main_menu_changeTextView"/>
    <item
        android:id="@+id/changeImageView"
        android:title="@string/main_menu_changeImageView"/>
    <item
        android:id="@+id/changeProgressBar"
        android:title="@string/main_menu_changeProgressBar"/>
    <item
        android:id="@+id/insertProgressBar"
        android:title="@string/main_menu_insertProgressBar"/>
    <item
        android:id="@+id/delProgressBar"
        android:title="@string/main_menu_delProgressBar"/>
    <item
        android:id="@+id/alertDialog"
        android:title="@string/main_menu_alertDialog"/>
    <item
        android:id="@+id/alertProgressDialog"
        android:title="@string/main_menu_progressDialog"/>

Android你全部給我改成com.example.svenweng.uiwidgettest:id/title是幾個(gè)意思????

在開發(fā)調(diào)用對(duì)應(yīng)菜單監(jiān)控的時(shí)候,用的也是它的id?。。?!

switch (item.getItemId()) {
            case R.id.getTextView:
                Toast.makeText(this, textView.getText().toString(), Toast.LENGTH_SHORT).show();
                break;
            case R.id.changeTextView:
                if (textView.getText().toString() == "Change Text") {
                    textView.setText("This is a TextView");
                } else {
                    textView.setText("Change Text");
                }
                break;
            case R.id.changeImageView:
                imageView.setImageResource(R.drawable.img_1);
                break;
            case R.id.changeProgressBar:
                if (progressBar.getVisibility() == View.GONE) {
                    progressBar.setVisibility(View.VISIBLE);
                } else {
                    progressBar.setVisibility(View.GONE);
                }
                break;
            case R.id.insertProgressBar:
                progress = progress + 10;
                oriProgressBar.setProgress(progress);
                break;
            case R.id.delProgressBar:
                progress = progress - 10;
                oriProgressBar.setProgress(progress);
                break;
            case R.id.alertDialog:
                AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
                dialog.setTitle("This is a Dialog");
                dialog.setMessage("This is a Message");
                dialog.setCancelable(false);
                dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(MainActivity.this, "OK", Toast.LENGTH_SHORT).show();
                    }
                });
                dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(MainActivity.this, "Cancel", Toast.LENGTH_SHORT).show();
                    }
                });
                dialog.show();
                break;
            case R.id.alertProgressDialog:
                ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
                progressDialog.setTitle("This is a ProgressDialog");
                progressDialog.setMessage("loading....");
                progressDialog.setCancelable(true);
                progressDialog.show();
                break;
            default:
                break;

為毛線在測試使用的時(shí)候,唯一的id都變成了一樣的東西?。。?!

最后

我一直堅(jiān)持的觀點(diǎn),不懂開發(fā)就很難測試,從這一次的學(xué)習(xí)來看,我之前有很多想法是有一些問題的,以前我總是覺得開發(fā)為什么這么懶,控件不用就不加id,或者老喜歡用同樣的id?,F(xiàn)在我終于知道了,很多時(shí)候我都在錯(cuò)怪開發(fā)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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