Blog
Blog
PHODAL
我的上一篇关于自动化测试的文章,大抵已经在一年以前——一篇关于前端自动化测试的 BDD 框架对比。这么长的时间里,没有相关的文章,总得给自己找一个合适的理由。 编写测试是开发人员日常工作的一小部分,但并非是全部。即使是专业的测试人员,自动化测试也并非是全部的工作。与此同时呢,有了单元测试之外,对于自动化测试的需求,并不是那么强烈。速度,是我们不考虑自动化测试的主要原因,运行起来慢了,进一步地导致编写测试也慢了。UI 上一旦有一丁点的修改,那怕是得引起多少的不愉快,不得不噗呲噗呲地去修改测试。我想说的无非就是,**避免编写 “可怕” 地自动化测试**,尽量用你的单元测试来保障质量。要是你们有 KPI 的限制,请将以上的文字当成废话。 似乎在有些公司里,自动化测试、单元测试并不是技术负责人要考虑的问题,可在我司并非如此,测试也在技术的范围里。每每开始一个项目时,就不得不去考虑自动化测试的问题,选用什么框架合适、需要前后端如何配合、怎样去替换第三方的服务。这些内容完全交给测试人员吧,怕是会遇到一些不顺。测试人员有自己的测试技术栈,拥有自己的 “银弹” —— 过去的经验和代码库。哪怕经验再丰富的测试人员,有时遇到一些新的项目、技术栈,这些东西可能就用不上了,又或者是使用某些框架可能会更加便利。因此呢,要成为更好的技术人,测试也是要考虑的范畴。 说到 Web 方面的自动化测试,我算是个有经验的老手。从顶层的 DSL 到底层的 Web Driver,到底来说,还是颇有经验的。可是说到 APP 的自动化测试,在项目上尝试过,但也不敢说经验丰富。而最近的项目,正在实施相应的移动应用自动化测试。看了看方案,与之前的 Web 或者是 React Native 的自动化测试,从底层对比架构吧,相似、差距也不是太大。便想着写篇文章来记录一下,相应的架构设计。 ## 技术远景 作为一个团队的技术负责人,我希望:拥有一个移动应用测试架构,它能快速让测试人员快速上手——阅读、编写测试用例。与此同时,我希望这些测试用例是能让非技术人员阅读,诸如业务分析人员,并且符合真实的用户使用场景。 ## 架构设计 当我们谈到业务分析人员也能编写的测试,我们说的只有 BDD(Behavior Driven Development,行为驱动开发)。它是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA(测试人员)和非技术人员或商业参与者之间的协作。 BDD 在这一种上相当的迷人——能让非技术人员编写测试。而其核心的部分在于:创建了一个环境隔离的 DSL,仿人类语言的 DSL。咳咳,这个 DSL 实现起来,可不轻松。关注顶层 DSL 的同时,开发人员还要努力实现好底层的实现。举个简单的例子,如下是之前在 BDD 一文中的 DSL 示例,这是顶层的设计: ```markdown 功能: 失败的登录 场景大纲: 失败的登录 假设 当我在网站的首页 ``` 对应的,开发人员需要编写实现: ```javascript ... Given('当我在网站的首页', function() { return this.driver.get('http://0.0.0.0:7272/'); }); .. ``` 从上述的代码中,一眼就可以看出复杂的地方,实现一个领域特定(业务特定)的 DSL 语言。 我们要完成的 DSL 实现,上层是提供一个 DSL,下层则是对接 driver 的 Agent 层。在 Web 领域里,这个 driver 的 Agent 层负责对接不同的浏览器,诸如 Selenium,driver 则视不同的浏览器而有所不同,如 ChromeDriver、FirefoxDriver、PhantomJSDriver 等等。Selenium 这样的测试框架,除了通过 driver 直接操作了浏览器,还提供了不同语言的编程接口。 相似的,在 APP 领域也有这样的方案,它要通常这个 agent 来连接物理设备,并提供一系列的编程接口。 ## 架构设计方案 对整个架构有了一个基本的认识之后,让我们继续往下移动,来重新发掘一下:我们究竟需要哪些基本元素? - BDD 测试框架,为开发人员提供可创建 DSL 的接口。 - 移动设备的测试编程接口,提供一个操作移动应用的接口。 - 连接移动设备的操作库,即移动端的 WebDriver。 - 用于编写测试时的 UI 检查工具。 从这一点上来看,它与 Web 应用的 BDD 架构差不多。为此,我们需要准备如下的一些框架: - Robot Framework,一个支持 BDD 的、基于 Python 编写的功能自动化测试软件框架。 - Appium,是一个开源测试自动化框架,用于原生,混合和移动 Web 应用程序。它使用 WebDriver 协议来驱动 iOS、Android 和 Windows 应用程序。 - XCUITest Driver,基于 Apple 官方的界面自动化测试 XCUITest 封装的测试接口,可以直接执行 iOS 的自动化测试。 - UiAutomator2 Driver,则是 Google 官方提供的用于 Android 系统的测试接口,可以直接执行 Android 的自动化测试。 - Appium Inspector,用于查找 iOS/Android 上的元素 - UiAutomator Viewer,由 Android SDK 自带的元素查找工具。 由于我们计划的顶层是由 DSL 来实现,而对应的 BDD 层实现是由 Robot Framework 来完成的。Robot Framework 使用的是 Python 语言,我们就需要找到对应的 Python 主要依赖有: - robotframework,即 Robot Framework 本身 - robotframework-appiumlibrary,用于为 Robot Framework 提供 Appium 相应的接口封装 - robotframework-ride,用于 Robot Framework 的测试数据编辑器 有了这些主要的库,我们就可以编写我们的 DSL?不,我们还需要配置好,对应的移动端 Driver。 **Android Driver 依赖** 比较简单,通过 ``appium-uiautomator2-driver`` 库就拥有了 driver。 **iOS Driver 依赖** 为了实现对 iOS 设备的自动化测试,需要安装 XCUITest,为此我们需要下面的这一系列工具: - ``libimobiledevice``,是一个跨平台的用于与 iOS 设备通讯的协议库,它可以让 Windows、macOS、GNU/Linux 系统连接 iPhone/iPod Touch 等iOS 设备。 - ``carthage`` 是一个简单的、去中心化的依赖管理工具。 - ``ios-deploy`` 是一个使用命令行安装 iOS 应用程序到连接设备的工具 - ``xcodebuild``,是苹果发布自动构建的工具。 - ``xcpretty``,用于对 xcodebuild 的输出进行格式化,包含输出 report 功能。 看,有了这一系列的知识,我们几乎知道怎么做搭建移动应用的自动化测试。 ## 结论 还是 Web 大法好。

