web页面E2E测试好帮手——Rod简介(一)

发表于:2022-11-01 09:37

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:ag9920    来源:掘金

  E2E
  业务系统的历史债务往往是 RD 最大的痛点,混乱且没有单测的代码,连重构都没有信心。这件事很多时候就是恶性循环,没有单测,不敢重构,继续在坏掉的代码上添砖加瓦。直到某一天跑路。
  但坦率的讲,好的单测是很高的要求,这个时候我们可以利用 End To End 测试来确认重构代码是否符合预期。这样也能给 RD 真实的自信。
  Rod 就是 web 页面 E2E 测试的好帮手,今天我们来看看它能做什么。大家只需要安装好 Golang 环境就可以体验了。
  Rod
  Rod is a high-level driver directly based on?DevTools Protocol. It's designed for web automation and scraping for both high-level and low-level use, senior developers can use the low-level packages and functions to easily customize or build up their own version of Rod, the high-level functions are just examples to build a default version of Rod.
  go-rod 是一款支持 golang 的 web automation工具,基于DevTools Protocol协议实现,Chrome DevTools Protocol 协议支持与浏览器进行通信,允许使用工具来检测、检查、调试和分析 Chromium、Chrome 和其他基于 Blink 的浏览器。一些需要页面端到端测试的case,使用自动化可以大幅减少手工操作时间。
  它具有以下优势:
  链式上下文设计;
  自动等待网页的元素加载完毕;
  对调试很友好,自动跟踪输入,远程监控浏览器;
  线程安全;
  自动查找,并下载浏览器,参照 launcher;
  高层次的 helper 方法,如 WaitStable, WaitRequestIdle, HijackRequests, WaitDownload;
  两步的 WaitEvent 设计,保障不会丢失 event,参照 goob;
  正确处理嵌入 iframe;
  进程 crash 后不会再有浏览器进程运行,防止泄露,参照 leakless;
  100% 的测试覆盖率,安全可靠。
  作为优秀的开源库,我们同样可以从 Rod 的单测来一窥究竟,看看到底提供了哪些能力。感兴趣的同学可以直接看 example_test.go,以及官方提供的各个案例库。
  对于对 rod 不熟悉的同学,我们下来一起看一看它的用法,希望能够带大家感受到它的魔力。
  这里需要注意,rod 只要求大家有 Golang 的环境,请确保已经安装,不用担心 HTML 不熟悉。
  用法入门
  用 rod 打开页面截屏需要几行代码?
  下面这个程序就是答案:
package main

import "github.com/go-rod/rod"

func main() {
    page := rod.New().MustConnect().MustPage("https://www.wikipedia.org/")
    page.MustWaitLoad().MustScreenshot("a.png")
}
  我们新建一个 main.go 文件,复制上面的内容后保存,运行 go run main.go,就会发现在你的项目目录下多了一张图片 a.png,这就是对指定网页的截图:
  rod.New
  回到代码,rod.New 创建了一个浏览器对象:
// New creates a controller.
// DefaultDevice to emulate is set to devices.LaptopWithMDPIScreen.Landscape(), it can make the actual view area
// smaller than the browser window on headful mode, you can use NoDefaultDevice to disable it.
func New() *Browser {
return (&Browser{
ctx:           context.Background(),
sleeper:       DefaultSleeper,
controlURL:    defaults.URL,
slowMotion:    defaults.Slow,
trace:         defaults.Trace,
monitor:       defaults.Monitor,
logger:        DefaultLogger,
defaultDevice: devices.LaptopWithMDPIScreen.Landescape(),
targetsLock:   &sync.Mutex{},
states:        &sync.Map{},
}).WithPanic(utils.Panic)
}
  事实上我们还可以设置为无痕模式,通过链式的 Incognito 方法即可:
// Incognito creates a new incognito browser
func (b *Browser) Incognito() (*Browser, error) {
res, err := proto.TargetCreateBrowserContext{}.Call(b)
if err != nil {
return nil, err
}

incognito := *b
incognito.BrowserContextID = res.BrowserContextID

return &incognito, nil
}
  MustConnect
  拿到了一个 Browser 对象后,通过 MustConnect 来启动并连接到浏览器:
// MustConnect is similar to Browser.Connect
func (b *Browser) MustConnect() *Browser {
b.e(b.Connect())
return b
}

// Connect to the browser and start to control it.
// If fails to connect, try to launch a local browser, if local browser not found try to download one.
func (b *Browser) Connect() error
  MustPage
  连接到浏览器后,我们给出希望打开的地址,通过调用 MustPage 来创建出一个页面对象。对应到下面的 *Page。
// MustPage is similar to Browser.Page.
// The url list will be joined by "/".
func (b *Browser) MustPage(url ...string) *Page {
p, err := b.Page(proto.TargetCreateTarget{URL: strings.Join(url, "/")})
b.e(err)
return p
}
  大家可以把 Page 想象成浏览器里的一个 tab。这就是我们这一行做的事情:
page := rod.New().MustConnect().MustPage("https://www.wikipedia.org/")
  创建一个 rod 浏览器实例,连接上去,打开指定地址的一个页面 tab。
  MustWaitLoad
  最后我们调用 Page 对象的 MustWaitLoad 方法,参照下面 WaitLoad 注释,这里会等待 window.onload 事件,也就是等页面完全加载完成。此处返回值还是 Page 对象,继续链式调用。
// MustWaitLoad is similar to Page.WaitLoad
func (p *Page) MustWaitLoad() *Page {
p.e(p.WaitLoad())
return p
}

// WaitLoad waits for the `window.onload` event, it returns immediately if the event is already fired.
func (p *Page) WaitLoad() error {
defer p.tryTrace(TraceTypeWait, "load")()
_, err := p.Evaluate(evalHelper(js.WaitLoad).ByPromise())
return err
}
  MustScreenshot
  最后,我们调用 Page 对象的 MustScreenshot 来截屏:
// MustScreenshot is similar to Screenshot.
// If the toFile is "", it Page.will save output to "tmp/screenshots" folder, time as the file name.
func (p *Page) MustScreenshot(toFile ...string) []byte {
bin, err := p.Screenshot(false, nil)
p.e(err)
p.e(saveFile(saveFileTypeScreenshot, bin, toFile))
return bin
}

// Screenshot captures the screenshot of current page.
func (p *Page) Screenshot(fullpage bool, req *proto.PageCaptureScreenshot) ([]byte, error) 

// PDF prints page as PDF
func (p *Page) PDF(req *proto.PagePrintToPDF) (*StreamReader, error)
  事实上,我们可以看到这里分为两步:
  截屏,拿到图片的数据,一个字节数组;
  将图片数据写入我们指定的文件中。
  除了图片,这里还支持 PDF,相当强大。这就是这一行的意义:
page.MustWaitLoad().MustScreenshot("a.png")
  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号