Android 谈谈自动化测试

Android自动化测试原来可以这么简单

几款Android 应用自动化测试工具

Android自动化测试探索(一)adb详细介绍 - 周希 - 博客园

移动测试基础 Android:自动化兼容性测试

Android 上一次编写,随处测试

androidStudio版本BumbleBee2021.1.1Patch3

文章日期20220421

android官网测试指南(可能需要梯子)

android 官方测试演示程序

github地址:  https://github.com/android/testing-samples/tree/mastericon-default.png?t=M3C8https://github.com/android/testing-samples/tree/master

测试入门

android测试个人理解分为本地测试和设备模拟测试

本地测试就是不依赖设备或者模拟器,只需要jvm虚拟机在pc上就可以完成的测试.特点是不依赖android框架,可以快速测试

设备模拟测试,指依赖android框架API或者设备服务设备其他应用.测试速度较慢,但是可以完成比本地测试更强大的功能

本地测试的实现多是指Junit测试框架

设备模拟测试,这里使用的是内置的AndroidJunitRunner测试框架

本地测试

参考官方示例

本地测试依赖项

dependencies {
        // Required -- JUnit 4 framework
        testImplementation 'junit:junit:4.12'
        // Optional -- Mockito framework
        testImplementation 'org.mockito:mockito-core:1.10.19'
    }

本地测试代码基础结构


public class CalculatorTest {

    private Calculator mCalculator;

    /**
     * Before注解,测试前的准备工作
     * 测试框架会优先运行before注解的方法
     * 该注解方法限制为public
     */
    @Before
    public void setUp() {
        mCalculator = new Calculator();
    }

    /**
     * After注解,测试后释放资源工作
     * 测试框架在运行完test注解方法后最后运行的方法
     * 同After,限制方法public
     */
    @After
    public void tearDown(){
        mCalculator=null;
    }


    /**
     * Test注解,真正测试的方法
     * 运行在Before和After之间
     * 一般运行结果要有断言来设置期望的结果或者异常
     * assertThat()来设置期望结果
     * 还有许多更方便的断言库可以选择,配合Junit框架
     */
    @Test
    public void addTwoNumbers() {
        double resultAdd = mCalculator.add(1d, 1d);
        assertThat(resultAdd, is(equalTo(2d)));
    }
   

    /**
     * Test注解,真正测试的方法
     * 运行在Before和After之间
     * 一般运行结果要有断言来设置期望的结果或者异常
     * Test注解内变量expected设置期望的异常
     */
    @Test(expected = IllegalArgumentException.class)
    public void divDivideByZeroThrows() {
        mCalculator.div(32d,0d);
    }

}

Mock模拟框架

如果需要依赖其他复杂的组件,但是只是依赖比较简单的功能,可以用Mock模拟框架,来提供一个虚假的对象,定制依赖的功能,从而起到隔离作用

//定义一个依赖context的类
class StringUtil{

    private Context mContext;

    public StringUtil(Context context) {
        mContext = context;
    }

    /**
     * 依赖context返回商店名
     * @return
     */
    public String getShopId(){
        return mContext.getString(R.string.settings_merchant_item_merchant_id);
    }
}


//测试StringUtil.getShopId()方法
//runWith加载MockitoJUnitRunner框架入口类
@RunWith(MockitoJUnitRunner.class)
public class MockStringTest {

    //mock注解标记需要模拟的对象
    @Mock
    Context mContext;

    @Test
    public void testGetString(){
        //mock的方法定义一个模拟规则
        when(mContext.getString(R.string.settings_merchant_item_merchant_id))
                .thenReturn("Hello World");
        //加载模拟类并断言测试
        StringUtil stringUtil=new StringUtil(mContext);
        Assert.assertEquals("Hello World",stringUtil.getShopId());
    }
}

设备模拟测试

参考官方演示程序中AndroidJunitRunnerSample示例

设备模拟测试需要结合Junit测试框架一起使用

依赖项

build.gralde文件

