python性能测试框架:Multi-Mechanize

Multi-Mechanize | 性能测试框架

multi-mechanize-logo

graphs

  • 主页
  • pypi
  • github
  • License: GNU LGPLv3
  • Author: Corey Goldberg - copyright © 2010-2012

使用python进行性能和负载测试

multi-mechanize是一个开源的性能和负载测试框架,它并发的运行python脚本来生成一个远程站点或服务的负载。
multi-mechanize通常用于web性能和扩展性测试,但是也能用于使用python来生成任何远程的可访问的api的负载。
测试输出报告既能存储为HTML,也可以存储为JMeter兼容的XML。


安装

multi-mechanize需要python2.6或2.7

pip install -U multi-mechanize

或者是从pypi下载源代码,解压缩,然后运行:
python setup.py install


使用介绍

创建项目

multimech-newproject命令创建一个新的测试项目:

multimech-newproject my_project

每一个项目包含下面的文件和目录:

  • config.cfg:配置文件,在这里设置测试选项。
  • test_scripts/:放置虚拟用户脚本的目录,在这里放置测试脚本。
  • results/:存储结果的目录,会为每次测试创建一个包含结果报告的时间戳目录。

运行项目

multimech-run命令运行一个测试项目:

multimech-run my_project  

可能会报错:

Traceback (most recent call last):
  File "/usr/bin/multimech-run", line 11, in <module>
    sys.exit(main())
  File "/usr/lib/python2.6/site-packages/multimechanize/utilities/run.py", line 64, in main
    run_test()
  File "/usr/lib/python2.6/site-packages/multimechanize/utilities/run.py", line 131, in run_test
    results.output_results(output_dir, 'results.csv', run_time, rampup, results_ts_interval, user_group_configs, xml_report)
  File "/usr/lib/python2.6/site-packages/multimechanize/results.py", line 65, in output_results
    graph.resp_graph_raw(trans_timer_points, 'All_Transactions_response_times.png', results_dir)
  File "/usr/lib/python2.6/site-packages/multimechanize/graph.py", line 23, in resp_graph_raw
    fig = figure(figsize=(8, 3.3))  # image dimensions
NameError: global name 'figure' is not defined

这是因为没有安装Matplotlib包,无法绘制结果图。
安装Matplotlib包:
yum install -y python-matplotlib


配置

配置文件(config.cfg)

每个项目都包含一个用于定义测试设置的config.cfg文件。
配置文件包含[global]区和[user_group-*]区。

全部配置

这是一个简单的config.cfg文件,它展示了所有可能的选项,定义了两个虚拟用户组:

[global]
run_time = 300
rampup = 300
results_ts_interval = 30
progress_bar = on
console_logging = off
xml_report = off
results_database = sqlite:///my_project/results.db
post_run_script = python my_project/foo.py

[user_group-1]
threads = 30
script = vu_script1.py

[user_group-2]
threads = 30
script = vu_script2.py

下面是global配置区域中可用的设置/选项:

  • run_time:测试的执行时间(单位是秒)[必须存在]
  • rampup:用户提速的时间,也就是在多长时间内创建完配置中配置的线程数(单位是秒)[必须存在]
  • results_ts_interval:用于结果分析的时间序列间隔(单位是秒)[必须存在]
  • progress_bar:测试运行期间打开或关闭控制台进度条[可选选项,默认是on]
  • console_logging:是否向标准输出中打印日志[可选选项,默认是off]
  • xml_report:是否生成XML报告[可选选项,默认是off]
  • results_database:保存结果的数据库连接串[可选选项]
  • post_run_script:测试完成后调用的脚本[可选选项]

下面是每一个[user_group-*]配置区域中可用的设置/选项:

  • threads:每一个虚拟用户的线程数量
  • script:要运行的虚拟用户测试脚本

脚本教程

虚拟用户脚本编写

脚本使用python编写

基础

每一个脚本必须实现一个Transaction()类,这个类必须实现run()方法。
因此一个基本的测试脚本包含:

class Transaction(object):
    def run(self):
        # do something here
        return

在测试运行期间,Transaction()类被实例化一次,它的run()方法会在一个循环里被重复的调用。

class Transaction(object):
    def __init__(self):
        # do per-user user setup here
        # this gets called once on user creation
        return

    def run(self):
        # do user actions here
        # this gets called repeatedly
        return

