工作了不到两周;我终于达到了 Go-DOM 的第一个重要里程碑。
现在,浏览器将在构建 DOM 树时下载并执行远程 JavaScript
。这个项目最初是一个疯狂的想法;看到 Go 和 HTMX 是一个越来越受欢迎的堆栈;
Go 已经拥有测试纯服务器端渲染所需的所有工具。但是,当添加像 HTMX 这样的库时,应用程序的行为是初始 DOM 之间编排的结果;交互元素的属性;到达的 HTTP 端点以及这些端点传送的内容;响应标头和正文。要从用户的角度验证行为,您需要一个浏览器;或者至少是一个行为...与浏览器并不完全不同的测试工具。1
搜索“Go 中的无头浏览器”只会导致建议在无头模式下使用真实浏览器的结果。这种组合有巨大的开销;阻碍快速高效的 TDD 循环。依赖真实的浏览器通常会减慢您的速度而不是加快您的速度。2
于是这个想法就被激发了;用纯 Go 编写一个无头浏览器作为 Web 应用程序的测试工具;
首先要解决的不确定性是 HTML 的解析;以及脚本执行。我很快就做到了; 2天内解决这两个问题。我有一个非常基本的 HTML 解析器;我还将 v8 集成到代码库中3
并使 Go 对象可以被 JavaScript 代码访问。HTML 解析器后来被删除,因为 go x/net/html 已经实现了 HTML 解析器;处理 HTML 解析的所有怪癖。解析格式良好的文档并不是一个非常难解决的问题。它可以优雅地处理格式错误的 HTML,但在这方面却变得棘手。
过了一段时间,我还设法让内联
脚本执行在正确的时刻运行;即当元素实际连接到 DOM 时执行脚本,而不是在解析完整的 HTML 之后执行。能够使用内联脚本处理 HTML 文档之后;下一步是实际从源下载
脚本。这就需要集成一个HTTP层;以便浏览器自行获取内容;而不是被灌输内容。http.Client 还允许您使用 http.RoundTripper 接口控制实际的传输层。通常你会启动一个服务器;它将侦听 TCP 端口上的请求。在这种情况下,TCP 充当传输层;但本身与 HTTP 请求的处理无关。由于 Go 中标准 HTTP 堆栈的简单性;整个 HTTP 服务器由单个函数 func Handle(http.ResponseWriter, *http.Request) 表示。
无头浏览器可以完全绕过 TCP 堆栈的开销,并使用自定义的 RoundTripper 直接调用此函数。
现在浏览器可以执行HTTP请求,但浏览器代码本身不知道HTTP层被绕过的事实。随之而来的是在 DOM 解析期间下载脚本的能力。
让我们探索一个简单的测试,就像它现在在代码库中一样(代码使用 Ginkgo 和 Gomega,恕我直言,这是一个有点被忽视的组合)
首先,测试创建一个简单的 HTTP 处理程序,该处理程序提供两个端点:/index.html 和 /js/script.js。
It("Should download and execute script from script tags", func() { // Setup a server with test content server := http.NewServeMux() server.HandleFunc( "GET /index.html", func(res http.ResponseWriter, req *http.Request) { res.Write( []byte( `<html><head><script src="/js/script.js"></script></head><body>Hello, World!</body>`, ), ) }, ) server.HandleFunc( "GET /js/script.js", func(res http.ResponseWriter, req *http.Request) { res.Header().Add("Content-Type", "text/javascript") res.Write([]byte(`var scriptLoaded = true`)) }, ) // ...
此处的目的只是验证脚本是否已执行。为此,脚本会产生一个可观察到的副作用:它在全局范围内设置一个值。
要验证脚本是否已执行,只需检查全局范围,这是通过从测试本身执行临时 JavaScript 来完成的;验证表达式的结果。
创建浏览器、加载索引文件并验证观察到的副作用的代码
browser := ctx.NewBrowserFromHandler(server) Expect(browser.OpenWindow("/index.html")).Error().ToNot(HaveOccurred()) Expect(ctx.RunTestScript("window.scriptLoaded")).To(BeTrue())
测试执行也相当快。测试套件中涉及 JavaScript 执行的部分目前由 32 个测试组成,运行时间为 23 毫秒。
由于该项目最初是在尝试验证 HTMX 应用程序时构思的,因此合理的下一个目标是支持这种情况。一个简单的 HTMX 应用程序,带有一个按钮和一个计数器,按下按钮时计数器会增加。
接下来是更高级的用户交互;正确的表单处理,例如,输入手线(例如,在 字段中按 enter 提交表单。这通常还涉及某种 URL 重定向;这驱动了对历史对象等的需求.
具有控制传输层的能力;我们可以提供具有独特能力的测试;我们可以模拟系统在运行时依赖的外部站点。例如,对于给定的主机名,测试可以提供另一个模拟行为的 Go HTTP 处理程序。
最明显的例子是使用外部身份提供商。该测试可以模拟登录流程的行为;不必强迫您在外部系统中创建虚拟帐户,由于外部系统中断而导致测试失败,或者由于身份提供商引入的 2FA 或验证码而根本无法自动化该过程。
另一个用例是使用 API 密集型库,例如地图库,这会产生使用成本。模拟外部站点,以免因运行测试套件而收到额外费用。
创建 100% 符合 Whatwg 标准的实现是一项艰巨的任务;直到我真正开始阅读 Whatwg 规范的部分内容之前,我并没有完全理解其范围。目标是创建一个工具帮助为 Web 应用程序编写测试。完全兼容是一个长期目标;但在项目达到某种程度的可用性之前,我会开始填补漏洞。
因此;在实际应用中更可能使用的功能更有可能被优先考虑。指向给出错误结果的实际测试的功能请求可能会被优先考虑。 实施特定标准的功能请求可能会被拒绝。
我相信这对于许多开发人员来说都是一个非常有用的工具,所以如果您阅读了本文,请让您的同事知道它的存在。到目前为止,这只是一个业余时间项目,目前我有很多空闲时间;但情况不会永远如此。
如果你想现场观看,请传播出去......
也许您甚至会赞助这个?您有一家使用 Go 构建 Web 应用程序的大公司吗?欢迎联系我。
在这里找到项目:https://github.com/stroiman/go-dom
如果您成功地听到了对 BBC 流行广播剧的致敬,那就太好了。 ↩
这是基于个人经验。由于快速的反馈周期,正确执行 TDD 会加快你的速度。真实浏览器的开销往往会让您在生产代码之后编写测试;失去了快速测试套件为您提供的反馈循环的好处。 ↩
v8go 项目已经奠定了基础。然而;并非 v8 的所有功能都暴露给 Go 代码;包括嵌入本机对象的必要功能。我能够将它们添加到单独的叉子中;这仍然是 WIP。 ↩
以上是Go-DOM - 重大里程碑的详细内容。更多信息请关注PHP中文网其他相关文章!