Skip to content

作业讲解

雪球 app 搜索功能自动化测试实战

需求分析

  • 被测应用:雪球 App,请在应用商店直接安装。
  • 雪球 App 介绍

    - 雪球 app 是一款免费版,非常实用的股票平台,提供热点资讯内容和实时行情,提供了证券交流交易,公募、理财服务 - 雪球 app 功能包括搜索股票,查看行情,交易,浏览热门文章,发帖,登录,注册,等功能

  • 测试场景:搜索股票

用例设计

  • 打开【雪球】应用首页
  • 点击搜索框(点击之前,判断搜索框的是否可用,并查看搜索框 name 属性值,并获取搜索框坐标,以及它的宽高)
  • 向搜索框输入:alibaba
  • 判断【阿里巴巴】是否可见

    - 如果可见,打印 “搜索成功” - 如果不可见,打印 “搜索失败”

用例编写思路

  • pytest 测试框架编写
  • 添加隐式等待
  • 添加测试装置
  • 添加断言

步骤拆解

  • 使用 setup_class 函数创建 driver 建立连接并打开【雪球】应用首页

因为雪球应用需要登录才能进入首页,看到搜索框元素。所以 capability 中需要设置不清空缓存信息。

这样执行测试时,就可以提前打开雪球 app,先完成登录操作,进入首页后,再运行代码完成用例执行。

Python 示例:

class TestXueqiu:

    def setup_class(self):
        '''
        完成 capability 设置
        初始化 driver
        :return:
        '''
        # 设置 cpability
        caps = {
            # 设置 app 安装的平台(Android,iOS)
            "platformName": "Android",
            # 设置 appium 驱动
            "appium:automationName": "uiautomator2",
            # 设置设备名称
            "appium:deviceName": "emulator-5554",
            # 设置被测 app 的包名
            "appium:appPackage": "com.xueqiu.android",
            # 设置被测 app 启动页面的 Activity
            "appium:appActivity": ".view.WelcomeActivityAlias",
            # 不清空缓存信息,保存登录信息
            "appium:noReset": True,
            # 强制app重启,整个测试运行之前重启 app
            "appium:forceAppLaunch": True,
            # 跳过安装,权限设置等操作
            "appium:skipDeviceInitialization": True
        }
        # 初始化 driver
        self.driver = webdriver.Remote(
            "http://127.0.0.1:4723",
            options=UiAutomator2Options().load_capabilities(caps)
        )
        # 隐式等待
        self.driver.implicitly_wait(20)

Java 示例:

public class TestXueqiu {

    private static AndroidDriver driver;

    @BeforeAll
    public static void setUp() throws MalformedURLException {
        // 初始化capability
        DesiredCapabilities caps = new DesiredCapabilities();
        // 设置 app 安装的平台(Android、iOS)
        caps.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        // 设置 appium 驱动
        caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
        // 设置设备名称
        caps.setCapability(MobileCapabilityType.DEVICE_NAME, "emulator-5554");
        // 设置 app 的包名
        caps.setCapability("appPackage", "com.xueqiu.android");
        // 设置 app 的启动页
        caps.setCapability("appActivity", ".view.WelcomeActivityAlias");
        // 设置 app 不清空缓存
        caps.setCapability("appium:noReset", true);
        // 设置 app 不重启
        caps.setCapability("appium:shouldTerminateApp", true);

        // 设置启动url
        URL remoteUrl = new URL("http://127.0.0.1:4723");
        // 初始化driver
        driver = new AndroidDriver(remoteUrl, caps);
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(20));

    }
  • 点击搜索框(点击之前,判断搜索框的是否可用,并查看搜索框 name 属性值,并获取搜索框坐标,以及它的宽高)

Python 示例:

    def test_search(self):
        # 定义搜索关键词
        search_key = "alibaba"
        ## 定位到搜索框
        searchbox_ele = self.driver.find_element(
            AppiumBy.ID, "com.xueqiu.android:id/home_search"
        )
        # 先判断一下搜索框是否可用
        if searchbox_ele.is_enabled():
            # 1. 查看搜索框属性
            searchbox_text = searchbox_ele.text
            print(f"首页搜索框的 text:{searchbox_text}")
            searchbox_location = searchbox_ele.location
            print(f"首页搜索框的 location坐标为:{searchbox_location}")
            searchbox_size = searchbox_ele.size
            print(f"首页搜索框的 size 宽高:{searchbox_size}")
            # 2. 点击搜索框
            searchbox_ele.click()