例子

下面是一个完整的使用mechanize生成HTTP GET请求的用户脚本:

import mechanize

class Transaction(object):
    def run(self):
        br = mechanize.Browser()
        br.set_handle_robots(False)
        resp = br.open('http://www.example.com/')
        resp.read()

下面的脚本增加了响应的断言:

import mechanize

class Transaction(object):
    def run(self):
        br = mechanize.Browser()
        br.set_handle_robots(False)

        resp = br.open('http://www.example.com/')
        resp.read()

        assert (resp.code == 200), 'Bad Response: HTTP %s' % resp.code
        assert ('Example Web Page' in resp.get_data())

下面的脚本使用了一个自定义的timer:

import mechanize
import time

class Transaction(object):
    def run(self):
        br = mechanize.Browser()
        br.set_handle_robots(False)

        start_timer = time.time()
        resp = br.open('http://www.example.com/')
        resp.read()
        latency = time.time() - start_timer

        self.custom_timers['Example_Homepage'] = latency

高级的例子

通过表单填写,提交的方式进行Wikipedia搜索,例子中包含:自定义timer,断言,自定义请求头,思考时间。

import mechanize
import time

class Transaction(object):
    def __init__(self):
        pass

    def run(self):
        # create a Browser instance
        br = mechanize.Browser()
        # don't bother with robots.txt
        br.set_handle_robots(False)
        # add a custom header so wikipedia allows our requests
        br.addheaders = [('User-agent', 'Mozilla/5.0 Compatible')]

        # start the timer
        start_timer = time.time()
        # submit the request
        resp = br.open('http://www.wikipedia.org/')
        resp.read()
        # stop the timer
        latency = time.time() - start_timer

        # store the custom timer
        self.custom_timers['Load_Front_Page'] = latency

        # verify responses are valid
        assert (resp.code == 200), 'Bad Response: HTTP %s' % resp.code
        assert ('Wikipedia, the free encyclopedia' in resp.get_data())

        # think-time
        time.sleep(2)

        # select first (zero-based) form on page
        br.select_form(nr=0)
        # set form field
        br.form['search'] = 'foo'

        # start the timer
        start_timer = time.time()
        # submit the form
        resp = br.submit()
        resp.read()
        # stop the timer
        latency = time.time() - start_timer

        # store the custom timer
        self.custom_timers['Search'] = latency

        # verify responses are valid
        assert (resp.code == 200), 'Bad Response: HTTP %s' % resp.code
        assert ('foobar' in resp.get_data()), 'Text Assertion Failed'

        # think-time
        time.sleep(2)

这个例子使用httplib生成HTTP GET请求,并且实现了详尽的时间:发送请求用的时间,接收响应用的时间,内容传输完成用的时间。

import httplib
import time

class Transaction(object):
    def run(self):
        conn = httplib.HTTPConnection('www.example.com')
        start = time.time()
        conn.request('GET', '/')
        request_time = time.time()
        resp = conn.getresponse()
        response_time = time.time()
        conn.close()
        transfer_time = time.time()

        self.custom_timers['request sent'] = request_time - start
        self.custom_timers['response received'] = response_time - start
        self.custom_timers['content transferred'] = transfer_time - start

        assert (resp.status == 200), 'Bad Response: HTTP %s' % resp.status


if __name__ == '__main__':
    trans = Transaction()
    trans.run()

    for timer in ('request sent', 'response received', 'content transferred'):
        print '%s: %.5f secs' % (timer, trans.custom_timers[timer])

报告和绘图

multi-mechanize使用matplotlib来生成报告。


使用数据库保存测试结果数据

当测试运行完成后,测试数据和结果能够被存储到数据库。为了开启数据库支持,必须向配置文件config.cfg中增加results_database选项,results_database定义了数据连接串。

例子

results_database: sqlite:///results.db

依赖

数据库存储需要sqlalchemy

示例连接串

SQLite:    sqlite:///dbname
MySQL:    mysql://user:password@localhost/dbname
PostgreSQL:    postgresql://user:password@host:port/dbname
MS SQL Server:    mssql://mydsn
  • 数据库在第一次运行时会自动创建,无需运行DDL语句。

结果数据库表

数据库表

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: