The most complete Appium automated test in history from introduction to framework Practice Essence learning notes

This article is a study note for the students of Hogwarts Testing Institute, and a group is added at the end of the advanced study.

This series of articles summarizes the essence of all aspects of knowledge points involved in Appium automated testing from basic to framework advanced practice (as shown below), hoping to help you quickly summarize and review.

Appium automated testing from foundation to framework

  1. Appium foundation 1 (environment construction and introduction)
  2. Appium foundation 2 (element positioning and common element methods)
  3. Appium foundation 3 (gesture operation and uiautomator lookup elements)
  4. Appium foundation 4 (explicit wait)
  5. Appium foundation 5 (toast and parameterization)
  6. Appium foundation 6 (webview)
  7. Appium_ Enterprise wechat exercise (non PO, add and delete contacts)
  8. Appium_ Enterprise wechat exercise (PO -- add contact)

This is the third article, which mainly explains Appium Toast, parameterization and WebView (with example code).

Toast

meaning

  • In order to display a floating display block for the current view, unlike dialog, it will never get the focus;
  • The display time is limited, and it will disappear automatically according to the display time set by the user;
  • It is a system level control. It belongs to system settings. When an App sends a message, it does not create a pop-up box. It sends it to the system. The system will pop-up the box uniformly. Such controls are not in the App and need special control identification methods;

Toast positioning

  • Appium uses the underlying mechanism of UIAutomator to analyze and grab the toast, and puts the toast into the control tree, but it does not belong to the control itself
  • AutoMationName:UIAutomator2 is provided by Appium itself. It does not need to be added. The default setting is UIAutomator2;
  • getPageSource cannot find Toast;
  • You must use Xpath to find:
    • //*[@class="android.widget.Toast"]
    • //*[contains(@text,"xxxxx")]

Example: App test Toast provided by Appium

  • adb shell dumpsys window | findstr mCurrent
    • This command can find the current activity. I don't know if the higher version of Android is ok. Because the API Demo has high permissions, you can directly jump to this activity to run, and other apps are not ok;
  • Driver Page_ Source can print the current page and find the fake control of Toast;
  • Print the text of toast;

Driver Page_ Source prints something, including Toast

<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy index="0" class="hierarchy" rotation="3" width="810" height="1440">
  <android.widget.FrameLayout index="0" package="io.appium.android.apis" class="android.widget.FrameLayout" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,0][810,1440]" displayed="true">
    <android.view.ViewGroup index="0" package="io.appium.android.apis" class="android.view.ViewGroup" text="" resource-id="android:id/decor_content_parent" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,0][810,1440]" displayed="true">
      <android.widget.FrameLayout index="0" package="io.appium.android.apis" class="android.widget.FrameLayout" text="" resource-id="android:id/action_bar_container" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,41][810,136]" displayed="true">
        <android.view.ViewGroup index="0" package="io.appium.android.apis" class="android.view.ViewGroup" text="" resource-id="android:id/action_bar" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,41][810,136]" displayed="true">
          <android.widget.TextView index="0" package="io.appium.android.apis" class="android.widget.TextView" text="Views/Popup Menu" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[27,65][324,111]" displayed="true" />
        </android.view.ViewGroup>
      </android.widget.FrameLayout>
      <android.widget.FrameLayout index="1" package="io.appium.android.apis" class="android.widget.FrameLayout" text="" resource-id="android:id/content" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,136][810,1440]" displayed="true">
        <android.widget.LinearLayout index="0" package="io.appium.android.apis" class="android.widget.LinearLayout" text="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,136][810,1440]" displayed="true">
          <android.widget.Button index="0" package="io.appium.android.apis" class="android.widget.Button" text="Make a Popup!" content-desc="Make a Popup!" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[297,136][513,217]" displayed="true" />
        </android.widget.LinearLayout>
      </android.widget.FrameLayout>
    </android.view.ViewGroup>
  </android.widget.FrameLayout>

  #Here we find the Tast control
  <android.widget.Toast index="1" package="com.android.settings" class="android.widget.Toast" text="Clicked popup menu item Search" checkable="false" checked="false" clickable="false" enabled="false" focusable="false" focused="false" long-clickable="false" password="false" scrollable="false" selected="false" bounds="[0,0][0,0]" displayed="false" />