所谓最优,就是从一堆非最佳的方案中找到最好的一个,或者整合一个。

这篇文章,则针对上一篇文章,做一些相应的插件管理机制相关的补充。该方案基于 Cordova 框架的插件管理方案,做了尽可能的精简。

最近的一个项目,是一个关于在移动应用中嵌入 WebView,并在其中实现 JavaScript 能调用原生代码。虽然已经有 Cordova 这样的成熟方案,但是它太 “重” 了——直接在一个拥有大量代码的项目上,修改起来并不容易。

“微” 害架构,即微架构以不合理的方式运行着,其表现形式不适当地采用 “微架构”(微服务、APP 插件化、微前端等)技术拆分臃肿的单体应用,导致软件架构进一步复杂化、难以维护,使得原本具有优势的微架构微微出现一些问题。

无论是学习还是应用新的技术,都需要一个短暂的学习与练习,才能获得相应的经验。这就造成了一个冲突,日益增长的技能需求,同不足的时间之间的矛盾。

> 如果用一个 UI 库不能解决问题,那就用两个 UI 库;如果用一个 UI 框架不能解决问题,那就用两个框架。 **跨框架的 UI 库**,即前端 UI 库可以不经任何修改,直接能运行在 React、Angular、Vue 等框架上。 在开源电子书《[微前端的那些事儿](https://microfrontend.cn/)》 中,我们讨论到了 Web Components 技术,一种新的 Web 前端容器化技术。在电子书里,我们主要介绍的是:如何使用 Web Components 来构建微服务。而在这篇文章里,我们讨论的是 Web 组件的下半场:**跨框架的 UI 库**。 ## 背景 最近的一段时间里,我花费大量地时间在练习微前端技术。在我的新 Markdown 编辑器 [Phodit](https://github.com/phodal/phodit) 中,我有意无意地去拆分出一个个的小组件,每个小的组件使用不同的技术构建,React、Angular、Stencil.js、原生 JavaScript 等等。如: - Stencil.js + Web Components 来放置 Terminal 的关闭窗口 - React.js 制作了左侧的树形文件树 - Angular 6 完成了重命名文件的交互 - sweetalert 用来做 Dialog 提醒 - …… 编辑器仍然在开发中,这并不是最后的所有技术。引入这么多框架的 “hello, world”,然后构建一个个简单的组件,大概、可能、也许是为了 炫技 练习。虽是这么说,事实是 SimpleMDE 已经封装了 CodeMirror 的一系列 API,为了能快速用上自己的编辑器,我决定地接基于SimpleMDE 来修改。而 SimpleMDE 并不能直接用在 Angular 等前端框架上,这也意味着,因为这个 Editor 的存在,我不得不将页面**撕裂**成几部分:左侧菜单、Terminal 窗口栏、辅助栏、状态栏等等的几部分。 换句话来说,就是这是一个**组件化架构**最好的应用场景。 过去我们谈论前端的组件化架构时,通常指的是**框架限制的组件化架构**。而当我们拥有基础的 UI 组件库时,我们的架构则是**基于 UI 组件库的组件化架构**,两者间的不同在于共性的第一次提取。而当我们在业务组件的基础上,进行对一些通用业务组件的封装时,我们的架构则基于**基于 UI 组件库和业务组件的组件化架构**。 可不论是哪种方式,最后我们都限定于**框架限制**——我们将系统绑定在框架上。而对于团队的技术决策者来说,绑定上框架的实现是一种冒险的作法。未来,这些都是风险,那么有没有可能将底层的 UI 组件库、 复合组件和业务组件库通用呢? ## 铺垫:React 中引入 Angular 组件 为了在我的编辑器中使用 Angular,我用 Angular 编写了一个重命名功能。而为了使用它,我得再次使用一次 ``customEvent``,而在这个微前端架构的系统中,其事件通讯机制已经相当的复杂。在这部分的代码进一步恶化之前,我得尝试有没有别的方式。于是,我想到了之前在其它组件中使用的 Web Components 技术,而 Angular 6 正好可以支持。 ### HTML 中引入 Web Components 我所需要做的事情也相当的简单,只需要将我的组件注册为一个 customElements,稍微改一下 ``app.module.ts`` 文件。在这种情况之下,我们就可以构建出独立于框架的组件。 如下是原始的 module 文件: ```javascript @NgModule({ declarations: [AppComponent], imports: [BrowserModule], bootstrap: [AppComponent] }) export class AppModule { } ``` 如下则是新的 module 文件: ```javascript @NgModule({ declarations: [InteractBar], imports: [BrowserModule], entryComponents: [InteractBar] }) export class AppModule { constructor(private injector: Injector) { const interactBar = createCustomElement(InteractBar, {injector}); customElements.define('interact-bar', interactBar); } } ``` 然后,只需要就可以在 HTML 中传递参数: ````,或者监听对应的 ``@Output`` 事件: ```javascript const bar = document.querySelector('interact-bar'); bar.addEventListener('action', (event: any) => { ... }) ``` 事实证明,使用 Angular 构建的 Web Components 组件是可以用的。于是,我便想,不如在 React 中引入 Angular 组件吧。 ### React 中引入 Angular 组件 于是,便使用 ``create-react-app`` 创建了一个 DEMO,然后引入组件: ```
logo

Welcome to React

To get started, edit src/App.js and save to reload.

``` 嗯,it works。至少 ``filename`` 参数可以成功地传递到 Angular 代码中,而 action 在当前似乎还不行。但是毫无疑问,它在未来是可用的。 Demo 见:[https://phodal.github.io/wc-angular-demo/](https://phodal.github.io/wc-angular-demo/) Repo 见:[https://github.com/phodal/wc-angular-demo](https://github.com/phodal/wc-angular-demo) 这个时候,我遇到了一个问题,我使用 Angular 构建的这个组件,大概是有 257kb。这个大小的组件,但是有点恐怖。 ### Web Components 框架构建组件 在那些微前端相关的文章中,我们指出类似于 [Stencil](https://github.com/ionic-team/stencil) 的形式,将组件直接构建成 Web Components 形式的组件,随后在对应的诸如,如 React 或者 Angular 中直接引用。 如下是一个使用 Stencil 写的 Web Components 的例子: ```javascript @Component({ tag: 'phodit-header', styleUrl: 'phodit-header.css' }) export class PhoditHeader { @State() showCloseHeader = false; componentDidLoad() {...} handleClick() {...} render() { if (this.showCloseHeader) {...} return (
); } } ``` 使用它构建出来的组件,大概可以在 30kb 左右的大小。 不论是不是一个经量级的方案,但是它至少证明了组件复用的可行性。 ## 跨平台 UI 库 在有了上面的技术基础之后,我们可以发现:我们可以构建跨 UI 框架的组件库。那么,它就可以解决我们在构建内部 UI 库时,面对不同技术框架,需要编写不同业务逻辑的问题。这个时候我们的 UI 架构,就会发生一系列的变化。原先我们需要为 React、Angular 和 Vue 等几个不同框架写几个不同的 UI 组件库,但是现在,我们只需要写一套 UI 组件库即可。 自此,我们的 UI 库架构变得更加简单、轻量。 那么问题来了,为什么还没有这样的 UI 库?原因主要有两个: **技术不够成熟**。主要原因是,现有的前端框架对于 Web Components 的支持并不是那么好,诸如我尝试使用 React 来使用时,遇到一些问题。与此同时,前端框架都能支持构建出这样的组件,那么也需要浏览器对于 Web Components 的支持。我们需要诸如 ``custom-elements-es5-adapter.js`` 等的支持,而像 Polymer 这样的 Web Components 框架也需要 IE 11+ 的支持。 **Web Components 技术重写组件**。是的,我们需要将之前使用 TypeScript 或者 JSX 或者 .vue 编写的组件,使用更轻量级框架来构建。UI 框架中的很多要素,是我们在编写组件的时候不需要的——我们只在需要的时候,引入我们所需要的组件即可。 而现在,正是构建这种跨平台 UI 库的最好时机。

为了在我的编辑器中使用 Angular,我用 Angular 编写了一个重命名功能。而为了使用它,我得再次使用一次 customEvent,而在这个微前端架构的系统中,其事件通讯机制已经相当的复杂。在这部分的代码进一步恶化之前,我得尝试有没有别的方式。于是,我想到了之前在其它组件中使用的 Web Components 技术,而 Angular 6 正好可以支持。

微应用化即在开发和运行时,应用都是以单一、微小应用的形式存在。

微应用化与微前端架构相当的类似,它们在开发时都是独立应用,在构建时又可以按照需求单独加载。如果以微前端的单独开发、单独部署、运行时聚合的基本思想来看,微应用化就是微前端的一种实践。只是使用微应用化意味着:我们只能使用唯一的一种前端框架。如果从框架不限的角度来定义,怕是离微前端有些远,不过大团队怕是不会想同时支持多个前端框架。

最近,我在写一个新的 markdown 内容,过程中发现没有合适的 markdown 客户端。于是,我希望为自己定制一款全新的编辑器,原因有许多吧,大抵是没有一个编辑器能满足我的需求。

Feeds

RSS / Atom

最近文章

存档

2026 (2 个月)
2025 (12 个月)
2024 (12 个月)
2023 (12 个月)
2022 (12 个月)
2021 (12 个月)
2020 (12 个月)
2019 (12 个月)
2018 (12 个月)
2017 (12 个月)
2016 (12 个月)
2015 (12 个月)
2014 (12 个月)
2013 (9 个月)
2012 (3 个月)
2011 (1 月)
2010 (1 月)
1991 (1 月)

分类

标签

作者