Java 示例:

    @Test
    public void testSearch() {
        String searchKey = "alibaba";
        WebElement searchBox = driver.findElement(AppiumBy.id("com.xueqiu.android:id/home_search"));
        if (searchBox.isEnabled()) {
            String searchBoxTest = searchBox.getText();
            System.out.println("首页搜索框的 text:" + searchBoxTest);
            org.openqa.selenium.Point searchboxLocation = searchBox.getLocation();
            System.out.println("首页搜索框的 location坐标为:" + searchboxLocation);
            org.openqa.selenium.Dimension searchboxSize = searchBox.getSize();
            System.out.println("首页搜索框的 size 宽高:" + searchboxSize);
            searchBox.click();
        }
    }
  • 如果搜索框可用,向搜索框输入:alibaba

Python 示例:

            # 3. 向搜索框输入:alibaba
            self.driver.find_element(
                AppiumBy.ID,
                "com.xueqiu.android:id/search_input_text"
            ).send_keys(search_key)

Java 示例:

            // 3. 向搜索框输入关键词:alibaba
            WebElement searchInputEle = driver.findElement(AppiumBy.id("com.xueqiu.android:id/search_input_text"));
            searchInputEle.sendKeys(searchKey);
  • 判断【阿里巴巴】是否可见,如果可见打印搜索,断言 result 为 true

    - 向输入框传入alibaba后,搜索结果如图所示 - 同时也注意到这个列表下方还有一个阿里巴巴,使用 Xpath 定位,会同时定位到两个元素,因为我们需要定位的是第一个元素,使用find_element会在找到第一个元素就将该元素返回,所以是可以正常定位到的。如果需要定位到页面下方的另一个阿里巴巴可以使用find_elements定位到多个元素再进行选择。

Python 示例:

            # 4. 判断【阿里巴巴】是否可见
            alibaba_element = self.driver.find_element(
                AppiumBy.XPATH,
                "//*[@text='阿里巴巴']"
            )
            result = alibaba_element.is_displayed()
            print(result)
            # 如果可见,打印“搜索成功”
            if result == True:
                print("搜索成功")
            # 如果不可见,打印“搜索失败
            else:
                print("搜索失败")
            assert result == True

Java 示例:

            // 4. 判断【阿里巴巴】是否可见
            WebElement alibabaElement = driver.findElement(AppiumBy.xpath("//*[@text='阿里巴巴']"));
            boolean result = alibabaElement.isDisplayed();
            System.out.println(result);

            // 如果可见,打印“搜索成功”
            if (result) {
                System.out.println("搜索成功");
            } else {
                // 如果不可见,打印“搜索失败”
                System.out.println("搜索失败");
            }

            // 断言结果是否为真
            assert result;

完整代码示例

Python 示例:

from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy


class TestXueqiu:

    def setup_class(self):
        '''
        完成 capability 设置
        初始化 driver
        :return:
        '''
        # 设置 cpability
        caps = {
            # 设置 app 安装的平台(Android,iOS)
            "platformName": "Android",
            # 设置 appium 驱动
            "appium:automationName": "uiautomator2",
            # 设置设备名称
            "appium:deviceName": "emulator-5554",
            # 设置被测 app 的包名
            "appium:appPackage": "com.xueqiu.android",
            # 设置被测 app 启动页面的 Activity
            "appium:appActivity": ".view.WelcomeActivityAlias",
            # 不清空缓存信息,保存登录信息
            "appium:noReset": True,
            # 强制app重启,整个测试运行之前重启 app
            "appium:forceAppLaunch": True,
            # 跳过安装,权限设置等操作
            "appium:skipDeviceInitialization": True
        }
        # 初始化 driver
        self.driver = webdriver.Remote(
            "http://127.0.0.1:4723",
            options=UiAutomator2Options().load_capabilities(caps)
        )
        # 隐式等待
        self.driver.implicitly_wait(20)

    def teardown_class(self):
        '''
        关闭 driver
        :return:
        '''
        self.driver.quit()

    def test_search(self):
        """
        1. 判断搜索框的是否可用
           并查看搜索框 name 属性值,并获取搜索框坐标,以及它的宽高
        2. 点击搜索框
        3. 向搜索框输入:alibaba
        4. 判断【阿里巴巴】是否可见
            如果可见,打印“搜索成功”
            如果不可见,打印“搜索失败
        :return:
        """
        # 1. 判断搜索框的是否可用,并查看搜索框 name 属性值,并获取搜索框坐标,以及它的宽高
        search_key = "alibaba"
        searchbox_ele = self.driver.find_element(
            AppiumBy.ID, "com.xueqiu.android:id/home_search"
        )
        # 先判断一下搜索框是否可用
        if searchbox_ele.is_enabled():
            # 1. 查看搜索框属性
            searchbox_text = searchbox_ele.text
            print(f"首页搜索框的 text:{searchbox_text}")
            searchbox_location = searchbox_ele.location
            print(f"首页搜索框的 location坐标为:{searchbox_location}")
            searchbox_size = searchbox_ele.size
            print(f"首页搜索框的 size 宽高:{searchbox_size}")
            # 2. 点击搜索框
            searchbox_ele.click()
            # 3. 向搜索框输入:alibaba
            self.driver.find_element(
                AppiumBy.ID,
                "com.xueqiu.android:id/search_input_text"
            ).send_keys(search_key)
            # 4. 判断【阿里巴巴】是否可见
            alibaba_element = self.driver.find_element(
                AppiumBy.XPATH,
                "//*[@text='阿里巴巴']"
            )
            result = alibaba_element.is_displayed()
            print(result)
            # 如果可见,打印“搜索成功”
            if result == True:
                print("搜索成功")
            # 如果不可见,打印“搜索失败
            else:
                print("搜索失败")
            assert result == True
        else:
            print("搜索框不可用")
            assert False
