IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> [Android]_[中级]_[如何对服务和Activity进行单元测试] -> 正文阅读

[开发测试][Android]_[中级]_[如何对服务和Activity进行单元测试]

场景

  1. 在做Android开发时,会使用Service来做一些后台工作。触发Service启动可能需要经过几次步骤,那么如果每次测试都需要手动点击这几个步骤无疑是很浪费时间的。那么如何使用单元测试来测试Service?

  2. 单元测试有需要启动Activity做一些接收Broadcast的消息,那么单元测试时如何启动指定的Activity

  3. 最后就是单元测试时可以编码自动化测试点击界面的按钮吗?

说明

配置设备单元测试

  1. Android上的设备单元测试最重要的需要依赖以下4种库, 需要在模块的build.gradle里添加以下的测试库,注意mockioMock库,而espresso是界面的单元测试库。而androidTestImplementation声明就是添加设备测试的库依赖,源码放在src/androidTest下会自动识别为设备测试源码,不会打包到产品App里。而设备单元测试会单独安装一个可动态更新的测试App,所以如果更新测试代码再运行会很快,因为它不会更新产品App

    androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
    androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito:2.28.0'
    androidTestImplementation 'org.mockito:mockito-core:2.28.2'
    androidTestImplementation 'androidx.test:core:' + rootProject.coreVersion;
    androidTestImplementation 'androidx.test.ext:junit:' + rootProject.extJUnitVersion;
    androidTestImplementation 'androidx.test:runner:' + rootProject.runnerVersion;
    androidTestImplementation 'androidx.test:rules:' + rootProject.rulesVersion;
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
    
  2. 还要在模块build.gradledefaultConfig添加设备单元测试的运行环境androidx.test.runner.AndroidJUnitRunner

    defaultConfig {
            applicationId "com.example.myapplication"
            minSdkVersion 23
            targetSdkVersion 30
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    
  3. 注意jcenter()资源库即将失效,不要再使用,在项目的build.gradle使用mavenCentral()和阿里的jcenter镜像代替。

    google()
    mavenCentral()
    maven{ url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
    maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/' }
    
  4. 在项目的build.gradle里增加一个ext属性, 全局配置测试库的版本号.

    	ext {
        buildToolsVersion = "31.0.0"
        coreVersion = "1.4.1-alpha05"
        extJUnitVersion = "1.1.4-alpha05"
        runnerVersion = "1.5.0-alpha02"
        rulesVersion = "1.4.1-alpha05"
    	}
    

使用设备单元测试库

  1. 官方文档上[1]使用ActivityTestRule已经是过时了,需要改为使用ActivityScenario或者ActivityScenarioRule来启动Activity.

  2. android的中文开发文档上,对设备的单元测试称为仪器单元测试,所以两种称呼都可以。

  3. 对于本地mock单元测试的可以参考我写的学院课程 Android-开发原生应用-1

  4. 测试bind服务,需要创建一个服务测试套件ServiceTestRule, 因为单元测试默认都是4秒运行结束,如果你想运行长时间的单元测试,需要添加注解@LargeTest。 获取产品AppContext,可以通过ApplicationProvider.getApplicationContext()或者InstrumentationRegistry.getInstrumentation().getTargetContext();获得。

    @RunWith(AndroidJUnit4ClassRunner.class)
    @LargeTest
    public class MyFirstServiceTest {
    
  5. 通过ServiceTestRule的实例调用bindService来启动bind的服务,或者通过contextstartService来启动非绑定服务。

    public static MyFirstService startBindService(ServiceTestRule serviceRule,Intent intent) throws  TimeoutException {
    
        // Bind the service and grab a reference to the binder.
        IBinder binder = serviceRule.bindService(intent);
    
        // Get the reference to the service, or you can call public methods on the binder directly.
        MyFirstService service = ((MyFirstService.LocalBinder) binder).getService();
        return service;
    }
    
  6. Activity的启动需要通过ActivityScenario启动,返回一个ActivityScenario<MainActivity>。注意,无法通过ActivityScenario实例来获取启动的Activity对象,和服务测试一样,测试库默认启动4秒自动退出,这也是为了让测试能迅速结束。如果想长时间测试,需要调用scenario.getResult();来等待Activity结束。这时候需要手动退出Activity单元测试才会结束,这个等待看源码默认是45s

    ActivityScenario<MainActivity> scenario = ActivityScenario.launch(
                    MainActivity.class, null);
    
  7. Activity的模拟点击自动化测试使用Espresso库,使用以下的方式来操作。

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

onView(withId(R.id.button_first)).perform(click());
  1. 为了测试调用方便,我把启动服务和Activity放在一个ComponentTestUtils类里。

  2. 单元测试时打印不要使用Log.x, 因为这样会打印到logcat里,在TestResult窗口不会出现,需要使用System.out.println来打印。

图1:
在这里插入图片描述

例子

ComponentTestUtils.java

package com.example.myapplication.common;

import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.rule.ServiceTestRule;

import com.example.myapplication.MainActivity;
import com.example.myapplication.R;
import com.example.myapplication.service.MyFirstService;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ComponentTestUtils {

    private static ExecutorService executor;

    public static ExecutorService getExecutor(){
        synchronized (ComponentTestUtils.class){
            if(executor == null)
                 executor = Executors.newFixedThreadPool(1);
        }
        return executor;
    }
    
    /**
     * 1. 创建30秒存活的`Service`.
     *
     * @return
     */
    public static ServiceTestRule createServiceTestRule(){
        ServiceTestRule serviceRule = ServiceTestRule.withTimeout(30, TimeUnit.SECONDS);
        return serviceRule;
    }

    public static void stopBindService(ServiceTestRule serviceRule){
        serviceRule.unbindService();
    }

    public static MyFirstService startBindService(ServiceTestRule serviceRule,Intent intent) throws  TimeoutException {

        // Bind the service and grab a reference to the binder.
        IBinder binder = serviceRule.bindService(intent);

        // Get the reference to the service, or you can call public methods on the binder directly.
        MyFirstService service = ((MyFirstService.LocalBinder) binder).getService();
        return service;
    }

    public static void sleep(long seconds){
        try {
            Thread.sleep(seconds*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static ActivityScenario<MainActivity> launchMainActivity(){
        ActivityScenario<MainActivity> scenario = ActivityScenario.launch(
                MainActivity.class, null);
        return scenario;
    }

    public static void tryWaitResultOfActivity(ActivityScenario<MainActivity> scenario){
        scenario.moveToState(Lifecycle.State.RESUMED);
        // onActivityResult never be called after %d milliseconds [45000] 45s
        Instrumentation.ActivityResult result = scenario.getResult();
        System.out.println("test delete image: "+result.getResultCode()+"");
    }

    public static Intent startService(Intent intent){
        // Create the service Intent.
        Context context = ApplicationProvider.getApplicationContext();

        ComponentName name = context.startService(intent);
        System.out.println("ServiceName: "+name.getClassName());
        return intent;
    }

    public static void stopService(Intent intent){
        ApplicationProvider.getApplicationContext().stopService(intent);
    }

}

MainActivityTest.java

package com.example.myapplication;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

import androidx.test.core.app.ActivityScenario;
import androidx.test.filters.LargeTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;

import com.example.myapplication.common.ComponentTestUtils;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4ClassRunner.class)
@LargeTest
public class MainActivityTest {

    @Test
    public void testBroadcast(){
        ActivityScenario<MainActivity> scenario = ComponentTestUtils.launchMainActivity();

        onView(withId(R.id.button_first)).perform(click());
        onView(withId(R.id.button_second)).perform(click());

        onView(withId(R.id.button_service)).perform(click());
        ComponentTestUtils.tryWaitResultOfActivity(scenario);
    }
}

MyFirstService.java

package com.example.myapplication;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.LargeTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import androidx.test.rule.ServiceTestRule;

import com.example.myapplication.common.ComponentTestUtils;
import com.example.myapplication.service.MyFirstService;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.concurrent.TimeoutException;

@RunWith(AndroidJUnit4ClassRunner.class)
@LargeTest
public class MyFirstServiceTest {

    public final ServiceTestRule serviceRule = ComponentTestUtils.createServiceTestRule();

    @Test
    public void testStartService() throws TimeoutException {

        Context context = ApplicationProvider.getApplicationContext();
        Intent intent = new Intent(context, MyFirstService.class);

        ContactUserData cud = new ContactUserData();
        String name = "infoworld";
        cud.setName(name);
        cud.setPhone("https://blog.csdn.net/infoworld");
        intent.putExtra("contact",cud);
        intent.putExtra("ip","192.168.0.1");

        MyFirstService service = ComponentTestUtils.startBindService(serviceRule, intent);
        System.out.println("name: "+service.getName());
        Assert.assertEquals(service.getName(),name);
    }
}

下载

https://download.csdn.net/download/infoworld/85287546

参考

  1. 测试单个应用的界面

  2. 构建插桩单元测试

  3. 测试服务

  4. 测试服务例子

  5. ActivityScenario

  6. Android-开发原生应用-1

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-05-07 11:26:12  更:2022-05-07 11:26:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 14:19:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码