android Instrumentation

2023-11-19

Android提供了一系列强大的测试工具,它针对Android的环境,扩展了业内标准的JUnit测试框架。尽管你可以使用JUnit测试Android工程,但Android工具允许你为应用程序的各个方面进行更为复杂的测试,包括单元层面及框架层面。

Android测试环境的主要特征有:

l   可以访问Android系统对象。

l   Instrumentation框架可以控制和测试应用程序。

l   Android系统常用对象的模拟版本。

l   运行单个test或test suite的工具,带或不带Instrumentation。

l   支持以Eclipse的ADT插件和命令行方式管理Test和Test工程。

这篇文章是对Android测试环境和测试方法的简要介绍,并假设你已经拥有一定的Android应用程序编程及JUnit测试的经验。

概要

Android 测试环境的核心是一个Instrumentation框架,在这个框架下,你的测试应用程序可以精确控制应用程序。使用Instrumentation, 你可以在主程序启动之前,创建模拟的系统对象,如Context;控制应用程序的多个生命周期;发送UI事件给应用程序;在执行期间检查程序状态。 Instrumentation框架通过将主程序和测试程序运行在同一个进程来实现这些功能。

通过在测试工程的manifest文件中添 加<instrumentation>元素来指定要测试的应用程序。这个元素的特性指明了要测试的应用程序包名,以及告诉Android如何 运行测试程序。在Inustrumentation TestRunner章节有更多的细节描述。

下面的图片概要的描述了Android的测试环境:



在 Android中,测试程序也是Android程序,因此,它和被测试程序的书写方式有很多相同的地方。SDK工具能帮助你同时创建主程序工程及它的测试 工程。你可以通过Eclipse的ADT插件或者命令行来运行Android测试。Eclipse的ADT提供了大量的工具来创建测试用例,运行以及查看 结果。

Testing API

Android提供了基于JUnit测试框架的测试API来书写测试用例和测试程序。另外,Android还提供了强大的Instrumentation框架,允许测试用例访问程序的状态及运行时对象。

下面的章节描述了Android中可利用的主要测试API。

JUnit TestCase类

继 承自JUnit的TestCase,不能使用Instrumentation框架。但这些类包含访问系统对象(如Context)的方法。使用 Context,你可以浏览资源,文件,数据库等等。基类是AndroidTestCase,一般常见的是它的子类,和特定组件关联。

子类有:

l   ApplicationTestCase——测试整个应用程序的类。它允许你注入一个模拟的Context到应用程序中,在应用程序启动之前初始化测试参数,并在应用程序结束之后销毁之前检查应用程序。

l   ProviderTestCase2——测试单个ContentProvider的类。因为它要求使用MockContentResolver,并注入一个IsolatedContext,因此Provider的测试是与OS孤立的。

l   ServiceTestCase——测试单个Service的类。你可以注入一个模拟的Context或模拟的Application(或者两者),或者让Android为你提供Context和MockApplication。

Instrumentation TestCase类

继 承自JUnit TestCase类,并可以使用Instrumentation框架,用于测试Activity。使用Instrumentation,Android可 以向程序发送事件来自动进行UI测试,并可以精确控制Activity的启动,监测Activity生命周期的状态。

基类是InstrumentationTestCase。它的所有子类都能发送按键或触摸事件给UI。子类还可以注入一个模拟的Intent。

子类有:

l   ActivityTestCase——Activity测试类的基类。

l   SingleLaunchActivityTestCase——测试单个Activity的类。它能触发一次setup()和tearDown(),而不是每个方法调用时都触发。如果你的测试方法都是针对同一个Activity的话,那就使用它吧。

l   SyncBaseInstrumentation——测试Content Provider同步性的类。它使用Instrumentation在启动测试同步性之前取消已经存在的同步对象。

l   ActivityUnitTestCase——对单个Activity进行单一测试的类。使用它,你可以注入模拟的Context或Application,或者两者。它用于对Activity进行单元测试。

不同于其它的Instrumentation类,这个测试类不能注入模拟的Intent。

l   ActivityInstrumentationTestCase2——在正常的系统环境中测试单个Activity的类。你不能注入一个模拟的 Context,但你可以注入一个模拟的Intent。另外,你还可以在UI线程(应用程序的主线程)运行测试方法,并且可以给应用程序UI发送按键及触 摸事件。

Assert类

Android还继承了JUnit的Assert类,其中,有两个子类,MoreAsserts和ViewAsserts:

l   MoreAsserts类包含更多强大的断言方法,如assertContainsRegex(String, String),可以作正则表达式的匹配。

l   ViewAsserts类包含关于Android View的有用断言方法,如assertHasScreenCoordinates(View, View, int, int),可以测试View在可视区域的特定X、Y位置。这些Assert简化了UI中几何图形和对齐方式的测试。

Mock对象类

Android 有一些类可以方便的创建模拟的系统对象,如Application,Context,Content Resolver和Resource。Android还在一些测试类中提供了一些方法来创建模拟的Intent。因为这些模拟的对象比实际对象更容易使 用,因此,使用它们能简化依赖注入。你可以在android.test和android.test.mock中找到这些类。

它们是:

l   IsolatedContext——模拟一个Context,这样应用程序可以孤立运行。与此同时,还有大量的代码帮助我们完成与Context的通信。这个类在单元测试时很有用。

l   RenamingDelegatingContext——当修改默认的文件和数据库名时,可以委托大多数的函数到一个存在的、常规的Context上。使用这个类来测试文件和数据库与正常的系统Context之间的操作。

l   MockApplication,MockContentResolver,MockContext,MockDialogInterface,MockPackageManager,MockResources ——创建模拟的系统对象的类。它们只暴露那些对对象的管理有用的方法。这些方法的默认实现只是抛出异常。你需要继承这些类并重写这些方法。

Instrumentation TestRunner

Android 提供了自定义的运行测试用例的类,叫做InstrumentationTestRunner。这个类控制应用程序处于测试环境中,在同一个进程中运行测试 程序和主程序,并且将测试结果输出到合适的地方。IntrumentationTestRunner在运行时对整个测试环境的控制能力的关键是使用 Instrumentation。注意,如果你的测试类不使用Instrumentation的话,你也可以使用这个TestRunner。

当 你运行一个测试程序时,首先会运行一个系统工具叫做Activity Manager。Activity Manager使用Instrumentation框架来启动和控制TestRunner,这个TestRunner反过来又使用 Intrumentation来关闭任何主程序的实例,然后启动测试程序及主程序(同一个进程中)。这就能确保测试程序与主程序间的直接交互。

在测试环境中工作

对Android程序的测试都包含在一个测试程序里,它本身也是一个Android应用程序。测试程序以单独的Android工程存在,与正常的Android程序有着相同的文件和文件夹。测试工程通过在manifest文件中指定要测试的应用程序。

每个测试程序包含一个或多个针对特定类型组件的测试用例。测试用例里定义了测试应用程序某些部分的测试方法。当你运行测试程序,Android会在相同进程里加载主程序,然后触发每个测试用例里的测试方法。

测试工程

为了开始对一个Android程序测试,你需要使用Android工具创建一个测试工程。工具会创建工程文件夹、文件和所需的子文件夹。工具还会创建一个manifest文件,指定被测试的应用程序。

测试用例

一个测试程序包含一个或多个测试用例,它们都继承自Android TestCase类。选择一个测试用例类取决于你要测试的Android组件的类型以及你要做什么样的测试。一个测试程序可以测试不同的组件,但每个测试用例类设计时只能测试单一类型的组件。

一 些Android组件有多个关联的测试用例类。在这种情况下,在可选择的类间,你需要判断你要进行的测试类型。例如,对于Activity来说,你有两个 选择,ActivityInstrumentationTestCase2和ActivityUnitTestCase。

ActivityInstrumentationTestCase2设计用于进行一些功能性的测试,因此,它在一个正常的系统环境中测试Activity。你可以注入模拟的Intent,但不能是模拟的Context。一般来说,你不能模拟Activity间的依赖关系。

相比而言,ActivityUnitTestCase设计用于单元测试,因此,它在一个孤立的系统环境中测试Activity。换句话说,当你使用这个测试类时,Activity不能与其它Activity交互。

作为一个经验法则,如果你想测试Activity与Android的交互的话,使用ActivityInstrumentationTestCase2。如果你想对一个Activity做回归测试的话,使用ActivityUnitTestCase。

