用flask开发个人博客(40)—— Flask中三种测试方法

一、单元测试并统计测试的覆盖率

1.1 Flask中的单元测试

        在之前的文章中介绍过在flask中进行单元测试的方法,目前我们的代码中存在下面的单元测试:

classBasicTestCase(unittest.TestCase):
    def Setup(self):
        self.app=create_app('testing')
        self.app_context=self.app.app_context()
        self.app_context.push()
        db.create_all()
 
    def teardown(self):
        db.session.remove()
        db.drop_all()
        self.app_context.pop()
 
    def test_app_exits(self):
        self.assertFalse(current_app is None)
 
    def test_app_is_testing(self):
       self.assertFalse(current_app.config['TESTING'])

        并且,我们在manager的命令行中增加了运行单元测试的命令:

@manager.command 
def test(): 
    """Run the unittests""" 
    import unittest 
   tests=unittest.TestLoader().discover('test') 
   unittest.TextTestRunner(verbosity=2).run(tests)

        可以通过命令行,按照下面的方式进行单元测试:

(flasky)hyman@hyman-VirtualBox:~/Github/flaskTs$ python manager.py test
test_app_exits(test_basic.BasicTestCase) ... ok
test_app_is_testing(test_basic.BasicTestCase) ... ok
 
----------------------------------------------------------------------
Ran 2 tests in0.005s
 
OK
 

1.2 使用coverage进行单元测试覆盖率的统计

        Flask中可以使用coverage模块来统计进行单元测试的代码覆盖率,其安装方式如下:

pip installcoverage

        安装完成后,我们将coverage作为一个自定义命令的选项附加到test命令中:

COV=None
ifos.environ.get('COVERAGE'):
    import coverage
    COV=coverage.coverage(branch=True,include='*')
    COV.start()
 
@manager.command 
deftest(coverage=False): 
    """Run the unittests"""
    if coverage and notos.environ.get('COVERAGE'):
        import sys
        os.environ['COVERAGE']='1'
       os.execvp(sys.executable,[sys.executable]+sys.argv)
    import unittest 
   tests=unittest.TestLoader().discover('tests') 
   unittest.TextTestRunner(verbosity=2).run(tests) 
    if COV:
        COV.stop()
        COV.save()
        print('Coverage:')
        COV.report()
        COV.erase()

        首先在全局的范围内,我们先检查是否具有'COVERAGE'环境变量,如果定义了该变量,就利用coverage的coverage函数构造一个coverage对象COV。coverage函数有两个参数,第一个参数branch设置为True表示检查的模块包含指定目录下的子目录,而第二个参数include是在指定目录,设置成’*’的意思就是设置当前目录和当前目录下的模块位检查模块。

        而在test()函数中,附加的命令行参数coverage会作为test()函数的一个bool类型的参数,如果命令行附加了该参数,coverage将会等于True。在函数内部,先判断是否coverage已经是否定义了'COVERAGE'环境变量。因为我们定义以及执行COV的代码为全局代码,在执行test命令行之前已经执行过,所以在判断完之后,调用了os.execvp重新执行了程序。接下来COV变量不再是None,就可进行if COV后面的操作。

        执行的程序结果如下,由于将当前目录下以及子目录下所有的模块都作为了统计对象,所以结果中包含了flask本身自带的一些模块。


二、使用Flask Web测试客户端进行测试   

        Flask中的成员函数test_client()可以返回一个客户端对象,可以模拟Web客户端,用来同Web服务端进行交互。它可以测试Web程序,也可以用来测试Rest API,下面就该客户端的使用进行讨论。

2.1 测试Web程序

        新建一个FlaskClientTest单元测试类如下:

classFlaskClientTest(unittest.TestCase):
    def setUp(self):
        self.app=create_app('testing')
        self.app_context=self.app.app_context()
        self.app_context.push()
        db.create_all()
        self.client=self.app.test_client()
   
    def tearDown(self):
        db.session.remove()
        db.drop_all()
        self.app_context.pop()
   
    def test_home_page(self):
        response=self.client.get(url_for('main.index'))
        self.assertTrue('Home' inresponse.get_data(as_text=True))
   
    def test_register(self):
       response=self.client.post(url_for('main.register'),data={
            'email':'[email protected]',
            'name':'Hyman',
            'password1':'123',
            'password2':'123'})
       self.assertTrue(response.status_code==302)

        test_home_page是用来进行测试主页的测试用例,这里使用self.client.get(url_for('main.index'))的方法,使用GET方法访问相应的url,并获取响应结果。然后调用response的get_data()获取响应结果,这里面设置as_text=True,会把结果格式化成一个字符串,进而判断‘Home’是否在这个字符串中。

        test_register用来模拟注册用户过程,对注册的路由进行测试。这里采用了POST的方法发送了一个表单,而data中是对表单各个字段的赋值,这里面的key值一定要和我们定义的注册表单的字段名称一致。由于注册路由最终向主页进行了重定向,届时服务端回返回给浏览器302的状态码,我们通过判断响应的status_code是否是302而判断测试是否通过。