android {
        defaultConfig {
            ...
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    }

dependencies{
        //runner本体
        androidTestImplementation 'androidx.test:runner:1.1.0'
        //配合runner使用的Junit库,以及获取context的相关库
        androidTestImplementation 'androidx.test.ext:junit:1.1.3'
        // Optional -- rule可用可不用
        androidTestImplementation 'androidx.test:rules:1.1.0'
        // Optional -- Hamcrest library 更人性化的断言库
        androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
        // Optional -- UI testing with Espresso UI测试库
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
       
        
}

查看一个测试类的代码结构 

/**
 * JUnit4 unit tests for the calculator logic.
 * RunWith注解,测试框架入口,不同的框架有不同的入口类,这里是android扩展的Junit4
 * SmallTest注解,标记测试的大小,按照时间分类,有小测试(<200ms),中等测试(<1000ms),大测试(>1000ms),大测试(>),分片测试(支持测试分片)
 */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CalculatorTest {

    private Calculator mCalculator;

    /**
     * Before注解,测试前的准备工作
     * 测试框架会优先运行before注解的方法
     * 该注解方法限制为public
     */
    @Before
    public void setUp() {
        mCalculator = new Calculator();
    }

    /**
     * After注解,测试后释放资源工作
     * 测试框架在运行完test注解方法后最后运行的方法
     * 同After,限制方法public
     */
    @After
    public void tearDown(){
        mCalculator=null;
    }


    /**
     * Test注解,真正测试的方法
     * 运行在Before和After之间
     * 一般运行结果要有断言来设置期望的结果或者异常
     * assertThat()来设置期望结果
     * 还有许多更方便的断言库可以选择,配合Junit框架
     */
    @Test
    public void addTwoNumbers() {
        double resultAdd = mCalculator.add(1d, 1d);
        assertThat(resultAdd, is(equalTo(2d)));
    }
   

    /**
     * Test注解,真正测试的方法
     * 运行在Before和After之间
     * 一般运行结果要有断言来设置期望的结果或者异常
     * Test注解内变量expected设置期望的异常
     */
    @Test(expected = IllegalArgumentException.class)
    public void divDivideByZeroThrows() {
        mCalculator.div(32d,0d);
    }

}

获取androidApi

插装测试的特点就是可以获取android的Api参数

可以结合UI测试框架,进行UI测试

获取Context参数

//来自androidx.test:monitor库
Context appContext = InstrumentationRegistry.getInstrumentation().getContext();
//来自androidx.test:core库
Context appContext2=ApplicationProvider.getApplicationContext();


build.gradle{

...
dependencies{
    //引入android的junit扩展库即可获得上述两个库.此外也是替换runner库中的AndroidJunit4来源
    androidTestImplementation    "androidx.test.ext:junit:1.1.3"
}
}

结合Espresso框架测试UI


@RunWith(AndroidJUnit4.class)
@LargeTest
public class CalculatorInstrumentationTest {

    /**
     * Use {@link ActivityScenario} to create and launch of the activity.
     */
    @Before
    public void launchActivity() {
        //加载activity界面,launch方法可以添加Bundle参数来控制activity初始化
        ActivityScenario.launch(CalculatorActivity.class);
    }

    @Test
    public void noOperandShowsComputationError() {
        final String expectedResult = getApplicationContext().getString(R.string.computationError);
        //Epresso框架模拟触发控件
        onView(withId(R.id.operation_add_btn)).perform(click());
        onView(withId(R.id.operation_result_text_view)).check(matches(withText(expectedResult)));
    }

}

套件测试

假设已经有了很多插桩测试文件,现在希望能够一起有顺序的运行测试文件,Junit框架支持把若干测试类组合起来作为一个套件运行测试

/**
 * 定义一个套件测试类
 * 效果是按照添加SuiteClasses中的顺序执行测试类
 * 1.RunWith中添加入口Suite.class
 * 2.Suite.SuiteClasses注解中顺序添加要执行的测试类
 * 3.直接运行该套件测试类
 * 4.会依次执行每个测试类的before-test-after
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({
        TestSuiteFirst.class,
        TestSuiteSecond.class,
        TestSuiteThird.class
})
public class TestSuite {
}

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