Java 示例:

import io.appium.java_client.AppiumBy;

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.remote.MobileCapabilityType;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;

public class TestXueqiu {

    private static AndroidDriver driver;

    @BeforeAll
    public static void setUp() throws MalformedURLException {
        // 初始化capability
        DesiredCapabilities caps = new DesiredCapabilities();
        // 设置 app 安装的平台(Android、iOS)
        caps.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
        // 设置 appium 驱动
        caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "UiAutomator2");
        // 设置设备名称
        caps.setCapability(MobileCapabilityType.DEVICE_NAME, "emulator-5554");
        // 设置 app 的包名
        caps.setCapability("appPackage", "com.xueqiu.android");
        // 设置 app 的启动页
        caps.setCapability("appActivity", ".view.WelcomeActivityAlias");
        // 设置 app 不清空缓存
        caps.setCapability("appium:noReset", true);
        // 设置 app 不重启
        caps.setCapability("appium:shouldTerminateApp", true);

        // 设置启动url
        URL remoteUrl = new URL("http://127.0.0.1:4723");
        // 初始化driver
        driver = new AndroidDriver(remoteUrl, caps);
        // 隐式等待
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(20));

    }

    @AfterAll
    public static void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    @Test
    public void testSearch() {
        // 1. 判断搜索框的是否可用,并查看搜索框 name 属性值,并获取搜索框坐标,以及它的宽高
        String searchKey = "alibaba";
        WebElement searchBox = driver.findElement(AppiumBy.id("com.xueqiu.android:id/home_search"));

        // 先判断一下搜索框是否可用
        if (searchBox.isEnabled()) {
            // 1. 查看搜索框属性
            String searchBoxTest = searchBox.getText();
            System.out.println("首页搜索框的 text:" + searchBoxTest);
            org.openqa.selenium.Point searchboxLocation = searchBox.getLocation();
            System.out.println("首页搜索框的 location坐标为:" + searchboxLocation);
            org.openqa.selenium.Dimension searchboxSize = searchBox.getSize();
            System.out.println("首页搜索框的 size 宽高:" + searchboxSize);

            // 2. 点击搜索框
            searchBox.click();

            // 3. 向搜索框输入:alibaba
            WebElement searchInputEle = driver.findElement(AppiumBy.id("com.xueqiu.android:id/search_input_text"));
            searchInputEle.sendKeys(searchKey);

            // 4. 判断【阿里巴巴】是否可见
            WebElement alibabaElement = driver.findElement(AppiumBy.xpath("//*[@text='阿里巴巴']"));
            boolean result = alibabaElement.isDisplayed();
            System.out.println(result);

            // 如果可见,打印“搜索成功”
            if (result) {
                System.out.println("搜索成功");
            } else {
                // 如果不可见,打印“搜索失败”
                System.out.println("搜索失败");
            }

            // 断言结果是否为真
            assert result;
        } else {
            System.out.println("搜索框不可用");
            assert false;
        }
    }
}

总结

  • App 自动化测试用例编写