</hierarchy>
copy

code

from appium import webdriver
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from appium.webdriver.common.mobileby import MobileBy as By

class TestFind():
    def setup(self):
        self.desire_cap= {
            "platformName":"android",
            "deviceName":"127.0.0.1:7555",
            "appPackage":"io.appium.android.apis",
            "appActivity":"io.appium.android.apis.view.PopupMenu1",
            "noReset":"true",
            "unicodeKeyboard":True
        }
        self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
        self.driver.implicitly_wait(5)

    def test_search(self):
        """
        1.open appium Demo of app
        2.Go directly to the test toast Interface of
        3.Click to display toast Button, and then through driver.page_source Get page
        4.find toast Pseudo control for
        5.Print out toast Value of
        :return:
        """
        #Click the control of Make a Popup
        self.driver.find_element(By.XPATH,'//*[@text="Make a Popup!"]').click()
        #Click the search control
        self.driver.find_element(By.XPATH, '//*[@text="Search"]').click()
        #Print the xml of the entire layout page
        print(self.driver.page_source)
        #Print out the value of toast
        print(self.driver.find_element(By.XPATH, '//*[contains(@text,"popup menu")]').text)
copy

Parameterization

Some small details

  • Parameterization aims to solve the problem that a use case can be reused. For example, if a use case repeatedly uses different data, parameterization can be used. For example, the same use case has the same method for searching stock prices and comparing stock prices, but the data is different;
  • @pytest.mark.parametrize('searchkey,type,price',[ ('alibaba','BABA',180), ('xiaomi','01810',10)
  • Use the above method to use parameterization;
  • Def test_ The parameters of the search (self, searchkey, type, price) function should be the same as the number of parameterized parameters, and the string should also be the same;
  • A use case with two sets of parameterization will run the methods of setup and teardown twice;
  • Use self Driver Find_ Element (by.id, "com.xueqiu.android:id/search_input_text") Send_ Keys (f"{searchkey}"), using f"{searchkey}" is a good thing, which can be used with parameterization;

code

from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy as By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
import pytest

class TestFind():
    #Set caps value
    def setup(self):
        self.desire_cap= {
            #Default is Android
            "platformName":"android",
            #sn name of adb devices
            "deviceName":"127.0.0.1:7555",
            #Package name
            "appPackage":"com.xueqiu.android",
            #activity name
            "appActivity":".view.WelcomeActivityAlias",
            "noReset":"true",
            "unicodeKeyboard":True
        }
        #Run appium on the premise that the appium server is opened
        self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
        self.driver.implicitly_wait(5)

    #It's OK to add it or not, because the parameterized operation will have setup. Setup is the process of starting the app, which is a little redundant
    #However, it seems that setup will not initialize the entire app and will stay on the previous page, so it is better to add it
    def teardown(self):
        self.driver.find_element(By.XPATH,'//*[@text= "Cancel"]') click()

    #This is a parameterized function. The first part is the parameterized name, which must be exactly the same as the following function parameters and included in the string
    #The Yuanzu in the list accepts specific parameterized data, separated by commas, just like the list
    @pytest.mark.parametrize('searchkey,type,price',[
        ('alibaba','BABA',180),
        ('xiaomi','01810',10)
    ])
    #The parameter of the function with parameter ha should be consistent with the parameter name above
    def test_search(self,searchkey,type,price):
        """
        1.Open the snowball app
        2.Click the search input box
        3.Enter "Alibaba" into the search input box
        4.Select Alibaba in the search results, and then click
        5.Obtain the share price of Alibaba in Hong Kong and judge the price of the share price>200
        6.Through the parametric method, use a use case to judge the stock prices of Alibaba and Xiaomi
        :return:
        """
        #The display is waiting to enter the home page, and the elements of the home page are loaded
        WebDriverWait(self.driver, 15).until(expected_conditions.element_to_be_clickable((By.XPATH,'//*[@text= "my"]'))
        #Click the search box
        self.driver.find_element(By.ID,"com.xueqiu.android:id/tv_search").click()
        #Input parameterized items such as Alibaba and Xiaomi into the search box. f"{searchkey}" is a good thing to use
        self.driver.find_element(By.ID,"com.xueqiu.android:id/search_input_text").send_keys(f"{searchkey}")
        #Find Alibaba where the search box previews the results, and click
        self.driver.find_element(By.XPATH,f"//*[@text='{type}']").click()
        #Select the element of HK stock price, which is located through the method of parent class
        current_price=self.driver.find_element(By.XPATH,f"//*[@text='{type}']/../../..//*[@resource-id='com.xueqiu.android:id/current_price']")
        #Extract text attribute of stock price
        current_price=float(current_price.text)

        #Judge whether the stock price is greater than 200
        assert current_price > price
copy

WebView

Environment preparation for pure WebView test (only test browser)

  • Mobile terminal
    • Tested browser: (not a third-party browser) safari for ios and chrome, chromium, or browser for Android
  • PC end
    • Install chrome browser or chromium
    • Download the driver corresponding to the mobile browser
  • Client code:
    • "browserName":"Browser" or "browserName":"Chrome" is the specified browser
    • "chromedriverExecutable":r"c:\chrome\chromedriver.exe" this is the path of the specified chromedriver
    • How to find the app version: ADB shell PM dump com Android Browser | findstr version
    • desire_cap

Case: open mumu's own browser and visit Baidu

  • Steps:
    • Open browser without package
    • Visit Baidu
    • Enter tongtong and click search
  • Note:
    • When you run Appium for the first time, you can find the chromedriver version of the browser and the path of the chromedriver by looking at the path in the background
    • https://blog.csdn.net/huilan_same/article/details/51896672
    • The relationship between the chromedriver and the chrome version of this website is more comprehensive

code

from time import sleep
from appium import  webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

class TestFind():
    def setup(self):
        self.desire_cap= {
            "platformName":"android",
            "platformVersion":"6.0",
            "deviceName":"127.0.0.1:7555",
            #To use a native Browser, select Browser. To select a Chrome Browser, enter Chrome
            "browserName":"Browser",
            "noRest":True,
            #Here is the path to specify the chromedriver. Remember to include the chromedriver Exe
            "chromedriverExecutable":r"c:\chrome\chromedriver.exe"
        }
        self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
        self.driver.implicitly_wait(5)

    def teardown(self):
        self.driver.quit()

    def test_browser(self):
        #Open Baidu browser on mobile terminal
        self.driver.get("http://m.baidu.com")
        #Show whether the search box waiting to be found is visible, expected_ The locator passed in conditions must be a Yuanzu
        WebDriverWait(self.driver,10).until(expected_conditions.visibility_of_element_located((By.CSS_SELECTOR,"#index-kw")))
        #Enter tongtong in the search box
        self.driver.find_element(By.CSS_SELECTOR,"#index-kw").send_keys("tongtong")
        sleep(2)
        #Baidu Click
        self.driver.find_element(By.CSS_SELECTOR, "#index-bn").click()
        sleep(3)
copy

How to determine whether a page is a WebView

  • Disconnected view. If the disconnected display page fails to load, it is WebView
  • Look at the loading bar. The loading bar is usually WebView
  • See if there is a close button at the top
  • Pull down refresh. If the page is refreshed, it is WebView
  • Whether there is a web page provider during pull-down refresh
  • View with tools. If the element displays WebView, it is WebView

WebView

  • It is a system control (special view) provided by Android system that can display pages
  • < android4.4 WebView bottom layer implements webkit internal
  • >=Android4.4 uses chromium as the underlying support for WebView and supports HTML5, CSS3 and JS
  • WebAudio: graphical interface for listening to audio
  • WebGL: rendering of page 3d effects
  • WebRTC: live broadcast, beauty

Mixed WebView test conditions

  • PC:
    • Access to Google
    • Download the corresponding version of chromedriver
  • Mobile terminal: the application code needs to turn on the WebView switch
  • chromedriverExecutable to be added to the code
  • Some webviews can be found by UIAutomatorview, but they are not recommended. Compatibility problems may occur. For example, the display strings of text will be different
  • How to find the web page of the current WebView
    • adb shell
    • logcat | grep http
    • You can find the HTTP to access

Case 1 hybrid WebView of appium's API

  • Open WebView of API demo
  • Enter text into the input box
  • Click i am link
  • Exit app

code

from time import sleep
from appium import  webdriver
from appium.webdriver.common.mobileby import MobileBy

class TestFind():
    def setup(self):
        self.desire_cap= {
            "platformName":"android",
            "platformVersion":"6.0",
            "deviceName":"127.0.0.1:7555",
            "noRest":True,
            "appPackage": "io.appium.android.apis",
            "appActivity":"io.appium.android.apis.view.webview1",
            #If you want to switch webview s, you must specify the chromdriver, or the chromedriver version of your default address corresponds to the mobile phone version
            "chromedriverExecutable": r"c:\chrome\chromedriver.exe"
        }
        self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
        self.driver.implicitly_wait(5)

    def teardown(self):
        self.driver.quit()

    def test_appium_api_webview(self):
        sleep(3)
        #Enter the webview page and directly print the contexts. There must be two, one is native and the other is webview
        print(self.driver.contexts)
        #If you need to switch to the context of webview, it is usually the last but one. Remember to have chromedriverExecutable
        self.driver.switch_to.context(self.driver.contexts[-1])
        sleep(2)
        #Enter tongtong into the input box
        self.driver.find_element(MobileBy.ID,"i_am_a_textbox").send_keys("tongtong")
        #Click on the link
        self.driver.find_element(MobileBy.ID,"i am a link").click()
        #Print out the current page layout and find that it is a webview html layout
        print(self.driver.page_source)
copy

Case 2 snowball webview

  • Open app
  • Click transaction
  • Click A share to open an account
  • Enter user name and password
  • Click to open an account now
  • Exit app
  • Note: opening a new page is actually a new window. You need to switch the window handle
#Because chrome cannot recognize the snowball webview, there is a problem with element positioning, so the code is uncertain
from time import sleep
from appium import  webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

class TestFind():
    def setup(self):
        self.desire_cap= {
            "platformName":"android",
            "platformVersion":"6.0",
            "deviceName":"127.0.0.1:7555",
            #To use a native Browser, select Browser. To select a Chrome Browser, enter Chrome
            "browserName":"Browser",
            "noRest":True,
            #Here is the path to specify the chromedriver. Remember to include the chromedriver Exe
            "chromedriverExecutable":r"c:\chrome\chromedriver.exe"
        }
        self.driver=webdriver.Remote("http://127.0.0.1:4723/wd/hub",self.desire_cap)
        self.driver.implicitly_wait(5)

    def teardown(self):
        self.driver.quit()

    def test_browser(self):
        #Open Baidu browser on mobile terminal
        self.driver.get("http://m.baidu.com")
        #Show whether the search box waiting to be found is visible, expected_ The locator passed in conditions must be a Yuanzu
        WebDriverWait(self.driver,10).until(expected_conditions.visibility_of_element_located((By.CSS_SELECTOR,"#index-kw")))
        #Enter tongtong in the search box
        self.driver.find_element(By.CSS_SELECTOR,"#index-kw").send_keys("tongtong")
        sleep(2)
        #Baidu Click
        self.driver.find_element(By.CSS_SELECTOR, "#index-bn").click()
        sleep(3)
copy

Pits encountered by WebView

  • equipment
    • Android simulator 6.0 supports WebView by default, and mumu is directly opened without setting;
    • At least the simulator and physical machine need to turn on the switch in the App (WebView debugging switch);
  • PC browser location element
    • Chrome browser -62 can better see the interior of webview. Other versions have some bug s;
    • Changing to a Chrome browser can avoid many pitfalls, and the display effect and speed are faster than that of chrome;
  • code
    • Some devices can use find_element_acessibility_id(), the pages rendered by different devices are different, and the compatibility is not suitable;
    • Switch_ To context() switches between different contexts, for one page;
    • Switch To_ Window() switches between different window handles for different pages;

We will share more in the following articles.

Tags: Appium

Posted by Alpha_One on Mon, 30 May 2022 23:09:41 +0530