赞
踩
目录
之前的用例中,数据直接写在代码文件里,不利于修改和构造数据,这里我们使用Excel
保存测试数据,实现代码和数据的分离,新建Excel
文件test_user_data.xlsx
包含两个工作簿TestUserLogin
和TestUserReg
,并复制到项目根目录下。
“更新: excel表格中,增加一个headers列,内容为
”json
格式, 如下
Python
我们使用三方库xlrd
来读取Excel,使用pip install xlrd
安装xlrd
。
- import xlrd
-
- wb = xlrd.open_workbook("test_user_data.xlsx") # 打开excel
- sh = wb.sheet_by_name("TestUserLogin") # 按工作簿名定位工作表
- print(sh.nrows) # 有效数据行数
- print(sh.ncols) # 有效数据列数
- print(sh.cell(0, 0).value) # 输出第一行第一列的值`case_name`
- print(sh.row_values(0)) # 输出第1行的所有值(列表格式)
-
- # 将数据和标题组装成字典,使数据更清晰
- print(dict(zip(sh.row_values(0), sh.row_values(1))))
-
- # 遍历excel,打印所有的数据
- for i in range(sh.nrows):
- print(sh.row_values(i))
输出结果
- 3
- 5
- case_name
- ['case_name', 'url', 'method', 'data', 'expect_res']
- {'case_name': 'test_user_login_normal', 'url': 'http://115.28.108.130:5000/api/user/login/', 'method': 'POST', 'data': '{"name": "张三","password":"123456"}', 'expect_res': '<h1>登录成功</h1>'}
- ['case_name', 'url', 'method', 'data', 'expect_res']
- ['test_user_login_normal', 'http://115.28.108.130:5000/api/user/login/', 'POST', '{"name": "张三","password":"123456"}', '<h1>登录成功</h1>']
- ['test_user_login_password_wrong', 'http://115.28.108.130:5000/api/user/login/', 'POST', '{"name": "张三","password":"1234567"}', '<h1>失败,用户不存在</h1>']
- 现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
- 如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
- 可以加入我们一起交流。而且还有很多在自动化,
- 性能,安全,测试开发等等方面有一定建树的技术大牛
- 分享他们的经验,还会分享很多直播讲座和技术沙龙
- 可以免费学习!划重点!开源的!!!
- qq群号:110685036
1.新建read_excel.py
我们的目的是获取某条用例的数据,需要3个参数,excel
数据文件名data_file
,工作簿名sheet
,用例名case_name
。如果我们只封装一个函数,每次调用(每条用例)都要打开一次excel
并遍历一次,这样效率比较低,我们可以拆分成两个函数,一个函数excel_to_list(data_file, sheet)
,一次获取一个工作表的所有数据,另一个函数get_test_data(data_list, case_name)
从所有数据中去查找到该条用例的数据。
- import xlrd
-
- def excel_to_list(data_file, sheet):
- data_list = [] # 新建个空列表,来乘装所有的数据
- wb = xlrd.open_workbook(data_file) # 打开excel
- sh = wb.sheet_by_name(sheet) # 获取工作簿
- header = sh.row_values(0) # 获取标题行数据
- for i in range(1, sh.nrows): # 跳过标题行,从第二行开始取数据
- d = dict(zip(header, sh.row_values(i))) # 将标题和每行数据组装成字典
- data_list.append(d)
- return data_list # 列表嵌套字典格式,每个元素是一个字典
-
- def get_test_data(data_list, case_name):
- for case_data in data_list:
- if case_name == case_data['case_name']: # 如果字典数据中case_name与参数一致
- return case_data
- # 如果查询不到会返回None
-
- if __name__ == '__main__': # 测试一下自己的代码
- data_list = excel_to_list("test_user_data.xlsx", "TestUserLogin") # 读取excel,TestUserLogin工作簿的所有数据
- case_data = get_test_data(data_list, 'test_user_login_normal') # 查找用例'test_user_login_normal'的数据
- print(case_data)
输出结果
{'case_name': 'test_user_login_normal', 'url': 'http://115.28.108.130:5000/api/user/login/', 'method': 'POST', 'data': '{"name": "张三","password":"123456"}', 'expect_res': '<h1>登录成功</h1>'}
2.用例中使用方法(test_user_login.py)
- import unittest
- import requests
- from read_excel import * # 导入read_excel中的方法
- import json # 用来转化excel中的json字符串为字典
-
- class TestUserLogin(unittest.TestCase):
- @classmethod
- def setUpClass(cls): # 整个测试类只执行一次
- cls.data_list = excel_to_list("test_user_data.xlsx", "TestUserLogin") # 读取该测试类所有用例数据
- # cls.data_list 同 self.data_list 都是该类的公共属性
-
- def test_user_login_normal(self):
- case_data = get_test_data(self.data_list, 'test_user_login_normal') # 从数据列表中查找到该用例数据
- if not case_data: # 有可能为None
- print("用例数据不存在")
- url = case_data.get('url') # 从字典中取数据,excel中的标题也必须是小写url
- data = case_data.get('data') # 注意字符串格式,需要用json.loads()转化为字典格式
- expect_res = case_data.get('expect_res') # 期望数据
-
- res = requests.post(url=url, data=json.loads(data)) # 表单请求,数据转为字典格式
- self.assertEqual(res.text, expect_res) # 改为assertEqual断言
-
- if __name__ == '__main__': # 非必要,用于测试我们的代码
- unittest.main(verbosity=2)
3.用例中使用方法(test_user_reg.py)
- import unittest
- import requests
- from db import *
- from read_excel import *
- import json
-
- class TestUserReg(unittest.TestCase):
-
- @classmethod
- def setUpClass(cls):
- cls.data_list = excel_to_list("test_user_data.xlsx", "TestUserReg") # 读取TestUserReg工作簿的所有数据
-
- def test_user_reg_normal(self):
- case_data = get_test_data(self.data_list, 'test_user_reg_normal')
- if not case_data:
- print("用例数据不存在")
- url = case_data.get('url')
- data = json.loads(case_data.get('data')) # 转为字典,需要取里面的name进行数据库检查
- expect_res = json.loads(case_data.get('expect_res')) # 转为字典,断言时直接断言两个字典是否相等
- name = data.get("name") # 范冰冰
-
- # 环境检查
- if check_user(name):
- del_user(name)
- # 发送请求
- res = requests.post(url=url, json=data) # 用data=data 传字符串也可以
- # 响应断言(整体断言)
- self.assertDictEqual(res.json(), expect_res)
- # 数据库断言
- self.assertTrue(check_user(name))
- # 环境清理(由于注册接口向数据库写入了用户信息)
- del_user(name)
-
- if __name__ == '__main__': # 非必要,用于测试我们的代码
- unittest.main(verbosity=2)
1.新建config.py
文件
- import logging
-
- logging.basicConfig(level=logging.DEBUG, # log level
- format='[%(asctime)s] %(levelname)s [%(funcName)s: %(filename)s, %(lineno)d] %(message)s', # log格式
- datefmt='%Y-%m-%d %H:%M:%S', # 日期格式
- filename='log.txt', # 日志输出文件
- filemode='a') # 追加模式
-
- if __name__ == '__main__':
- logging.info("hello")
运行后在当前目录下生成log.txt
,内容如下
[2018-09-11 18:08:17] INFO [<module>: config.py, 38] hello
2.日志别级(Log Level
)
CRITICAL: 用于输出严重错误信息
ERROR: 用于输出错误信息
WARNING: 用于输出警示信息
INFO: 用于输出一些提升信息
DEBUG: 用于输出一些调试信息
3.日志优先级
CRITICAL
> ERROR
> WARNING
> INFO
> DEBUG
指定level = logging.DEBUG
所有等级大于等于DEBUG
的信息都会输出;若指定level = logging.ERROR WARNING,INFO,DEBUG
小于设置级别的信息不会输出。
4.日志格式说明
%(levelno)s: 打印日志级别的数值
%(levelname)s: 打印日志级别名称
%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s: 打印当前执行程序名
%(funcName)s: 打印日志的当前函数
%(lineno)d: 打印日志的当前行号
%(asctime)s: 打印日志的时间
%(thread)d: 打印线程ID
%(threadName)s: 打印线程名称
%(process)d: 打印进程ID
%(message)s: 打印日志信息
5.日志在项目中的使用
db.py
- import pymysql
- from config import *
-
- # 封装数据库查询操作
- def query_db(sql):
- conn = get_db_conn()
- cur = conn.cursor()
- logging.debug(sql) # 输出执行的sql
- cur.execute(sql)
- conn.commit()
- result = cur.fetchall()
- logging.debug(result) # 输出查询结果
- cur.close()
- conn.close()
- return result
-
- # 封装更改数据库操作
- def change_db(sql):
- conn = get_db_conn()
- cur = conn.cursor()
- logging.debug(sql) # 输出执行的sql
- try:
- cur.execute(sql)
- conn.commit()
- except Exception as e:
- conn.rollback()
- logging.error(str(e)) # 输出错误信息
- finally:
- cur.close()
- conn.close()
用例
- import unittest
- import requests
- from read_excel import * # 导入read_excel中的方法
- import json # 用来转化excel中的json字符串为字典
- from config import *
-
- class TestUserLogin(unittest.TestCase):
- @classmethod
- def setUpClass(cls): # 整个测试类只执行一次
- cls.data_list = excel_to_list("test_user_data.xlsx", "TestUserLogin") # 读取该测试类所有用例数据
- # cls.data_list 同 self.data_list 都是该类的公共属性
-
- def test_user_login_normal(self):
- case_data = get_test_data(self.data_list, 'test_user_login_normal') # 从数据列表中查找到该用例数据
- if not case_data: # 有可能为None
- logging.error("用例数据不存在")
- url = case_data.get('url') # excel中的标题也必须是小写url
- data = case_data.get('data') # 注意字符串格式,需要用json.loads()转化为字典格式
- expect_res = case_data.get('expect_res') # 期望数据
-
- res = requests.post(url=url, data=json.loads(data)) # 表单请求,数据转为字典格式
- logging.info("测试用例:{}".format('test_user_login_normal'))
- logging.info("url:{}".format(url))
- logging.info("请求参数:{}".format(data))
- logging.info("期望结果:{}".format(expect_res))
- logging.info("实际结果:{}".format(res.text)
- self.assertEqual(res.text, expect_res) # 断言
-
- if __name__ == '__main__':
- unittest.main(verbosity=2)
输出结果
- [2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 8] 测试用例:test_user_login_normal
- [2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 9] url:http://115.28.108.130:5000/api/user/login/
- [2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 10] 请求参数:{"name": "张三","password":"123456"}
- [2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 11] 期望结果:<h1>登录成功</h1>
- [2018-09-13 10:34:49] INFO [log_case_info: case_log.py, 12] 实际结果:<h1>登录成功</h1>
因为每个用例都要输出很多log
信息,我们封装一个case_log
的函数
- from config import *
- import json
-
- def log_case_info(case_name, url, data, expect_res, res_text):
- if isinstance(data,dict):
- data = json.dumps(data, ensure_ascii=False) # 如果data是字典格式,转化为字符串
- logging.info("测试用例:{}".format(case_name))
- logging.info("url:{}".format(url))
- logging.info("请求参数:{}".format(data))
- logging.info("期望结果:{}".format(expect_res))
- logging.info("实际结果:{}".format(res_text)
简化后的用例log
输出
- import unittest
- import requests
- from read_excel import *
- import json
- from config import *
- from case_log import log_case_info # 导入方法
-
- class TestUserLogin(unittest.TestCase):
- @classmethod
- def setUpClass(cls):
- cls.data_list = excel_to_list("test_user_data.xlsx", "TestUserLogin")
-
- def test_user_login_normal(self):
- case_data = get_test_data(self.data_list, 'test_user_login_normal')
- if not case_data:
- logging.error("用例数据不存在")
- url = case_data.get('url')
- data = case_data.get('data')
- expect_res = case_data.get('expect_res')
-
- res = requests.post(url=url, data=json.loads(data))
- log_case_info('test_user_login_normal', url, data, expect_res, res_text) # 输出用例log信息
- self.assertEqual(res.text, expect_res)
-
- if __name__ == '__main__':
- unittest.main(verbosity=2)
在生成报告后我们希望框架能自动把报告发送到我们的邮箱中。和outlook
,foxmail
等邮件客户端一样,Python
中发送邮件需要通过Email
的smtp
服务发送。
1.首先需要确认用来发送邮件的邮箱是否启用了smtp
服务;
2.编写邮件内容(Email
邮件需要专门的MIME
格式);
3.组装Email
头(发件人,收件人,主题);
4.连接smtp
服务器并发送邮件;
- import smtplib # 用于建立smtp连接
- from email.mime.text import MIMEText # 邮件需要专门的MIME格式
-
- # 1. 编写邮件内容(Email邮件需要专门的MIME格式)
- msg = MIMEText('this is a test email', 'plain', 'utf-8') # plain指普通文本格式邮件内容
-
- # 2. 组装Email头(发件人,收件人,主题)
- msg['From'] = 'test_results@sina.com' # 发件人
- msg['To'] = '2375247815@qq.com' # 收件人
- msg['Subject'] = 'Api Test Report' # 邮件主题
-
- # 3. 连接smtp服务器并发送邮件
- smtp = smtplib.SMTP_SSL('smtp.sina.com') # smtp服务器地址 使用SSL模式
- smtp.login('自己的邮箱地址', '自己的邮箱密码') # 用户名和密码
- smtp.sendmail("接收邮件地址1", "接收邮件地址2", msg.as_string())
- smtp.quit()
5.中文邮件主题、HTML邮件内容,及附件
- import smtplib
- from email.mime.text import MIMEText
- from email.mime.multipart import MIMEMultipart # 混合MIME格式,支持上传附件
- from email.header import Header # 用于使用中文邮件主题
-
- # 1. 编写邮件内容
- with open('report.html', encoding='utf-8') as f: # 打开html报告
- email_body = f.read() # 读取报告内容
-
- msg = MIMEMultipart() # 混合MIME格式
- msg.attach(MIMEText(email_body, 'html', 'utf-8')) # 添加html格式邮件正文(会丢失css格式)
-
- # 2. 组装Email头(发件人,收件人,主题)
- msg['From'] = 'test_results@sina.com' # 发件人
- msg['To'] = '2375247815@qq.com' # 收件人
- msg['Subject'] = Header('接口测试报告', 'utf-8') # 中文邮件主题,指定utf-8编码
-
- # 3. 构造附件1,传送当前目录下的 test.txt 文件
- att1 = MIMEText(open('report.html', 'rb').read(), 'base64', 'utf-8') # 二进制格式打开
- att1["Content-Type"] = 'application/octet-stream'
- att1["Content-Disposition"] = 'attachment; filename="report.html"' # filename为邮件中附件显示的名字
- msg.attach(att1)
-
- # 4. 连接smtp服务器并发送邮件
- smtp = smtplib.SMTP_SSL('smtp.sina.com') # smtp服务器地址 使用SSL模式
- smtp.login('test_results@sina.com', 'hanzhichao123') # 用户名和密码
- smtp.sendmail("test_results@sina.com", "2375247815@qq.com", msg.as_string())
- smtp.sendmail("test_results@sina.com", "superhin@126.com", msg.as_string()) # 发送给另一个邮箱
- smtp.quit()
6.封装发送邮件方法
- import smtplib
- from email.mime.text import MIMEText
- from email.mime.multipart import MIMEMultipart # 混合MIME格式,支持上传附件
- from email.header import Header # 用于使用中文邮件主题
- from config import *
-
-
- def send_email(report_file):
- msg = MIMEMultipart() # 混合MIME格式
- msg.attach(MIMEText(open(report_file, encoding='utf-8').read(), 'html', 'utf-8')) # 添加html格式邮件正文(会丢失css格式)
-
- msg['From'] = 'test_results@sina.com' # 发件人
- msg['To'] = '2375247815@qq.com' # 收件人
- msg['Subject'] = Header('接口测试报告', 'utf-8') # 中文邮件主题,指定utf-8编码
-
- att1 = MIMEText(open(report_file, 'rb').read(), 'base64', 'utf-8') # 二进制格式打开
- att1["Content-Type"] = 'application/octet-stream'
- att1["Content-Disposition"] = 'attachment; filename="report.html"' # filename为邮件中附件显示的名字
- msg.attach(att1)
-
- try:
- smtp = smtplib.SMTP_SSL('smtp.sina.com') # smtp服务器地址 使用SSL模式
- smtp.login('test_results@sina.com', 'hanzhichao123') # 用户名和密码
- smtp.sendmail("test_results@sina.com", "2375247815@qq.com", msg.as_string())
- smtp.sendmail("test_results@sina.com", "superhin@126.com", msg.as_string()) # 发送给另一个邮箱
- logging.info("邮件发送完成!")
- except Exception as e:
- logging.error(str(e))
- finally:
- smtp.quit()
7.run_all.py
中结束后发送邮件
- import unittest
- from HTMLTestReportCN import HTMLTestRunner
- from config import *
- from send_email import send_email
-
- logging.info("====================== 测试开始 =======================")
- suite = unittest.defaultTestLoader.discover("./")
-
- with open("report.html", 'wb') as f: # 改为with open 格式
- HTMLTestRunner(stream=f, title="Api Test", description="测试描述", tester="卡卡").run(suite)
-
- send_email('report.html') # 发送邮件
- logging.info("======================= 测试结束 =======================")
和项目的log
配置一样,数据库服务器地址,邮件服务地址我们一般放到配置文件config.py
中。
- import logging
- import os
-
- # 项目路径
- prj_path = os.path.dirname(os.path.abspath(__file__)) # 当前文件的绝对路径的上一级,__file__指当前文件
-
- data_path = prj_path # 数据目录,暂时在项目目录下
- test_path = prj_path # 用例目录,暂时在项目目录下
-
- log_file = os.path.join(prj_path, 'log.txt') # 也可以每天生成新的日志文件
- report_file = os.path.join(prj_path, 'report.html') # 也可以每次生成新的报告
-
- # log配置
- logging.basicConfig(level=logging.DEBUG, # log level
- format='[%(asctime)s] %(levelname)s [%(funcName)s: %(filename)s, %(lineno)d] %(message)s', # log格式
- datefmt='%Y-%m-%d %H:%M:%S', # 日期格式
- filename=log_file, # 日志输出文件
- filemode='a') # 追加模式
-
-
- # 数据库配置
- db_host = '127.0.0.1' # 自己的服务器地址
- db_port = 3306
- db_user = 'test'
- db_passwd = '123456'
- db = 'api_test'
-
- # 邮件配置
- smtp_server = 'smtp.sina.com'
- smtp_user = 'test_results@sina.com'
- smtp_password = 'hanzhichao123'
-
- sender = smtp_user # 发件人
- receiver = '2375247815@qq.com' # 收件人
- subject = '接口测试报告' # 邮件主题
修改db.py
,send_email.py
,run_all.py
等对配置文件的引用
db.py
- import pymysql
- from config import *
-
- # 获取连接方法
- def get_db_conn():
- conn = pymysql.connect(host=db_host, # 从配置文件中读取
- port=db_port,
- user=db_user,
- passwd=db_passwd, # passwd 不是 password
- db=db,
- charset='utf8') # 如果查询有中文,需要指定测试集编码
send_email.py
- import smtplib
- from email.mime.text import MIMEText
- from email.mime.multipart import MIMEMultipart
- from email.header import Header
- from config import *
-
-
- def send_email(report_file):
- msg = MIMEMultipart()
- msg.attach(MIMEText(open(report_file, encoding='utf-8').read(), 'html', 'utf-8'))
-
- msg['From'] = 'test_results@sina.com'
- msg['To'] = '2375247815@qq.com'
- msg['Subject'] = Header(subject, 'utf-8') # 从配置文件中读取
-
- att1 = MIMEText(open(report_file, 'rb').read(), 'base64', 'utf-8') # 从配置文件中读取
- att1["Content-Type"] = 'application/octet-stream'
- att1["Content-Disposition"] = 'attachment; filename="{}"'.format(report_file) # 参数化一下report_file
- msg.attach(att1)
-
- try:
- smtp = smtplib.SMTP_SSL(smtp_server) # 从配置文件中读取
- smtp.login(smtp_user, smtp_password) # 从配置文件中读取
- smtp.sendmail(sender, receiver, msg.as_string())
- logging.info("邮件发送完成!")
- except Exception as e:
- logging.error(str(e))
- finally:
- smtp.quit()
run_all.py
- import unittest
- from HTMLTestReportCN import HTMLTestRunner
- from config import *
- from send_email import send_email
-
- logging.info("==================== 测试开始 =======================")
- suite = unittest.defaultTestLoader.discover(test_path) # 从配置文件中读取用例路径
-
- with open(report_file, 'wb') as f: # 从配置文件中读取
- HTMLTestRunner(stream=f, title="Api Test", description="测试描述").run(suite)
-
- send_email(report_file) # 从配置文件中读取
- logging.info("==================== 测试结束 =======================")
当前所有文件(配置文件,公共方法,测试用例,数据,报告,log
)都在项目根目录下,随着用例的增加和功能的补充,文件会越来越多,不便于维护和管理,因此我们要建立不同的文件夹,对文件进行分类组织。
1.在项目中新建以下文件夹
config: 存放项目配置文件
data: 存放用例数据文件
lib: 公共方法库
log: 存放日志文件
report: 存放报告文件
test: 存放测试用例
user: 存放user模块用例(模块下要有__init__.py,这样里面的用例才能读取到)
2.代码目录整理
将配置文件config.py
移动到config
目录下
将数据文件test_user_data.xlsx
移动到data
目录下
将公共方法db.py
,send_email.py
,case_log.py
,read_excel.py
,HTMLTestReportCN.py
移动到lib
目录下
将测试用例test_user_login.py
,test_user_reg.py
移动到test/user
目录下,保留run_all.py
在项目根目录下,如图
3.修改配置文件(config/config.py
)
- import logging
- import os
-
- # 项目路径
- prj_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 当前文件的上一级的上一级目录(增加一级)
-
- data_path = os.path.join(prj_path, 'data') # 数据目录
- test_path = os.path.join(prj_path, 'test') # 用例目录
-
- log_file = os.path.join(prj_path, 'log', 'log.txt') # 更改路径到log目录下
- report_file = os.path.join(prj_path, 'report', 'report.html') # 更改路径到report目录下
4.修改对配置文件及公共方法的引用 为避免相对路径导包出错的问题,我们统一把导包搜索路径(sys.path)提升到项目根目录下,如lib/db.py
db.py
- import pymysql
- import sys
- sys.path.append('..') # 提升一级到项目更目录下
- from config.config import * # 从项目根目录下导入
test_user_login.py
- import unittest
- import requests
- import json
- import os # 增加了一个os,需要用来组装路径
- import sys
- sys.path.append("../..") # 提升2级到项目根目录下
- from config.config import * # 从项目路径下导入
- from lib.read_excel import * # 从项目路径下导入
- from lib.case_log import log_case_info # 从项目路径下导入
-
- class TestUserLogin(unittest.TestCase):
- @classmethod
- def setUpClass(cls): # 整个测试类只执行一次
- cls.data_list = excel_to_list(os.path.join(data_path, "test_user_data.xlsx"),"TestUserLogin") # 增加data路径
run_all.py
- import unittest
- from lib.HTMLTestReportCN import HTMLTestRunner # 修改导入路径
- from config.config import * # 修改导入路径
- from lib.send_email import send_email # 修改导入路径
-
- logging.info("================================== 测试开始 ==================================")
- suite = unittest.defaultTestLoader.discover(test_path) # 从配置文件中读取
-
- with open(report_file, 'wb') as f: # 从配置文件中读取
- HTMLTestRunner(stream=f, title="Api Test", description="测试描述").run(suite)
-
- send_email(report_file) # 从配置文件中读取
- logging.info("================================== 测试结束 ==================================")
“如果同一文件夹下的方法相互引用(如
”lib/read_excel.py
假如需要引用lib/db.py
),也需要采用这种从项目路径下导入的方式;run_all.py
直接在项目路径下,不需要提升sys.path
,无需相对导入我们自己的包时,如read_excel.py
,不需要提升
5.运行run_all.py
,根据log
和报告调试代码,直至所有用例全部通过
最后: 下方这份完整的软件测试视频学习教程已经整理上传完成,朋友们如果需要可以自行免费领取【保证100%免费】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。