51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 655|回复: 0
打印 上一主题 下一主题

[原创] Web测试好帮手Rod(一)

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2022-11-2 16:30:59 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-11-22 01:52 , Processed in 0.067138 second(s), 25 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表