2.2 测试Rest API

        接下来,测试下上篇文章编写的一个用来增加新博客的Rest API:

deftest_posts(self):
        response=self.client.post(
            url_for('main.new_post'),
            data=json.dumps({'body':'I am a newpost'}),
            content_type='application/json')
       self.assertTrue(response.status_code==200)

        这里面需要注意的就是,测试Rest API时需要在client的post方法中显示的指定传送的数据类型content_type为'application/json',而且传送的data需要用json.dumps()方法将数据格式化成json的格式。assertTrue中还是检验响应的status_code,如果文章创建成功,服务端会返回200的状态码,所以在这里同200进行了比较。

三、使用selenium进行自动化测试

        Selenium可以自动启动浏览器,模拟用户点击浏览器的连接,selenium的安装方法如下:

pip install selenium

        我们进行测试的思路时,利用后台线程启动服务端,然后在主线程中利用selenium自动操作firefox进行自动化的测试。在使用selenium之前,我们先要定义一个路由可以关闭服务端的运行,以使在测试结束后服务端自动结束运行。

@main.route('/shutdown')
defserv_shutdown():
   shutdown=request.environ.get('werkzeug.server.shutdown')
    shutdown()
    return 'shuting down'

         werkzeug服务器提供了一个shutdown函数供外部使用,在上述的视图函数中,我们获取到了这个函数,并进行调用,这样通过访问http://localhost:5000/shutdown这个url我们就可以实现关闭服务端。

        接下来是使用selenium进行自动化测试的代码:

#coding:utf-8
import unittest
from flaskimport current_app,url_for,json
from app importcreate_app,db
from seleniumimport webdriver
 
classSeleniumTestCase(unittest.TestCase):
    client=None
    @classmethod
    def setUpClass(cls):
        #启动firefox
        try:
            cls.client=webdriver.Firefox()
        except:
            pass
        if cls.client:
            cls.app=create_app('testing')
           cls.app_context=cls.app.app_context()
            cls.app_context.push()
            db.create_all()
           threading.Thread(target=cls.app.run).start()
    @classmethod
    def tearDownClass(cls):
        if cls.client:
           cls.client.get('http://localhost:5000/shutdown')
            cls.client.close()
            db.drop_all()
            db.session.remove()
            cls.app_context.pop()
 
    def setUp(self):
        if not self.client:
            self.skipTest('Firefox isinvailable')
   
    def tearDown(self):
        pass
   
    def test_home_page(self):
       self.client.get('http://localhost:5000/')
        self.assertTrue(re.search('Home',self.client.page_source))

        通过修饰器classmethod定义了setUpClass()和tearDownClass()两个成员函数,分别在该类中的所有的测试用例执行之前和之后运行,而setUp()和tearDown()是在每个测试用例执行前后都运行。在setUpClass()中,通过webdriver.Firefox()获取到了Firefox()浏览器对象,并在threading.Thread(target=cls.app.run).start()中在后台线程运行服务端,然后在测试用例test_home_page中我们通过这个浏览器对象访问'http://localhost:5000/',并利用正则表达式判断返回的结果中是否包含'Home'字符串。在所有的测试完成之后,在tearDownClass()中访问'http://localhost:5000/shutdown'从而完成了服务端的关闭。至此,一个完整的selenium测试流程完成。

        Selenium的强大在于,它可以操作浏览器实现自动化的测试,仿佛就像有人在浏览器上点击一样,它创建的Firefox客户端对象提供了很多方法可以在Web界面找到相关的元素并进行后续的操作。比如find_element_by_link_text()方法,可以根据超链接的名称找到该链接,并在调用其Click()方法模拟点击超链接;find_element_by_name()方法根据根据名字找到对应的字段,然后可以使用期send_keys()的方法在各个字段中填入值。



Github位置:
https://github.com/HymanLiuTS/flaskTs
克隆本项目:
Git clone Git@github.com:HymanLiuTS/flaskTs.Git
获取本文源代码:
Git checkout FL40

    

你可能感兴趣的:(用flask开发个人博客)