18、测试、性能优化与部署指南

测试、性能优化与部署指南

1. 测试Web服务

若已在GitHub上克隆应用的Git仓库,可运行 git checkout 15b 来检出该版本的应用。Flask测试客户端也可用于测试RESTful Web服务。以下是一个包含两个测试的单元测试类示例:

# tests/test_api.py: RESTful API testing with the Flask test client
import unittest
from flask import url_for
import json
from base64 import b64encode
from app.models import Role, User, db

class APITestCase(unittest.TestCase):
    # ...
    def get_api_headers(self, username, password):
        return {
            'Authorization':
                'Basic ' + 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):
        # add a user
        r = Role.query.filter_by(name='User').first()
        self.assertIsNotNone(r)
        u = User(email='john@example.com', password='cat', confirmed=True,
                 role=r)
        db.session.add(u)
        db.session.commit()
        # write a post
        response = self.client.post(
            url_for('api.new_post'),
            headers=self.get_api_headers('john@example.com', 'cat'),
            data=json.dumps({'body': 'body of the blog post'}))
        self.assertTrue(response.status_code == 201)
        url = response.headers.get('Location')
        self.assertIsNotNone(url)
        # get the new post
        response = self.client.get(
            url,
            headers=self.get_api_headers('john@example.com', 'cat'))
        self.assertTrue(response.status_code == 200)
        json_response = json.loads(response.data.decode('utf-8'))
        self.assertTrue(json_response['url'] == url)
        self.assertTrue(json_response['body'] == 'body of the *blog* post')
        self.assertTrue(json_response['body_html'] ==
                        '<p>body of the <em>blog</em> post</p>')

API测试的 setUp() tearDown() 方法与常规应用相同,但无需配置cookie支持,因为API不使用它。 get_api_headers() 方法是一个辅助方法,返回所有请求都需发送的通用头信息,包括认证凭证和MIME类型相关头信息。

  • test_no_auth() 测试确保不包含认证凭证的请求会被拒绝,并返回错误代码401。
  • test_posts() 测试向数据库添加用户,然后使用RESTful API插入博客文章并读取。发送数据的请求必须使用 json.dumps() 进行编码,响应体也以JSON格式返回,需使用 json.loads() 解码后才能检查。
2. 端到端测试与Selenium

Flask测试客户端无法完全模拟运行中应用的环境,例如依赖客户端浏览器中JavaScript代码运行的应用就无法正常工作。此时,可使用Selenium进行端到端测试。

首先,安装Selenium的Python接口:

(venv) $ pip install selenium

测试时,应用需在监听真实HTTP请求的Web服务器中运行。此方法在后台线程中使用开发服务器启动应用,同时在主线程上运行测试。Selenium在测试控制下启动Web浏览器并连接到应用执行操作。

为了在测试完成后优雅地停止Flask服务器,可实现一个服务器关闭路由:

# _app/main/views.py: Server shutdown route
from flask import request, abort, current_app
from . import main

@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...'

以下是一个使用Selenium配置测试用例的框架:

# tests/test_selenium.py: Framework for tests using Selenium
import unittest
from selenium import webdriver
import threading
from app import create_app, db
from app.models import Role, User

class SeleniumTestCase(unittest.TestCase):
    client = None
    @classmethod
    def setUpClass(cls):
        # start Firefox
        try:
            cls.client = webdriver.Firefox()
        except:
            pass
        # skip these tests if the browser could not be started
        if cls.client:
            # create the application
            cls.app = create_app('testing')
            cls.app_context = cls.app.app_context()
            cls.app_context.push()
            # suppress logging to keep unittest output clean
            import logging
            logger = logging.getLogger('werkzeug')
            logger.setLevel("ERROR")
            # create the database and populate with some fake data
            db.create_all()
            Role.insert_roles()
            User.generate_fake(10)
            Post.generate_fake(10)
            # add an administrator user
            admin_role = Role.query.filter_by(permissions=0xff).first()
            admin = User(email='john@example.com',
                         username='john', password='cat',
                         role=admin_role, confirmed=True)
            db.session.add(admin)
            db.session.commit()
            # start the Flask server in a thread
            threading.Thread(target=cls.app.run).start()
    @classmethod
    def tearDownClass(cls):
        if cls.client:
            # stop the flask server and the browser
            cls.client.get('http://localhost:5000/shutdown')
            cls.client.close()
            # destroy database
            db.drop_all()
            db.session.remove()
            # remove application context
            cls.app_context.pop()
    def setUp(self):
        if not self.client:
            self.skipTest('Web browser not available')
    def tearDown(self):
        pass

