在当前,移动端应用的壁垒越来越高。首先,应用必须要满足各个应用市场所期望的质量标准;其次,移动应用的用户对产品的要求也是非常高的。由于越来越多的同类应用可以下载,所以用户完全不能够容忍一个有问题的应用。同时,移动应用现在已经是人们日常生活中一个非常重要的部分,他们完全不会吝啬于表达对一个应用的爱和恨,在数秒之间就可以得到来自于数百万用户的反馈。
当前的移动应用比以往任何一个时候都要更加重要 ,但是同时也是一项非常困难的有挑战性的任务,比如:开发一个恰到好处的应用,其可以在所有可能的设备(不同OS版本、屏幕尺寸、芯片和其他硬件特征)运行良好同时获得流畅的用户体验。
增长的移动平台和设备碎片
当前有太多的技术、工具、框架和开源组件可以被用来构建原生移动应用,但是React Native能够为此场景带来什么样的价值,以及它如何能够确保基于其开发出来的应用被目标用户所接受?
本文主要介绍在 测试React Native应用 中,什么是可用的。首先,本文在介绍如何进行测试之前解释了React Native的一些关键特征;其次,本文从单元、集成、功能测试三个角度对测试方法和框架进行分类,并为每一类提供了实例;最后,本文就如何使用流行的开源自动化测试框架来对功能型应用进行测试提供了一些简单实例。
React Native应用的基本结构
所有的这一切都开始于三年前Facebook将其自己的框架React开源给web开发者。React终将是会流行起来的,不仅仅是因为它是由Facebook开发,而是因为其给予web开发者的能力,尤其是它改变了我们构建应用的方式。
事实上,“learn once, write anywhere”类型框架也不是一个新的概念了,我们已经有许多JavaScript库(如 Sencha , PhoneGap and Appcelerator , among others)来做相似的事情了,但是React对开发者的影响更大一些,主要体现在对开发者习惯的影响和使开发者思考如何将一个应用UI拆分为具体的组件。
React Native 并不会使用DOM渲染,相反,它使用原生的UI视图来进行渲染,这就意味着你可以使用操作系统提供的原生组件。这种使用声明式的API来替代原来的DOM API的产品工作流给了开发者一种 更加内聚的和简化的抽象水平 .
React Native在Android和iOS上的开发流 (图片: Testdroid )
React Native的关键性技术在于将 React programming model 引入了移动应用的开发和测试。确切的说,它并不是被直接当作一种跨平台的工具或者框架来使用,但是它加速了在这个新的平台上构建移动应用的趋势,这同时也是使得React Native如此强大并且容易在这个新的平台上学习和工作的基石之一。
原生移动应用与web应用的最大的不同(同时也是优势)之处在于。原生移动应用不是在浏览器端运行一个基于JavaScript的实现同时暴露HTML元素,而是依赖于 应用中嵌入的JavaScript内核 ,其可以获得特定平台的UI元素。
不同级别的自动化测试:单元、集成、组件和功能
所有的移动软件都是采用成分(译者注:可以理解为组件或者控件)构建起来的。在 Android and iOS 中可以这样理解:小的软件组件通过组合来形成更大的更高级别的功能性更强的组件,直到满足整个应用的目标和需求。一个好的测试方案是运行可以覆盖所有层次功能的测试用例。
在本文中,我们将在三个层次来介绍测试方法和自动化框架。最基本的关注点在最高的层次,即功能性测试,但是React Native应用也能够至少在以下级别进行测试,而且是自动化测试:
单元测试可以看作是和在组件级别测试JavaScript对象和方法一样的最基本的。
组件测试
每个组件都可以在视觉上或者功能上被测试。 ReactTestUtils 提供了一个用于测试React组件的简易框架。
集成测试
接下来是集成测试。集成测试是一组不同的单元被当作一个整体来进行测试的阶段。
功能测试
功能测试是一种黑盒测试的类型,其主要关注于用户需求和交互。功能测试会从整体上覆盖所有的底层软件,所有的用户交互和应用。
除了 ReactTestUtils ,React Native也提供了很有用的 单元测试方法 ,但是这些方法还没有一个可以完全覆盖应用程序的实际逻辑,因此,基于React Native构建的移动应用可以更多的获益于功能性UI测试。 许多功能性的自动化测试框架 都是可以使用的,在本文中我们只涉及最流行的几个。
虽然单元测试能够在组建级进行,但是在React Native应用中,功能性自动化测试为应用整体提供了更好的能力。使用React Native,组件的逻辑单元测试可以被分开进行,其使用传统的JavaScript库同时强制让React Native返回规则的组件而不是原生组件。使用功能性的自动化测试框架,UI元素是整个应用的一部分,同时也可以很容易当作一个整体来进行测试。
如下图所示:本文将这些框架分为和
可用于React Native应用的不同的自动化测试选项.
React Native应用最好的部分是:对于所有主要的移动平台(Android和iOS)它都是完全原生的,这就意味着我们可以根据测试目标来获得更多的框架、工具和原生方法。在接下来题目为“在React Native应用中使用功能性的自动化测试框架”的部分我们将介绍功能性的自动化测试框架。
接下来我们从 unit-testing capabilities 开始介绍,使用一个JavaScript测试来说明。
使用Jest和Jasmine进行单元测试
默认情况下,React Native提供在Android和iOS都可以使用的 Jest 来进行单元测试。现在,测试的覆盖率并不完美,但是根据Facebook的说法,未来将会有更强大测试能力的工具被引入到React Native,同时用户也可以构建他们自己的测试工具。
Jest采用基本的 Jasmine行为驱动框架 来测试JavaScript代码,每一个测试用例都以一个函数调用 describe() 开始,这一点和JUnit采用 TestCase 类很相似。 describe() 需要传入两个参数:一个是测试用例的名称和描述,另一个是要执行的函数。 it() 函数包含了所有的测试步骤同时(和JUnit相似的)提供了一系列的 expect() 函数。
下面是一个用于播放器应用的Jasmine测试脚本的实例。
describe("Player", function() { var player; var song; beforeEach(function() { player = new Player(); song = new Song(); }); it("should be able to play a song", function() { player.play(song); expect(player.currentlyPlayingSong).toEqual(song); //demonstrates use of custom matcher expect(player).toBePlaying(song); }); describe("when song has been paused", function() { beforeEach(function() { player.play(song); player.pause(); }); it("should indicate the song is paused", function() { expect(player.isPlaying).toBeFalsy(); // demonstrates use of 'not' with a custom matcher expect(player).not.toBePlaying(song); }); it("should be possible to resume", function() { player.resume(); expect(player.isPlaying).toBeTruthy(); expect(player.currentlyPlayingSong).toEqual(song); }); }); // demonstrates use of spies to intercept and test method calls it("tells the current song whether the user has made it a favorite", function() { spyOn(song, 'persistFavoriteStatus'); player.play(song); player.makeFavorite(); expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); }); //demonstrates use of expected exceptions describe("#resume", function() { it("should throw an exception if song is already playing", function() { player.play(song); expect(function() { player.resume(); }).toThrow("song is already playing"); }); }); }); |
这个基本的实例展示了Jasmine是如何被用来测试一个应用的功能,但是它始终关注在方法层面的测试。另外,React Native也提供了一些测试集成组件的基本能力,这都可以同时用于原生组件和JavaScript组件,并且使得它们能够通过一个桥梁进行通信。
集成测试
当前,在React Native社区获得关注的集成测试仅仅可用于iOS而且其测试组件的能力是非常受限的。通过桥梁来进行通信同时需要原生组件和JavaScript组件。为了实现可定制化的集成测试,两个工具 RCTestRunner 和 RCTestModule 可以被用来使用。
用于iOS应用的测试架构的基本Objecttive-C实例一般如下启动:
@implementation ExampleTests { RCTTestRunner *_runner; } - (void)setUp { [super setUp]; _runner = RCTInitRunnerForApp(@"IntegrationTestHarnessTest", nil); } - void()testExampleTests { [_runner runTest:_cmd module:@"ExampleTests"] } @end |
然而,还有很多种其他能够被扩展到Android和iOS的集成测试的方法。一个好的同时可以进行单元和集成测试的替代工具是 Mocha ,这是一个运行于 Node.js 的富特征JavaScript测试框架,同时它还提供了行为驱动开发(BDD)、测试驱动开发(TDD)和用于测试的QUnit接口。