【VSRC唯科普】用自动化程序测试网站(13/14篇)

百家 作者:唯品会安全 2020-04-08 12:12:22

鸣 谢



VSRC感谢业界小伙伴——Mils 投稿精品科普类文章。VSRC欢迎精品原创类文章投稿,优秀文章一旦采纳发布,将有好礼相送,我们已为您准备好了丰富的奖品!

(活动最终解释权归VSRC所有)




当研发一个技术栈较大的网络项目时,经常只对栈底,即项目后期用到的技术和功能,进行一些常规测试。目前大多数编程语言,包括Python在内,都有一些测试框架,但是网站的前端通常并没有自动化的测试工具,尽管前端通常是整个项目中真正与用户零距离接触的部分之一。每当有新的特性加入网站时,或一个元素的位置改变时,测试小组通常就会执行一组自动化测试来进行验证。

在本次的唯科普中,将介绍测试的基础知识,以及如何使用Python网络爬虫测试各种简单或复杂的网站,大致分为以下四块内容:

1.使用Python进行单元测试Unit Test

2.测试维基百科

3.Selenium测试

4.Python单元测试与Selenium单元测试的选择





1.使用Python进行单元测试Unit Test


运行一套自动化的测试方法,即能确保代码按照既定的目标运行,还能节约人力时间,使得版本升级变得更加高效和简单。为了了解什么是单元测试,我们这里引用网上一段对单元测试较为直观的描述来进行解释:“单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。例如,你可能把一个很大的值放入一个有序list 中去,然后确认该值出现在list 的尾部。或者,你可能会从字符串中删除匹配某种模式的字符,然后确认字符串确实不再包含这些字符了。单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。工厂在组装一台电视机之前,会对每个元件都进行测试,这,就是单元测试。”

在Python中,可以使用unittest模块来进行单元测试,导入模块后继承unittest.TestCase类,就可以实现以下功能:

  • 为每个单元测试的开始和技术提供setUp和tearDown函数

  • 提供不同类型的“断言”语句让测试成功或失败

  • 把所有以test_开头的函数当作单元测试运行,忽略不带test_函数





2.测试维基百科


将Python的unittest库与网络爬虫组合起来,就可以对不含有JavaScript的网站前端进行测试的功能:

#!/usr/bin/env python
# -*-coding:utf-8-*-

from urllib.request import urlopen
from bs4 import BeautifulSoup
import unittest

class WikiTest(unittest.TestCase):
    def setUpClass(self):
        global bsObj
        url = "https://wiki.mbalib.com/wiki/Python"
        bsObj = BeautifulSoup(urlopen(url))

    def t_titleTest(self):
        global bsObj
        page_title = bsObj.find("h1").get_text()
        self.assertEqual("Python", page_title)
        # assertEqual若两个值相等,则pass

    def t_contentExists(self):
        global bsObj
        content = bsObj.find("div", {"id""BAIDU_DUP_fp_wrapper"})
        # 测试是否有一个节点id属性是BAIDU_DUP_fp_wrapper
        self.assertIsNotNone(content)

if __name__ == '__main_':
    unittest.main()

运行成功以后会得到以下返回结果:

Ran 0 tests in 0.000s
OK
Process finished with exit code 0

这里需要注意的是这个页面只加载一次,全局对象bsObj由多个测试共享,这是通过unittest类的setUpClass函数来实现的,这个函数只在类的初始化阶段运行一次,一次性采集全部内容,供多个测试使用。由于有很多种方法可以重复执行一次测试操作,但是又必须对即将在页面上运行的所有测试都时刻保持谨慎,因为我们只加载一次页面,而且我们必须避免在内存中一次性加大量的信息,这里可以通过以下设置来实现:

#!/usr/bin/env python
# -*-coding:utf-8-*-

from urllib.request import urlopen
from urllib.request import urlparse
from bs4 import BeautifulSoup
import unittest