以下是一个使用Selenium的单元测试示例:

# tests/test_selenium.py: Example Selenium unit test
import unittest
import re
from selenium import webdriver

class SeleniumTestCase(unittest.TestCase):
    # ...
    def test_admin_home_page(self):
        # navigate to home page
        self.client.get('http://localhost:5000/')
        self.assertTrue(re.search('Hello,\s+Stranger!',
                                  self.client.page_source))
        # navigate to login page
        self.client.find_element_by_link_text('Log In').click()
        self.assertTrue('<h1>Login</h1>' in self.client.page_source)
        # login
        self.client.find_element_by_name('email').\
            send_keys('john@example.com')
        self.client.find_element_by_name('password').send_keys('cat')
        self.client.find_element_by_name('submit').click()
        self.assertTrue(re.search('Hello,\s+john!', self.client.page_source))
        # navigate to the user's profile page
        self.client.find_element_by_link_text('Profile').click()
        self.assertTrue('<h1>john</h1>' in self.client.page_source)

此测试使用 setUpClass() 中创建的管理员账户登录应用,然后打开个人资料页面。与Flask测试客户端不同,Selenium测试向Web浏览器发送命令,不直接与应用交互,命令与真实用户使用鼠标或键盘执行的操作相似。

3. 性能优化
3.1 记录缓慢的数据库性能

应用性能随时间下降,可能是由于数据库查询缓慢,且随着数据库规模增大而恶化。优化数据库查询可简单到添加更多索引,也可复杂到在应用和数据库之间添加缓存。

Flask - SQLAlchemy可记录请求期间发出的数据库查询统计信息。以下是记录慢查询的示例:

# app/main/views.py: Report slow database queries
from flask.ext.sqlalchemy import get_debug_queries
from . import main
from flask import current_app

@main.after_app_request
def after_request(response):
    for query in get_debug_queries():
        if query.duration >= current_app.config['FLASKY_SLOW_DB_QUERY_TIME']:
            current_app.logger.warning(
                'Slow query: %s\nParameters: %s\nDuration: %fs\nContext: %s\n' %
                    (query.statement, query.parameters, query.duration,
                     query.context))
    return response

get_debug_queries() 函数返回请求期间发出的查询列表,每个查询的信息如下表所示:

名称 描述
statement SQL语句
parameters SQL语句使用的参数
start_time 查询发出的时间
end_time 查询返回的时间
duration 查询持续的时间(秒)
context 指示查询发出的源代码位置的字符串

为了在生产模式下启用数据库查询性能记录,需进行以下配置更改:

# config.py: Configuration for slow query reporting
class Config:
    # ...
    SQLALCHEMY_RECORD_QUERIES = True
    FLASKY_DB_QUERY_TIMEOUT = 0.5
    # ...
3.2 源代码分析

另一个可能的性能问题来源是高CPU消耗,由执行大量计算的函数引起。源代码分析器有助于找到应用中最慢的部分。

Flask的开发Web服务器可选择为每个请求启用Python分析器。以下是添加启动分析器的命令行选项示例:

# manage.py: Run the application under the request profiler
from flask_script import Manager
from werkzeug.contrib.profiler import ProfilerMiddleware
from app import app

manager = Manager(app)

@manager.command
def profile(length=25, profile_dir=None):
    """Start the application under the code profiler."""
    app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[length],
                                      profile_dir=profile_dir)
    app.run()

当使用 python manage.py profile 启动应用时,控制台将显示每个请求的分析器统计信息,包括最慢的25个函数。可使用 --length 选项更改报告中显示的函数数量,使用 --profile-dir 选项将每个请求的分析数据保存到指定目录的文件中。

4. 部署

Flask自带的Web开发服务器在生产环境中不够健壮、安全或高效。部署Flask应用时,无论使用何种托管方法,在生产服务器上安装应用时都需执行一系列任务,如创建或更新数据库表。

可在 manage.py 中添加一个命令来执行所有必需的任务:

# manage.py: deploy command
from flask_script import Manager
from flask.ext.migrate import upgrade
from app.models import Role, User
from app import app

manager = Manager(app)

@manager.command
def deploy():
    """Run deployment tasks."""
    # migrate database to latest revision
    upgrade()
    # create user roles
    Role.insert_roles()
    # create self-follows for all users
    User.add_self_follows()

总结