测试方法

每 个测试用例类提供了可以建立测试环境和控制应用程序的方法。例如,所有的测试用例类都提供了JUnit的setUp()方法来搭建测试环境。另外,你可以 添加方法来定义单独的测试。当你运行测试程序时,每个添加的方法都会运行一次。如果你重写了setUp()方法,它会在每个方法运行前运行。相似 的,tearDown()方法会在每个方法之后运行。

测试用例类提供了大量的对组件启动和停止控制的方法。由于这个原因,在运行测试之 前,你需要明确告诉Android启动一个组件。例如,你可以使用getActivity()来启动一个Activity。在整个测试用例期间,你只能调 用这个方法一次,或者每个测试方法一次。甚至你可以在单个测试方法中,调用它的finishing()来销毁Activity,然后再调用 getActivity()重新启动一个。

运行测试并查看结果

编译完测试工程后,你就可以使用系统工具Activity Manager来运行测试程序。你给Activity Manager提供了TestRunner的名(一般是InstrumentationTestRunner,在程序中指定);名包括被测试程序的包名和 TestRunner的名。Activity Manager加载并启动你的测试程序,杀死主程序的任何实例,然后在测试程序的同一个进程里加载主程序,然后传递测试程序的第一个测试用例。这个时 候,TestRunner会接管这些测试用例,运行里面的每个测试方法,直到所有的方法运行结束。

如果你使用Eclipse,结果会在JUnit的面板中显示。如果你使用命令行,将输出到STDOUT上。

测试什么?

除了一些功能测试外,这里还有一些你应该考虑测试的内容:

l   Activity生命周期事件:你应该测试Activity处理生命周期事件的正确性。例如,一个Activity应该在pause或destroy事件 时保存它的状态。记住一点的是屏幕方向的改变也会引发当前Activity销毁,因此,你需要测试这种偶然情况确保不会丢失应用程序状态。

l   数据库操作:你应该确保数据库操作能正确处理应用程序状态的变化。使用android.test.mock中的模拟对象。

l   屏幕大小和分辨率:在发布程序之前,确保在所有要运行的屏幕大小和分辨率上测试通过。你可以使用AVD来测试,或者使用真实的目标设备进行测试。

附加:UI测试

接下来的章节为应用程序UI的测试提供了一些提示,特别是帮助你在UI线程里处理动作,触屏和按键事件,和锁屏。

UI线程中测试

Activity运行在程序的UI线程里。一旦UI初始化后,例如在Activity的onCreate()方法后,所有与UI的交互都必须运行在UI线程里。当你正常运行程序时,它有权限可以访问这个线程,并且不会出现什么特别的事情。

当 你运行测试程序时,这一点发生了变化。在带有instrumentation的类里,你可以触发方法在UI线程里运行。其它的测试用例类不允许这么做。为 了一个完整的测试方法都在UI线程里运行,你可以使用@UIThreadTest来声明线程。注意,这将会在UI线程里运行方法里所有的语句。不与UI交 互的方法不允许这么做;例如,你不能触发Instrumentation.waitForIdleSync()。

如果让方法中的一部分代码运行在UI线程的话,创建一个匿名的Runnable对象,把代码放到run()方法中,然后把这个对象传递给appActivity.runOnUiThread(),在这里,appActivity就是你要测试的app对象。