class TestWiki(unittest.TestCase):
    bsObj = None
    url = None

    def Test_PageProperties(self):
        global bsObj
        global url

        url = "https://wiki.mbalib.com/wiki/Python"
        for i in range(1100):
            bsObj = BeautifulSoup(urlopen(url))
            titles = self.titleMatchesURL()
            self.asserEquals(titles[0], titles[1])
            self.asserTrue(self.contentExists())
            url = self.getNextLink()
        print("done")

    def titleMatchesURL(self):
        global bsObj
        global url
        pageTitle = bsObj.find("h1").get_text()
        urlTitle = url[(url.index("/wiki/")+6):]
        urlTitle = urlTitle.replace("_"' ')
        urlTitle = unquote(urlTitle)
        return [pageTitle.lower(), urlTitle.loser()]

    def contentExists(self):
        global bsObj
        content = bsObj.find("div",{"id":"BAIDU_DUP_fp_wrapper"})
        if content is not None:
            return True
        return False

if __name__ == '__main_':
    unittest.main()





3.Selenium测试


虽然在前几次的唯科普中,我们介绍过链接跳转、表单提交和其他网站交互行为,但是本质都是为了避开浏览器的图形界面,而不是直接使用浏览器。Selenium可以在浏览器上实现诸如文字输入、点击按钮等操作,这样就可以找出异常表单、JavaScript代码错误、HTML排版错误,以及其他用户使用过程中可能出现的问题。以下示例中的测试代码,使用的是Selenium的elements对象,elements对象可通过以下方式进行调用。

usernameFileld = driver.find_element_by_name('username')

就像用户可以在浏览器里面对网站上的不同元素执行一系列操作一样,Selenium也可以对任何给定元素执行很多操作:

myElement.Click()
myElement.Click_and_hold()
myElement.release()
myElement.double_click()
myElement.send_keys_to_element("content to enter")

为了一次完成针对同一个元素的多个操作,可以使用动作链(action chain)存储多个操作,然后在一个程序中执行一次或多次。用动作链存储多个操作也比较方便,并且他们的功能和前面示例中对一个元素显式调用操作是完全一样的。

为了演示两种方式的差异,以 http://pythonscraping.com/pages/files/form.html 的表单为例,用以下方式填写并提交:

#!/usr/bin/env python
# -*-coding:utf-8-*-

from selenium import webdriver
from selenium.webdriver.remote.webelement import  WebElement
from selenium.webdriver.common.keys import Keys
from selenium.webdriver import ActionChains

driver = webdriver.Chrome(executable_path='C:\chromedriver.exe')
driver.get("http://pythonscraping.com/pages/files/form.html")

firstnameField = driver.find_elements_by_name('firstname')[0]
lastnameField = driver.find_elements_by_name('lastname')[0]
submitButton = driver.find_element_by_id('submit')

# method 1
firstnameField.send_keys("VSRC")
lastnameField.send_keys('POP')
submitButton.click()

# method 2
actions = ActionChains(driver).click(firstnameField).send_keys("VSRC").click(lastnameField).send_keys('POP').send_keys(Keys.RETURN)
actions.perform()

print(driver.find_elements_by_tag_name('body')[0].text)
driver.close()

使用方法1在两个字段上都调用send_keys,然后点击提交按钮;而方法2在用一个动作链来点击每个字段并填写内容,最后确认,这些行为是在perform调用之后才发生的。无论用第一个方法还是第二个方法,这个程序的执行结果都一样:

Hello there,VSRC POP!

这两个方法除了处理命令的对象不同之外,第二个方法还有一点差异,注意这里第一个方法提交使用的是点击click操作,而第二个方法提交表单使用的是回车键Keys.RETURN,因为实现同样效果的网络事件发生顺序可以有多种,所以Selenium实现同样的结果也有许多方式。

这里再演示一个鼠标拖放动作。单击按钮和输入文字只是Selenium的一个功能,其真正的亮点是能够处理更加复杂的网络表单交互行为。Selenium可以轻松地完成鼠标拖放动作(drag-and-drop),使用它的拖放函数,你需要指定一个被拖放的元素以及拖放的距离,护着元素将被拖放到的目标元素。这里使用   http://pythonscraping.com/pages/javascript/draggableDemo.html 页面来演示拖放动作:

from selenium import webdriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver import ActionChains
import time

exec_path = "C:\chromedriver.exe"
driver = webdriver.Chrome(executable_path=exec_path)
driver.get('http://pythonscraping.com/pages/javascript/draggableDemo.html')
print(driver.find_element_by_id('message').text)

element = driver.find_element_by_id('draggable')
target = driver.find_element_by_id('div2')
actions = ActionChains(driver)
actions.drag_and_drop(element, target).perform()
time.sleep(1)
print(driver.find_element_by_id('message').text)
driver.close()

运行后该程序会返回以下两条信息:

Prove you are not a bot, by dragging the square from the blue area to the red area!
You are definitely not a bot!






4.Python单元测试与Selenium单元测试的选择


通常,Python的单元测试语法严谨且冗长,更适合大型项目写测试,而Selenium的测试方式更为灵活且功能强大,可以成为一些网站功能测试的首选,两者各有不同的特点,且组合起来使用效果也更为高效。以下是一段测试拖拽功能的单元测试程序,如果一个元素并未被正确的拖放到另一个元素内,那么推断条件成立,则会显示“Prove you are not a bot":

#!/usr/bin/env python
# -*-coding:utf-8-*-

from selenium import webdriver
from selenium.webdriver import ActionChains
import unittest

class TestAddition(unittest.TestCase):
    driver = None

    def setUp(self):
        global driver
        driver = webdriver.Chrome(executable_path="C:\chromedriver.exe")
        driver.get('http://pythonscraping.com/pages/javascript/draggableDemo.html')

    def test_drag(self):
        global driver
        element = driver.find_element_by_id('draggable')
        target = driver.find_element_by_id('div2')
        actions = ActionChains(driver)
        actions.drag_and_drop(element, target).perform()

        self.assertEqual("Prove you are not a bot, by dragging the square from the blue area to the red area!", driver.find_element_by_id("message").text)

if __name__ == '__main_':
    unittest.main()

所以,大多数网站上可以看到的内容,一般都可以通过Python的单元测试和Selenium组合测试来完成。





参考资料


1、https://www.w3school.com.cn

2、https://www.python.org/

3、《Web Scraping with Python》




唯科普 | 《数据采集》目录

A.K.A "小白终结者"系列

第1篇、初识网络通信

第2篇、来点更精彩的正则表达式吧

第3篇、多种数据采集方式

第4篇、让我们加入点API

第5篇、数据的存储

第6篇、文档读取

第7篇、数据清洗

第8篇、自然语言处理之概括数据(上篇)

第8篇、自然语言处理之马尔可夫模型(中篇)

第8篇、自然语言处理之六度分割终极篇(下篇)

第9篇、穿越网页表单与登录窗口的采集

第10篇、关于数据的采集姿势

第11篇、图像识别与文字处理

第12篇、避开采集的陷阱

第13篇、用自动化程序测试网站

第14篇、远程采集




精彩原创文章投稿有惊喜!

欢迎投稿!

VSRC欢迎精品原创类文章投稿,优秀文章一旦采纳发布,将为您准备的丰富奖金税后1000元现金或等值礼品,上不封顶!如若是安全文章连载,奖金更加丰厚,税后10000元或等值礼品,上不封顶!还可领取精美礼品!可点击“阅读原文”了解规则。(最终奖励以文章质量为准。活动最终解释权归VSRC所有)



我们聆听您宝贵建议


不知道,大家都喜欢阅读哪些类型的信息安全文章?

不知道,大家都希望我们更新关于哪些主题的干货?

现在起,只要您有任何想法或建议,欢迎直接回复本公众号留言!

精彩留言互动的热心用户,将有机会获得VSRC赠送的精美奖品一份!

同时,我们也会根据大家反馈的建议,选取热门话题,进行原创发布!


点击阅读原文进入   【VSRC征稿】宅家副业攻略请查收!



关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接