12月阅读周·编写可测试的JavaScript代码:调试之远程调试篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读十一个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》、《JavaScript异步编程设计快速响应的网络应用》。
当前阅读周书籍:《编写可测试的JavaScript代码》。
远程调试
首先,我们将了解一下两个环境(Chrome和Firefox)中远程调试的具体细节,然后我们将访问上述每个场景,依次看看它是如何工作的。远程调试允许调试器在一个单独进程(甚至另外一个主机)中运行,与被调试JavaScript不属于同一进程。
Chrome
到目前为止,Chrome/WebKit是最成熟的远程开发环境。Chrome 18和后续版本有最新版本的基于JSON的远程调试API,即1.0版。注意它和V8调试器API不同。Chrome允许调试器用于远程代码,并且允许远程调试器链接到Chrome调试所运行的JavaScript。这才是服务!
远程调试Chrome中运行的JavaScript,需要启动一个带有调试器可监听端口的Chrome可执行文件。Windows下的执行代码如下:
chrome.exe -remote-debugging-port=9222
Mac下的执行代码如下(都在一行上):
/Applications/Googlel Chrome.app/Contents/MacOS/Googlel\ Chrome
--remote-debugging-port=9222
这相当于启动一个有fresh配置文件的Chrome,就像Firefox的配置文件一样。现在,可以连接到9222端口并和1.0调试器进行交互了。
这里有一个例子,用于说明如何使用该功能取出时间统计信息——与Speed Tracer和 Chrome调试器的Timeline面板的图形化数据相同。我们将使用Selenium自动生成一个Chrome实例并在9222端口监听调试器链接。大家可能会发现比较有趣的是,JSON调试API是运行在Web Sockets上,而不是HTTP上;使用Web Sockets将会使事件传播变得简单。在普通HTTP上使用EventHub或socket.io会提供相同的功能。
让我们使用webdriverjs看一下Node.js脚本,它利用了Chrome的远程调试功能。该脚本有点长,所以我们只看其中两部分。第一部分使用相应的Chrome命令行选项启动Selenium,运行浏览器并捕获我们想捕获的内容:
var webdriverjs = require('webdriverjs');
browser = webdriverjs.remote({
host: 'localhost',
port: 4444,
desiredCapabilities: {
browserName: 'Chrome',
seleniumProtocol: 'WebDriver',
'chrome.switches': ['--remote-debugging-port=9222', '--user-data-dir=remote-profile'],
},
});
browser.addCommand('startCapture', startCapture);
browser.init().url('http://search.yahoo.com').startCapture().setValue('#yschsp', 'JavaScript').submitForm('#sf').saveScreenshot('results.png');
end();
这是我们之前见过的最标准的webdriverjs内容。然而,有两点内容需要补充。第一个是chrome.switches功能。Chrome驱动可以让我们很方便地给Chrome可执行文件提供命令行开关。这里我们告诉Chrome开始在9222端口上监听远程调试器连接,并使用特定目录中的配置文件。使用远程调试器,我们不需要任何特定的Chrome扩展或设置,所以这是一个非常简单的配置文件。
第二部分是startCapture函数。webdriverjs允许我们定义一个自定义方法,可以在标准的webdriverjs方法上进行链式调用。任何自定义方法结束时,都会调用其接收的回调函数。如下,我们定义了一个startCapture方法,用于捕捉所有Chrome提供的时间信息——网络时间、绘制时间、布局时间以及所有其他可用的时间。本例中没有相应的stopCapture函数,我们只会捕捉浏览器关闭之前的内容。
访问https://localhost:9222/json(或任何一个正在运行Chrome的主机),将返回一个JSON响应,该响应是一个可以连接到远程调试的选项卡数组。因为我们刚刚使用Selenium打开了只有一个选项卡的Chrome实例,因此该数组只有一个对象。注意webdriverjs向startCapture传递一个回调函数,在startCapture完成时会调用该函数。所以,该函数连接到本地(或远程)Chrome实例,并获取当前要调试选项卡的JSON数据,然后将该信息传递给connectToDebugger函数.
所以,这就是整个事情的全部!记住,调试器协议是在Web套接字(socket)上运行的。faye-websocket npm模块提供了一个很好的客户端Web套接字实现,这里我们用到了它。startCapture提供的对象包含了webSocketDebuggerUrl属性,该属性是该选项卡——也就是Selenium模拟出来的选项卡所要链接的Web套接字地址。
连接到指定的网络套接字地址之后,faye-websocket将会给onopen函数发送一个回调。onopen函数将发送一个JSON编码的消息。
这个消息的规范可以在Chrome Developers Tools网站找到。它将告诉Chrome发送一个websocket消息给我们,该消息是每个Timeline事件的Chrome调试器事件。一旦我们发送该消息,我们就调用webdriverjs回调,以便webdriverjs链可以继续(还记得吗?)。
在这些操作完成后,我们关闭Selenium,Selenium会结束Chrome,而Chrome会调用onclose处理程序,该处理程序也会做一些有趣的事情——将所有保存的事件保存到一个HTML文件中。非常方便,该HTML文件和Speed Tracer使用的格式一样,所以如果使用Chrome的Speed Tracer扩展加载该文件的话,它就会加载带有数据显示的Speed Tracer UI——很神奇!这允许我们自动捕获Speed Tracer的输入,可以利用它比较早期版本的代码,或者闲暇时进行检查。图7-15展示了在装有Speed Tracer的Chrome上加载HTML代码时的样子。
除了从Chrome捕获Timeline事件之外,还可以从远程完全控制Chrome调试器,暂停并恢复脚本、捕获控制台输出、调用并执行页面上下文中的JavaScript,以及所有其他能直接在Chrome调试器上做的事情。
PhantomJS
与Chrome类似,PhantomJS也可以用作远程调试的目标。但在这种情况下,远程机器可以是无序的,或者可以在本地运行PhantomJS,而无需打开另外一个带有单独配置文件的浏览器实例。
可惜的是,我们不能直接与PhantomJS浏览器中运行的Web应用程序进行交互。幸运的是,可以通过PhantomJS脚本,对运行代码执行单步交互或者以编程方式进行交互。
使用PhantomJS进行远程调试,对于脚本化调试或编程调试是最有用的。
PhantomJS提供了一个evaluate方法,该方法允许我们操作加载页面上的DOM,并可以执行页面上的JavaScript。注意,PhantomJS所执行的任何JavaScript都是在沙盒中执行的,并且不能和应用程序上的JavaScript进行交互——这就是远程调试器!与Web应用程序进行单项交互,可以通过console.log消息在PhantomJS脚本实现。还有一种有趣的方式,那就是可以在收集单元测试和代码覆盖率信息的时候,使用PhantomJS自动化执行单元测试。
Firefox
使用Crossfire扩展,Firebug也有一个远程调试功能。Crossfire是Firebug的一个扩展,其暴露了一个调试协议以进行网络编程调试。可以在其网站(https://getfirebug.com/releases/crossfire)下载最新的版本。安装Crossfire并重启Firefox后,在Add-on栏上我们将看到一个Crossfire图标。
与Chrome的选项卡类似,Firebug会将每个选项卡作为一个单独的上下文。可惜的是,Selenium Firefox驱动目前不支持向Firefox可执行文件传递命令行选项,并且Crossfire也不支持profile文件的配置,所以不能使用Selenium的自动化。我希望这个情况很快就能有所改变。
Crossfire没有Chrome内置的调试服务器成熟。我希望这能随着时间的改变而改变,但目前最好使用基于WebKit / Chrome的浏览器调试器进行远程调试。
总结
远程调试JavaScript很容易。这种情况可能比我们原来想象的更常见、更有用。远程调试在以下场景是必要的或有用的:
- 调试移动浏览器
- 调试服务器端JavaScript
- 调试浏览器工具
- 编程化调试
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)