例如,下面的代码实例化了一个要测试的Activity,为Spinner请求焦点,然后发送一个按键给它。注意:waitForIdleSync和sendKeys不允许在UI线程里运行:

  private MyActivity mActivity; // MyActivity is the class name of the app under test 

  private Spinner mSpinner; 



  ... 



  protected void setUp() throws Exception { 

      super.setUp(); 

      mInstrumentation = getInstrumentation(); 



      mActivity = getActivity(); // get a references to the app under test 



      /* 

       * Get a reference to the main widget of the app under test, a Spinner 

       */ 

      mSpinner = (Spinner) mActivity.findViewById(com.android.demo.myactivity.R.id.Spinner01);



  ... 



  public void aTest() { 

      /* 

       * request focus for the Spinner, so that the test can send key events to it 

       * This request must be run on the UI thread. To do this, use the runOnUiThread method 

       * and pass it a Runnable that contains a call to requestFocus on the Spinner. 

       */ 

      mActivity.runOnUiThread(new Runnable() { 

          public void run() { 

              mSpinner.requestFocus(); 

          } 

      }); 



      mInstrumentation.waitForIdleSync(); 



      this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);



关闭触屏模式

为了控制从测试程序中发送给模拟器或设备的按键事件,你必须关闭触屏模式。如果你不这么做,按键事件将被忽略。

关 闭触摸模式,你需要在调用getActivity()启动Activity之前调用 ActivityInstrumentationTestCase2.setActivityTouchMode(false)。你必须在非UI线程中运 行这个调用。基于这个原因,你不能在声明有@UIThread的测试方法调用。可以在setUp()中调用。

模拟器或设备的解锁

你可能已经发现,如果模拟器或设备的键盘保护模式使得HOME画面不可用时,UI测试不能正常工作。这是因为应用程序不能接收sendKeys()的事件。避免这种情况最好的方式是在启动模拟器或设备时关闭键盘保护模式。

你还可以显式地关闭键盘保护。这需要在manifest文件中添加一个权限,然后就能在程序中关闭键盘保护。注意,你必须在发布程序之前移除这个,或者在发布的程序中禁用这个功能。

在<manifest> 元素下添加<uses-permission android:name=”androd.permission.DISABLE_KEYGUARD”/>。为了关闭键盘保护,在你测试的 Activity的onCreate()方法中添加以下代码:

  mKeyGuardManager = (KeyguardManager)getSystemService(KEYGUARD_SERVICE); 

  mLock = mKeyGuardManager.newKeyguardLock("activity_classname"); 

  mLock.disableKeyguard();

这里,activity_classname是Activity的类名。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android Instrumentation 的相关文章

  • 商标45类分类表明细表_注册商标没选择好分类,注册下来也白搭

    商标是区别商品或服务来源的一种标志 每一个注册商标都是指定用于某一商品或服务上的 正是为了商标检索 审查 管理工作的需要 把某些具有共同属性的商品组合到一起 编为一个类 将所有商品及服务共划分为45个类别 形成了商标分类表 商标注册用商品和
  • 输入一串字符,输出其最长单词的长度(不要求输出最长单词)

    无非就是要解决两个问题 1 如何判断一个单词的开始 lastchar 2 统计该单词的长度 sum 3 记录最大单词长度 max 以下是不用数组的做法 include
  • 系统架构设计师 8:系统质量属性与架构评估

    软件系统属性包括功能属性和质量属性 软件架构重点关注的是质量属性 为了精确 定量地表达系统的质量属性 通常会采用质量属性场景的方式进行描述 在确定软件系统架构 精确描述质量属性场景后 就需要对系统架构进行评估 软件系统架构评估是在对架构分析
  • 乘势而起,走进2019年风口“边缘计算”

    戳蓝字 CSDN云计算 关注我们哦 作者 pasca 原创 蛋蛋团 ID dandan tuan 5G火了 也带火了边缘计算 2019年3月 一篇 5G风口扩散 超清视频边缘计算概念崛起 的文章意外走红 直接导致此前不温不火的 百度搜索暴增
  • 微服务+Jenkins 简单的linux自动部署脚本

    No 1 启停脚本 bin bash 加载配置 避免获取不到java home source etc profile SERVICE HOME 项目路径 SERVICE NAME 服务名称 cd SERVICE HOME SERVICE N
  • easyclick与autojs的对比和常用代码(完善中)

    作成了文档 链接 https easydoc xyz s 10157628 加入我们的QQ群553908361 和各作者同群交流 该教程优先发布于收费脚本v群 部分代码收集于网络 现在已经不知道作者谁了 如有侵权 请私信我进行删除 启动ap
  • Redis 缓存穿透和雪崩的概念介绍

    Redis 缓存穿透和雪崩的概念 缓存穿透 缓存穿透的概念其实很简单 我们经常使用 Redis 作为缓存数据库 如果用户相插叙一个数据 但是发现 Redis 没用这条数据 也就是没有命中 Redis 数据库中的数据 于是就到数据库中查询 上
  • 基于weblibvirt管理多个kvm主机

    1 问题a virt manager管理kvm需要接入显示屏或搭建vnc或开启xming转发 问题b virt manager纳管其他kvm后 重启kvm后需要重新纳管 且无法便捷的创建存储池 问题c 基于centos8 stream系统环
  • html图片加白边阴影,CSS3毛玻璃效果(blur)有白边问题的解决方法

    做一个登录页 全屏背景图毛玻璃效果 实现方法如下 var w window innerWidth document documentElement clientWidth document body clientWidth var h wi
  • nginx根据header分流

    OS Centos 7 6 version nginx 1 18 0 nginx根据header分流 不同header头分发到不同节点 同一域名路径根据不同header头分发到不同节点 主要配置 通过 http version 判断版本 转
  • Android 拍摄照片后返回缩略图的两种方法详解

    博主前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住也分享一下给大家 点击跳转到网站 前言 如果简单地拍照片并非您应用的主要目标 那么您可能希望从相机应用中获取图片并对该图片执行一些操作 一 这就是第一种方法 比较简单 不
  • 【深度学习】 Python 和 NumPy 系列教程(十三):Matplotlib详解:1、2d绘图(上):折线图、散点图、柱状图、直方图、饼图

    目录 一 前言 二 实验环境 三 Matplotlib详解 0 绘图风格 1 2d绘图类型 0 设置中文字体 1 折线图 Line Plot 2 散点图 Scatter Plot 3 柱状图 Bar Plot 4 直方图 Histogram
  • pyqt5讲解12:自定义参数 (给信号传入参数)

    在pyqt编程过程中 经常会遇到给槽函数传递自定义参数的情况 比如有一个信号与槽函数的连接是 button1 clicked connect shou page 对于clicked 信号来说 是没有参数的 对于shou page 是可以有参
  • SemanticKitti数据集的使用

    简单介绍 SemanticKitti数据集是在Kitti数据集上进行语义分割等操作的结果 主要任务包括点云的语义预测等 Kitti的点云里程计数据集一共有00 21这22个序列 每个序列都是一段录制的点云包 SemanticKitti为所有
  • ER模型详解

    ER 实体联系 模型中有三个主要的概念 实体集 属性 联系集 实体集对应的是数据库中的一个表 一个实体则对应数据库表中的一行 一个属性则对应数据库表中的一列 则就是一个字段 联系集就是表与表之间的关联关系 一对一关联 两个表的实体是一一对应
  • QT信号和槽的链接问题

    QT中常用的连接信号和槽的方式为以下两种 方法1 connect ui btnClose SIGNAL clicked Widget SLOT close 方法2 connect ui btnClose QPushButton clicke
  • R语言学习笔记:分析学生的考试成绩

    孩子上初中时拿到过全年级一次考试所有科目的考试成绩表 正好可以用于R语言的统计分析学习 为了不泄漏孩子的姓名 就用学号代替了 感兴趣可以下载测试数据进行练习 num class chn math eng phy chem politics
  • splunk 之 搜索(Searching)

    Let s Searching Results Example Results Example 注意 在结果之上 有一个菜单项允许您更改页面上显示的事件的数量 默认情况下 这个选项是每页20个 但是您可以单击这个选项来增加或减少这个数字 t
  • hooks中useEffect()使用总结

    常见使用 获取数据案例 import React useState useEffect from react import axios from axios function App const data setData useState

随机推荐

  • 非线性控制3.0——常用镇定算法之Lyapunov 直接法与间接法

    一 非线性系统镇定方法 Lyapunov 直接法与间接法 俄国学者李雅普诺夫提出的稳定性定理采用了状态向量来描述 适用于单变量 线性 非线性 定常 时变 多变量等系统 该方法称为李雅普诺夫法 有两种分类 1 李氏第一法 间接法 求解特征方程
  • css 实现相关案例

    css 实现相关案例 抽屉案例 带吸附箭头
  • ES删除数据

    ES删除数据 注意 删除数据请谨慎执行 删除大于 日期的 其他场景可参照执行 DeleteQuery deleteQuery new DeleteQuery deleteQuery setIndex es中 index deleteQuer
  • ORACLE数据库备份管理-rman备份到NFS

    1 相关案例 1 df h无法正常显示文件系统挂载情况 使用如下命令进行处理 umount f nfsdir 2 节点启动出现问题 询问是否包含nfs服务 将分享节点nfs服务重启 1 案例1从linux linux 实施脚本如下 mkdi
  • LeetCode 237. 删除链表中的节点

    题目链接 点击这里 Definition for singly linked list public class ListNode int val ListNode next ListNode int x val x class Solut
  • 深度学习(一)深度学习的概念

    什么是深度学习 深度学习是机器学习与神经网络 人工智能 图形化建模 优化 模式识别和信号处理等技术融合后产生的一个领域 深度学习网路是神经网络革命性的发展 人们甚至认为可以用它来创建更加强大的预测模型 深度学习的分类 深度学习使用多层机器学
  • CMakeLists中条件判断: if()-endif()成对出现

    缺少endif 时提示代码块opening is not closed 1 if if xxx 要缩进 endif 2 if else if xxx 要缩进 else xxx 要缩进 endif
  • Windows RuntimeError: Distributed package doesn‘t have NCCL built in问题

    问题描述 python在windows环境下dist init process group backend rank world size 处报错 RuntimeError Distributed package doesn t have
  • Serilog + SQL Server 动态分表记录日志

    Serilog SQL Server 分表记录日志 Serilog SQL Server 动态分表记录日志 依赖包 安装依赖包 配置 appsettings json 配置 Program cs Serilog SQL Server 动态分
  • visio 科学图形包_Gnuplot科学绘图(三十九)——地图及圆圈数据图

    Gnuplot科学绘图 系列内容Gnuplot科学绘图 三十一 曲线色彩填充 Gnuplot科学绘图 三十二 填充风格Gnuplot科学绘图 三十三 柱状图Gnuplot科学绘图 三十四 阶梯图Gnuplot科学绘图 三十五 数据平滑Gnu
  • element-ui中日期区间组件

    elementui中日期组件使用 最长只能选择3个月 不限制禁用日期 描述 时间组件代码 描述 点击 确定 按钮进行验证 点击 清空 按钮 清空输入框中的数据 时间范围不能超过3个月 并添加快捷选择今天 最近一周 最近一月 最近3个月 以下
  • js正则 年龄只能为正数,不能为负数,且不能超过150

    需求 要对输入的年龄加条件限制 不能任意输入 只能为正数 不能输入小数 负数
  • 正则表达式(日期、金额、特殊字符)_java语言

    正则表达式 java 正则表达式在线测试网站 个人觉得还算精确 http tool chinaz com regex 备注 如有错误 希望留言指出 虚心请教 金额格式 正数 包含至多2位小数 第1种形式 校验金额格式是否正确 正数 包含至多
  • Android OpenCv 提取图像的RGB三原色分割图像Split Core.split

    基础知识 如果你还不了解 图片 是如何存储的 建议先去看这篇文章 关于三通道彩色图像的存储方式理解 函数简析 我们都知道 彩色图片每个像素点都对应三个值 如 R G B Core split 这个函数则是帮我们这三个值分开 即分别提取 R
  • VUE3快速上手--知识点

    本文是根据B站尚硅谷的视频 尚硅谷Vue2 0 Vue3 0全套教程 全网最新最强vuejs从入门到精通 Vue3部分形成的笔记 一 简介 2020年9月18日 Vue js发布3 0版本 代号 One Piece 海贼王 Vue3相比Vu
  • fileinclude (攻防世界web)

    题目 从题目页面可得到对我们有用的信息 flag存放在了flag php中 并且还知道了当前页面的绝对路径 分析完当前页面能够获取到的所有信息后 查看页面源代码试试 发现得到了主页的php源码 分析以上源码 可知其中 lan为我们可控的 并
  • CentOS安装mariadb

    1 安装 root localhost yum install mariadb mariadb server 2 启动并自启 root ecs 3f21 systemctl enable mariadb now 3 查看启动状态 root
  • CLIP 改进工作

    Contents 图像分类 IJCV 2022 Learning to prompt for vision language models Introduction Context Optimization CoOp Experiments
  • Quartz定时任务详解

    一 添加依赖
  • android Instrumentation

    Android提供了一系列强大的测试工具 它针对Android的环境 扩展了业内标准的JUnit测试框架 尽管你可以使用JUnit测试Android工程 但Android工具允许你为应用程序的各个方面进行更为复杂的测试 包括单元层面及框架层