Flask 学习 十四 测试

获取代码覆盖报告

安装代码覆盖工具

pip install coverage

manage.py 覆盖检测

COV = None
if os.environ.get('FLASK_COVERAGE'):
    import coverage
    COV = coverage.coverage(branch=True,include='app/*')
    COV.start()
@manager.command
def test(coverage=False):
    '''启动单元测试'''
    if coverage and not os.environ.get('FLASK_COVERAGE'):
        import sys
        os.environ['FLASK_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('覆盖总计:')
        COV.report()
        basedir = os.path.abspath(os.path.dirname(__file__))
        covdir = os.path.join(basedir,'tmp/coverage')
        COV.html_report(directory=covdir)
        print('HTML 版本:file://%s/index.html'% covdir)
        COV.erase()

 Flask测试客户端

测试web程序

tests/test_client.py 使用Flask测试客户端编写的测试框架

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import unittest
from app import create_app,db
from app.models import User,Role
from flask import url_for
import re
class FlaskClientCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app('testing')
        self.app_context = self.app.app_context()
        self.app_context.push()
        db.create_all()
        Role.insert_roles()
        # 测试客户端对象,use_cookies可保存cookies记住上下文
        self.client = self.app.test_client(use_cookies=True)

    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'))
        # as_text = True得到易于处理的字符串而不是字节数组
        self.assertTrue('访客' in response.get_data(as_text = True))

    def test_register_and_login(self):
        # 注册新账户
        respose = self.client.post(url_for('auth.register'),data = {'email':'papapa@qq.com','username':'papapa','password':'abc','password2':'abc'})
        self.assertTrue(respose.status_code ==302)

        # 使用新注册的账户登陆
        respose = self.client.post(url_for('auth.login'),data ={'email':'papapa@qq.com','password':'abc'},follow_redirects=True)
        data = respose.get_data(as_text=True)
        self.assertTrue(re.search('你好,\s+papapa!',data))
        self.assertTrue('你还没有确认你的邮件信息' in data)

        # 发送确认令牌
        user = User.query.fliter_by(email ='papapa@qq.com').first()
        token = user.generate_confirmation_token()
        respose = self.client.get(url_for('auth.confirm',token=token),follow_redirects = True)
        data = respose.get_data(as_text=True)
        self.assertTrue('你已经确认了你的账户' in data)

        # 退出
        respose = self.client.get(url_for('auth.logout'),follow_redirects = True)
        data = respose.get_data(as_text=True)
        self.assertTrue('你已经退出' in data)

config.py 在测试配置中禁用CSRF保护

class Config:
    WTF_CSRF_ENABLED=False # 测试中禁用CSRF保护

测试web api服务

tests/test_api.py 使用Flask 测试客户端测试REST API

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import unittest
from app import create_app,db
from app.models import User,Role
from flask import url_for
import re,json
import base64

class APITestCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app('testing')
        self.app_context = self.app.app_context()
        self.app_context.push()
        db.create_all()
        Role.insert_roles()
        self.client = self.app.test_client()

    def tearDown(self):
        db.session.remove()
        db.drop_all()
        self.app_context.pop()

    def get_api_headers(self,username,password):
        return {
            'Authorization':'Basic' + base64.b64encode((username+':'+password).encode('utf-8')).decode('utf-8'),
            'Accept':'application/json',
            'Content-Type':'application/json'
        }
    def test_no_auth(self):
        response = self.client.get(url_for('api.get_posts'),content_type='application/json')
        self.assertTrue(response.status_code ==401)

    def test_posts(self):
        # 添加一个用户
        r = Role.query.fliter_by(name = 'User').first()
        self.assertIsNotNone(r)
        u = User(email = 'bababa@qq.com',password = 'abc',confirmed = True,role = r)
        db.session.add(u)
        db.session.commit()

        # 写一篇文章
        response = self.client.post(url_for('api.new_post'),
                                    headers = self.get_api_headers('bababa@qq.com','abc'),
                                    data = json.dumps({'body':'body of the blog'}) )
        self.assertTrue(response.status_code ==201)
        url = response.headers.get('Location')
        self.assertIsNotNone(url)

        # 获取刚发布文章
        response = self.client.get(url,headers = self.get_api_headers('bababa@qq.com','abc'))
        self.assertTrue(response.status_code ==200)
        # 测试客户端不会自动json编码解码
        json_response = json.loads(response.data.decode('utf-8'))
        self.assertTrue(json_response['url']==url)
        self.assertTrue(json_response['body']=='body of the blog')
        self.assertTrue(json_response['body_html'=='<p>body of blog post</p>'])

使用Selenium 进行端到端测试

pip install selenium

app/main/views.py 关闭服务器的路由

# 当所有测试完成之后关闭服务器的路由
@main.route('/shutdown')
def server_shutdown():
    # 只有在测试环境中,当前路由可用
    if not current_app.testing:
        abort(404)
    shutdown = request.environ.get('werkzeug.server.shutdown')
    if not shutdown:
        abort(500)
    shutdown()
    return 'Shutting down....'

tests/test_selenium.py 使用Selenium运行测试的框架

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import unittest
from selenium import webdriver
from app import create_app,db
from app.models import User,Role,Post
from flask import url_for
import re,json,time
import base64
import threading

class SeleniumTestCase(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()

            # 禁止日志,保持输出简洁
            import logging
            logger = logging.getLogger('werkzeug')
            logger.setLevel('ERROR')

            # 创建数据库,并使用一些虚拟数据填充
            db.create_all()
            Role.insert_roles()
            User.generate_fake(10)
            Post.generate_fake(10)

            # 添加管理员
            admin_role = Role.query.filter_by(permission=0xff).first()
            admin = User(email = 'bababa@qq.com',username = 'bababa',password = 'abc',role = admin_role,confirmed = True)
            db.session.add(admin)
            db.session.commit()

            # 在一个线程中启动Flask服务器
            threading.Thread(target=cls.app.run).start()

            time.sleep(1)

    @classmethod
    def tearDownClass(cls):
        if cls.client:
            # 关闭Flask服务器和浏览器
            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('web 浏览器无效')
    def tearDown(self):
        pass

    def test_admin_home_page(self):
        # 进入首页
        self.client.get('http://localhost:5000/')
        self.assertTrue(re.search('你好,\s+访客',self.client.page_source))

        # 进入登陆页面
        self.client.find_element_by_link_text('登陆').click()
        self.assertTrue('<h1>登陆</h1>' in self.client.page_source)

        # 登陆
        self.client.find_element_by_name('邮箱').send_keys('bababa.qq.com')
        self.client.find_element_by_name('密码').send_keys('abc')
        self.client.find_element_by_name('提交').click()
        self.assertTrue(re.search('你好,\s+bababa!',self.client.page_source))

        # 进入用户个人资料页面
        self.client.find_element_by_link_text('个人资料').click()
        self.assertTrue('<h1>bababa</h1>' in self.client.page_source)

 

posted @ 2017-06-08 08:29  Erick-LONG  阅读(460)  评论(0编辑  收藏  举报