51Testing软件测试论坛

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

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

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

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

[复制链接]
  • TA的每日心情
    无聊
    昨天 09:05
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-11-3 14:20:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    获取图片
      思路一样,区别是我们需要获取二进制的图片数据了,这里需要用到 Element 的MustResource方法:
    1. <font size="3">// MustResource is similar to Element.Resource

    2. func (el *Element) MustResource() []byte {

    3. bin, err := el.Resource()

    4. el.e(err)

    5. return bin

    6. }




    7. // Resource returns the "src" content of current element. Such as the jpg of <img src="a.jpg">

    8. func (el *Element) Resource() ([]byte, error) {

    9. src, err := el.Evaluate(evalHelper(js.Resource).ByPromise())

    10. if err != nil {

    11. return nil, err

    12. }




    13. return el.page.GetResource(src.Value.String())

    14. }</font>
    复制代码


    这里会返回一个字节数组,需要我们进行转换。rod 的 util 包也提供了相关方法:


    1. <font size="3">// OutputFile auto creates file if not exists, it will try to detect the data type and

    2. // auto output binary, string or json

    3. func OutputFile(p string, data interface{}) error {

    4. dir := filepath.Dir(p)

    5. _ = Mkdir(dir)




    6. var bin []byte




    7. switch t := data.(type) {

    8. case []byte:

    9. bin = t

    10. case string:

    11. bin = []byte(t)

    12. case io.Reader:

    13. f, _ := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)

    14. _, err := io.Copy(f, t)

    15. return err

    16. default:

    17. bin = MustToJSONBytes(data)

    18. }




    19. return ioutil.WriteFile(p, bin, 0664)

    20. }</font>
    复制代码


    这里接收的 data 就可以是个字节数组,p 是文件输出路径。OutputFile 会自行处理创建目录,文件,写入数据的流程。下面我们改下代码看看:


    1. <font size="3">package main




    2. import (

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

    4.     "github.com/go-rod/rod/lib/utils"

    5. )




    6. func main() {

    7.     page := rod.New().MustConnect().MustPage("https://www.wikipedia.org/")




    8.     page.MustElement("#searchInput").MustInput("earth")

    9.     page.MustElement("#search-form > fieldset > button").MustClick()




    10.     el := page.MustElement("#mw-content-text > div.mw-parser-output > table.infobox > tbody > tr:nth-child(1) > td > a > img")

    11.     _ = utils.OutputFile("b.png", el.MustResource())

    12. }</font>
    复制代码
    重新运行后,我们的 b.png 文件就包含了要爬的图片:





    E2E 示例
      这一节我们来看看官方给出的 E2E 示例。真实的业务场景远比这个复杂,但本质内核都是一样的,大家可以结合相关 API 设计自己的 E2E 流程。
      https://ahfarmer.github.io/calculator/是一个经典的在线计算器,大家可以自己打开看一看,页面非常简单,模拟大家手机的计算器 app。
      假定我们是这个计算器的开发者,希望做端到端测试,就可以借助 rod 的能力,直接在网页上操作加减法,看看最终结果是否符合预期。
      为此,我们还需要借助一些测试框架的断言能力,比较结果。这里官方是用了作者自己的测试库:"github.com/ysmood/got",大家有需求的话其实可以替换成自己常用的。
      第一步,我们需要先创建出一个 Browser 对象,毕竟 E2E 不可能只测几个单独的case,也需要支持并发。这里可以使用一个共用的 Browser 对象。新建一个 setup_test.go,填充以下内容:



    1. <font size="3">// This is the setup file for this test suite.




    2. package main




    3. import (

    4. "testing"




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

    6. "github.com/ysmood/got"

    7. )




    8. // test context

    9. type G struct {

    10. got.G




    11. browser *rod.Browser

    12. }




    13. // setup for tests

    14. var setup = func() func(t *testing.T) G {

    15. browser := rod.New().MustConnect()




    16. return func(t *testing.T) G {

    17. t.Parallel() // run each test concurrently




    18. return G{got.New(t), browser}

    19. }

    20. }()




    21. // a helper function to create an incognito page

    22. func (g G) page(url string) *rod.Page {

    23. page := g.browser.MustIncognito().MustPage(url)

    24. g.Cleanup(page.MustClose)

    25. return page

    26. }</font>
    复制代码


    这里的 page 也是默认开启无痕模式,打开指定的 url,利用了 ysmood/got 的 Cleanup 能力随后进行清理,本质是个 helper 函数。
      第二步,开始校验业务逻辑,新建 calculator_test.go,填充以下内容:



    1. <font size="3">// Package main ...

    2. package main




    3. import "testing"




    4. // test case: 1 + 2 = 3

    5. func TestAdd(t *testing.T) {

    6. g := setup(t)




    7. p := g.page("https://ahfarmer.github.io/calculator")




    8. p.MustElementR("button", "1").MustClick()

    9. p.MustElementR("button", `^\+[b][size=3]获取图片[/size][/b]
    10. [size=3]  思路一样,区别是我们需要获取二进制的图片数据了,这里需要用到 Element 的MustResource方法:[/size]
    11. [code]<font size="3">// MustResource is similar to Element.Resource

    12. func (el *Element) MustResource() []byte {

    13. bin, err := el.Resource()

    14. el.e(err)

    15. return bin

    16. }




    17. // Resource returns the "src" content of current element. Such as the jpg of <img src="a.jpg">

    18. func (el *Element) Resource() ([]byte, error) {

    19. src, err := el.Evaluate(evalHelper(js.Resource).ByPromise())

    20. if err != nil {

    21. return nil, err

    22. }




    23. return el.page.GetResource(src.Value.String())

    24. }</font>
    复制代码


    这里会返回一个字节数组,需要我们进行转换。rod 的 util 包也提供了相关方法:


    1. <font size="3">// OutputFile auto creates file if not exists, it will try to detect the data type and

    2. // auto output binary, string or json

    3. func OutputFile(p string, data interface{}) error {

    4. dir := filepath.Dir(p)

    5. _ = Mkdir(dir)




    6. var bin []byte




    7. switch t := data.(type) {

    8. case []byte:

    9. bin = t

    10. case string:

    11. bin = []byte(t)

    12. case io.Reader:

    13. f, _ := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)

    14. _, err := io.Copy(f, t)

    15. return err

    16. default:

    17. bin = MustToJSONBytes(data)

    18. }




    19. return ioutil.WriteFile(p, bin, 0664)

    20. }</font>
    复制代码


    这里接收的 data 就可以是个字节数组,p 是文件输出路径。OutputFile 会自行处理创建目录,文件,写入数据的流程。下面我们改下代码看看:


    1. <font size="3">package main




    2. import (

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

    4.     "github.com/go-rod/rod/lib/utils"

    5. )




    6. func main() {

    7.     page := rod.New().MustConnect().MustPage("https://www.wikipedia.org/")




    8.     page.MustElement("#searchInput").MustInput("earth")

    9.     page.MustElement("#search-form > fieldset > button").MustClick()




    10.     el := page.MustElement("#mw-content-text > div.mw-parser-output > table.infobox > tbody > tr:nth-child(1) > td > a > img")

    11.     _ = utils.OutputFile("b.png", el.MustResource())

    12. }</font>
    复制代码
    重新运行后,我们的 b.png 文件就包含了要爬的图片:





    E2E 示例
      这一节我们来看看官方给出的 E2E 示例。真实的业务场景远比这个复杂,但本质内核都是一样的,大家可以结合相关 API 设计自己的 E2E 流程。
      https://ahfarmer.github.io/calculator/是一个经典的在线计算器,大家可以自己打开看一看,页面非常简单,模拟大家手机的计算器 app。
      假定我们是这个计算器的开发者,希望做端到端测试,就可以借助 rod 的能力,直接在网页上操作加减法,看看最终结果是否符合预期。
      为此,我们还需要借助一些测试框架的断言能力,比较结果。这里官方是用了作者自己的测试库:"github.com/ysmood/got",大家有需求的话其实可以替换成自己常用的。
      第一步,我们需要先创建出一个 Browser 对象,毕竟 E2E 不可能只测几个单独的case,也需要支持并发。这里可以使用一个共用的 Browser 对象。新建一个 setup_test.go,填充以下内容:



    1. <font size="3">// This is the setup file for this test suite.




    2. package main




    3. import (

    4. "testing"




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

    6. "github.com/ysmood/got"

    7. )




    8. // test context

    9. type G struct {

    10. got.G




    11. browser *rod.Browser

    12. }




    13. // setup for tests

    14. var setup = func() func(t *testing.T) G {

    15. browser := rod.New().MustConnect()




    16. return func(t *testing.T) G {

    17. t.Parallel() // run each test concurrently




    18. return G{got.New(t), browser}

    19. }

    20. }()




    21. // a helper function to create an incognito page

    22. func (g G) page(url string) *rod.Page {

    23. page := g.browser.MustIncognito().MustPage(url)

    24. g.Cleanup(page.MustClose)

    25. return page

    26. }</font>
    复制代码


    这里的 page 也是默认开启无痕模式,打开指定的 url,利用了 ysmood/got 的 Cleanup 能力随后进行清理,本质是个 helper 函数。
      第二步,开始校验业务逻辑,新建 calculator_test.go,填充以下内容:



    ).MustClick()

    p.MustElementR("button", "2").MustClick()

    p.MustElementR("button", "=").MustClick()




    // assert the result with t.Eq

    g.Eq(p.MustElement(".component-display").MustText(), "3")

    }




    // test case: 2 * 3 = 6

    func TestMultiple(t *testing.T) {

    g := setup(t)




    p := g.page("https://ahfarmer.github.io/calculator")




    // use for-loop to click each button

    for _, regex := range []string{"2", "x", "3", "="} {

    p.MustElementR("button", regex).MustClick()

    }




    g.Eq(p.MustElement(".component-display").MustText(), "6")

    }</font>[/code]

    这里只是示例,case 比较简单。大家可以自行调整一下试试。
      第一个case我们需要校验加法是否正常我们打开计算器网页,通过 MustElementR 获取到指定元素,触发点击。



    1. <font size="3">// MustElementR is similar to Page.ElementR

    2. func (p *Page) MustElementR(selector, jsRegex string) *Element {

    3. el, err := p.ElementR(selector, jsRegex)

    4. p.e(err)

    5. return el

    6. }




    7. // ElementR retries until an element in the page that matches the css selector and it's text matches the jsRegex,

    8. // then returns the matched element.

    9. func (p *Page) ElementR(selector, jsRegex string) (*Element, error) {

    10. return p.ElementByJS(evalHelper(js.ElementR, selector, jsRegex))

    11. }</font>
    复制代码


    这里的 MustElementR 补充了正则能力,毕竟有些元素单独靠 selector 无法定位,这里是增强的能力。
      触发元素点击后,通过断言,判断文本和指定的结果是否匹配即可。
      第二个 case 也是类似的场景,大家可以由此体会一下 MustElementR 的用法。
      执行测试case和平常的 Golang 单测没有区别,直接 go test 命令即可。
      小结
      这里我们只是拿出来 rod 常见的一些用法,它可以打印,可以转图片,截屏,导出 pdf。可以模拟人为的浏览器操作,整体功能还是非常强大的,建议大家自己动手试一下。官方的 guide 也是很不错的参考资料。


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

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

    使用道具 举报

    本版积分规则

    关闭

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

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

    GMT+8, 2024-11-22 08:13 , Processed in 0.065284 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

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