测试是确保应用质量的重要环节,应设计高效的测试策略,并编写易于测试的代码。性能优化能提升应用响应速度,提高用户体验。合理的部署流程可确保应用在生产环境中稳定运行。通过以上方法,可有效提升应用的质量、性能和可靠性。

测试、性能优化与部署指南

5. 关键操作流程总结

为了更清晰地展示各个部分的操作流程,下面将以流程图和列表的形式进行总结。

5.1 测试流程
graph LR
    A[克隆应用仓库] --> B[切换到指定版本]
    B --> C{选择测试方式}
    C -->|Flask测试客户端| D[编写API测试用例]
    C -->|Selenium| E[安装Selenium]
    E --> F[启动应用服务器]
    F --> G[启动浏览器并执行测试]
    D --> H[执行测试并检查结果]
    G --> H

具体步骤如下:
1. 克隆应用仓库: git clone <repository_url>
2. 切换到指定版本: git checkout <version> ,如 git checkout 15b
3. 若使用Flask测试客户端:
- 编写API测试用例,参考 tests/test_api.py 中的示例代码。
- 执行测试命令,检查测试结果。
4. 若使用Selenium:
- 安装Selenium: pip install selenium
- 启动应用服务器:在后台线程中使用开发服务器启动应用。
- 启动浏览器并执行测试,参考 tests/test_selenium.py 中的示例代码。

5.2 性能优化流程
graph LR
    A[开启查询统计记录] --> B[运行应用并处理请求]
    B --> C[记录查询信息]
    C --> D{查询是否缓慢}
    D -->|是| E[记录慢查询到日志]
    D -->|否| F[继续处理请求]
    G[启动应用并开启分析器] --> H[执行请求并记录函数信息]
    H --> I[分析函数性能]

具体步骤如下:
1. 记录缓慢的数据库性能:
- 配置 config.py ,开启查询统计记录:

class Config:
    SQLALCHEMY_RECORD_QUERIES = True
    FLASKY_DB_QUERY_TIMEOUT = 0.5
- 运行应用,处理请求时记录查询信息。
- 检查查询是否缓慢,若缓慢则记录到日志。
  1. 源代码分析:
    • 启动应用并开启分析器: python manage.py profile
    • 执行请求,记录函数信息。
    • 分析函数性能,找出慢函数。
5.3 部署流程
graph LR
    A[克隆应用仓库] --> B[切换到指定版本]
    B --> C[执行部署命令]
    C --> D[迁移数据库]
    D --> E[创建用户角色]
    E --> F[添加用户自关注]

具体步骤如下:
1. 克隆应用仓库: git clone <repository_url>
2. 切换到指定版本: git checkout <version> ,如 git checkout 16a
3. 执行部署命令: python manage.py deploy
4. 迁移数据库到最新版本。
5. 创建用户角色。
6. 为所有用户添加自关注。

6. 注意事项与最佳实践

在进行测试、性能优化和部署的过程中,有一些注意事项和最佳实践需要遵循。

6.1 测试方面
  • 对于简单的数据库模型和应用逻辑,应编写简单、聚焦的测试用例,确保核心逻辑的正确性。
  • 端到端测试虽然必要,但由于编写复杂度较高,应仅用于无法孤立测试的功能。
  • 应用代码应进行合理组织,将业务逻辑尽可能推到数据库模型或其他辅助类中,以便于测试。
6.2 性能优化方面
  • 数据库查询优化可从简单的添加索引开始,逐步尝试更复杂的方法,如添加缓存。
  • 源代码分析应在开发环境中进行,避免在生产环境中使用可能影响性能的分析器。
  • 配置日志记录时,根据实际需求选择合适的日志级别,如将慢查询日志级别设置为“error”可通过邮件接收通知。
6.3 部署方面
  • 每次部署前,确保所有依赖项已正确安装,可使用 pip install -r requirements/dev.txt 进行安装。
  • 部署命令应包含所有必要的任务,如数据库迁移、角色创建等,避免手动操作带来的错误。

通过遵循这些注意事项和最佳实践,可以提高应用的质量、性能和可靠性,为用户提供更好的体验。

总结

本文围绕应用的测试、性能优化和部署展开,详细介绍了使用Flask测试客户端和Selenium进行测试的方法,记录缓慢数据库查询和进行源代码分析的性能优化策略,以及部署应用的具体步骤。通过流程图、列表和代码示例,清晰地展示了各个环节的操作流程。同时,还给出了相关的注意事项和最佳实践,希望能帮助开发者更好地开发和维护高质量的应用。在实际应用中,应根据具体情况选择合适的方法和策略,不断优化应用,提升用户体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值