前言:

首先我们先明确一个概念,什么叫接口、什么叫接口测试?

接口的全称叫【Application Programming Interface 又叫API】,是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。

(本章不会讲POSTMan、Jmeter等工具,直接入直路,工具毕竟是有依赖性)

一. 前期准备

1、准备工具:

本文版权归作者和博客园共有,原创作者:http://www.cnblogs.com/BenLam,未经作者同意必须在文章页面给出原文连接。

1、Python3.7 + notepad (Windows自带的文本文档工具)|(Linux配合vi编辑也可以)

2、Request库:下载地址 或通过Pip命令安装lib库:

pip install requests

  3、urllib、Unittest

  4、HTMLTestRunner获取方法【打开下面折叠代码,另存为HTMLTestRunner_Py3.Py文件】

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @file : HTMLTestRunner_Py3.x.py
# @Date : 2018-06-02 14:14:46
# @Author : Destroyers (https://github.com/lguobin)
# @Link : https://github.com/lguobin
# @Version : $1.0$ """
A TestRunner for use with the Python unit testing framework. It
generates a HTML report to show the result at a glance. The simplest way to use this is to invoke its main method. E.g. import unittest
import HTMLTestRunner ... define your tests ... if __name__ == '__main__':
HTMLTestRunner.main() For more customization options, instantiates a HTMLTestRunner object.
HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. # output to a file
fp = file('my_report.html', 'wb')
runner = HTMLTestRunner.HTMLTestRunner(
stream=fp,
title='My unit test',
description='This demonstrates the report output by HTMLTestRunner.'
) # Use an external stylesheet.
# See the Template_mixin class for more customizable options
runner.STYLESHEET_TMPL = '<link rel="stylesheet" href="my_stylesheet.css" type="text/css">' # run the test
runner.run(my_test_suite) ------------------------------------------------------------------------
Copyright (c) 2004-2007, Wai Yip Tung
All rights reserved. Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met: * Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name Wai Yip Tung nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
""" # URL: http://tungwaiyip.info/software/HTMLTestRunner.html __author__ = "Wai Yip Tung"
__version__ = "0.8.2.1" # lguobin change Python 3.x """
Change History Version 0.8.2
* Show output inline instead of popup window (Viorel Lupu). Version in 0.8.1
* Validated XHTML (Wolfgang Borgert).
* Added description of test classes and test cases. Version in 0.8.0
* Define Template_mixin class for customization.
* Workaround a IE 6 bug that it does not treat <script> block as CDATA. Version in 0.7.1
* Back port to Python 2.3 (Frank Horowitz).
* Fix missing scroll bars in detail log (Podi).
""" # TODO: color stderr
# TODO: simplify javascript using ,ore than 1 class in the class attribute? import datetime
import io
import sys
import time
import unittest
from xml.sax import saxutils # ------------------------------------------------------------------------
# The redirectors below are used to capture output during testing. Output
# sent to sys.stdout and sys.stderr are automatically captured. However
# in some cases sys.stdout is already cached before HTMLTestRunner is
# invoked (e.g. calling logging.basicConfig). In order to capture those
# output, use the redirectors for the cached stream.
#
# e.g.
# >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
# >>> class OutputRedirector(object):
""" Wrapper to redirect stdout or stderr """
def __init__(self, fp):
self.fp = fp def write(self, s):
self.fp.write(s) def writelines(self, lines):
self.fp.writelines(lines) def flush(self):
self.fp.flush() stdout_redirector = OutputRedirector(sys.stdout)
stderr_redirector = OutputRedirector(sys.stderr) # ----------------------------------------------------------------------
# Template class Template_mixin(object):
"""
Define a HTML template for report customerization and generation. Overall structure of an HTML report HTML
+------------------------+
|<html> |
| <head> |
| |
| STYLESHEET |
| +----------------+ |
| | | |
| +----------------+ |
| |
| </head> |
| |
| <body> |
| |
| HEADING |
| +----------------+ |
| | | |
| +----------------+ |
| |
| REPORT |
| +----------------+ |
| | | |
| +----------------+ |
| |
| ENDING |
| +----------------+ |
| | | |
| +----------------+ |
| |
| </body> |
|</html> |
+------------------------+
""" STATUS = {
0: 'pass',
1: 'fail',
2: 'error',
} DEFAULT_TITLE = 'Unit Test Report'
DEFAULT_DESCRIPTION = '' # ------------------------------------------------------------------------
# HTML Template HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>%(title)s</title>
<meta name="generator" content="%(generator)s"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
%(stylesheet)s
</head>
<body>
<script language="javascript" type="text/javascript"><!--
output_list = Array(); /* level - 0:Summary; 1:Failed; 2:All */
function showCase(level) {
trs = document.getElementsByTagName("tr");
for (var i = 0; i < trs.length; i++) {
tr = trs[i];
id = tr.id;
if (id.substr(0,2) == 'ft') {
if (level < 1) {
tr.className = 'hiddenRow';
}
else {
tr.className = '';
}
}
if (id.substr(0,2) == 'pt') {
if (level > 1) {
tr.className = '';
}
else {
tr.className = 'hiddenRow';
}
}
}
} function showClassDetail(cid, count) {
var id_list = Array(count);
var toHide = 1;
for (var i = 0; i < count; i++) {
tid0 = 't' + cid.substr(1) + '.' + (i+1);
tid = 'f' + tid0;
tr = document.getElementById(tid);
if (!tr) {
tid = 'p' + tid0;
tr = document.getElementById(tid);
}
id_list[i] = tid;
if (tr.className) {
toHide = 0;
}
}
for (var i = 0; i < count; i++) {
tid = id_list[i];
if (toHide) {
document.getElementById('div_'+tid).style.display = 'none'
document.getElementById(tid).className = 'hiddenRow';
}
else {
document.getElementById(tid).className = '';
}
}
} function showTestDetail(div_id){
var details_div = document.getElementById(div_id)
var displayState = details_div.style.display
// alert(displayState)
if (displayState != 'block' ) {
displayState = 'block'
details_div.style.display = 'block'
}
else {
details_div.style.display = 'none'
}
} function html_escape(s) {
s = s.replace(/&/g,'&');
s = s.replace(/</g,'<');
s = s.replace(/>/g,'>');
return s;
} /* obsoleted by detail in <div>
function showOutput(id, name) {
var w = window.open("", //url
name,
"resizable,scrollbars,status,width=800,height=450");
d = w.document;
d.write("<pre>");
d.write(html_escape(output_list[id]));
d.write("\n");
d.write("<a href='javascript:window.close()'>close</a>\n");
d.write("</pre>\n");
d.close();
}
*/
--></script> %(heading)s
%(report)s
%(ending)s </body>
</html>
"""
# variables: (title, generator, stylesheet, heading, report, ending) # ------------------------------------------------------------------------
# Stylesheet
#
# alternatively use a <link> for external style sheet, e.g.
# <link rel="stylesheet" href="$url" type="text/css"> STYLESHEET_TMPL = """
<style type="text/css" media="screen">
body { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
table { font-size: 100%; }
pre { } /* -- heading ---------------------------------------------------------------------- */
h1 {
font-size: 16pt;
color: gray;
}
.heading {
margin-top: 0ex;
margin-bottom: 1ex;
} .heading .attribute {
margin-top: 1ex;
margin-bottom: 0;
} .heading .description {
margin-top: 4ex;
margin-bottom: 6ex;
} /* -- css div popup ------------------------------------------------------------------------ */
a.popup_link {
} a.popup_link:hover {
color: red;
} .popup_window {
display: none;
position: relative;
left: 0px;
top: 0px;
/*border: solid #627173 1px; */
padding: 10px;
background-color: #E6E6D6;
font-family: "Lucida Console", "Courier New", Courier, monospace;
text-align: left;
font-size: 8pt;
width: 500px;
} }
/* -- report ------------------------------------------------------------------------ */
#show_detail_line {
margin-top: 3ex;
margin-bottom: 1ex;
}
#result_table {
width: 80%;
border-collapse: collapse;
border: 1px solid #777;
}
#header_row {
font-weight: bold;
color: white;
background-color: #777;
}
#result_table td {
border: 1px solid #777;
padding: 2px;
}
#total_row { font-weight: bold; }
.passClass { background-color: #6c6; }
.failClass { background-color: #c60; }
.errorClass { background-color: #c00; }
.passCase { color: #6c6; }
.failCase { color: #c60; font-weight: bold; }
.errorCase { color: #c00; font-weight: bold; }
.hiddenRow { display: none; }
.testcase { margin-left: 2em; } /* -- ending ---------------------------------------------------------------------- */
#ending {
} </style>
""" # ------------------------------------------------------------------------
# Heading
# HEADING_TMPL = """<div class='heading'>
<h1>%(title)s</h1>
%(parameters)s
<p class='description'>%(description)s</p>
</div> """ # variables: (title, parameters, description) HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
""" # variables: (name, value) # ------------------------------------------------------------------------
# Report
# REPORT_TMPL = """
<p id='show_detail_line'>Show
<a href='javascript:showCase(0)'>Summary</a>
<a href='javascript:showCase(1)'>Failed</a>
<a href='javascript:showCase(2)'>All</a>
</p>
<table id='result_table'>
<colgroup>
<col align='left' />
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' />
<col align='right' />
</colgroup>
<tr id='header_row'>
<td>Test Group/Test case</td>
<td>Count</td>
<td>Pass</td>
<td>Fail</td>
<td>Error</td>
<td>View</td>
</tr>
%(test_list)s
<tr id='total_row'>
<td>Total</td>
<td>%(count)s</td>
<td>%(Pass)s</td>
<td>%(fail)s</td>
<td>%(error)s</td>
<td> </td>
</tr>
</table>
""" # variables: (test_list, count, Pass, fail, error) REPORT_CLASS_TMPL = r"""
<tr class='%(style)s'>
<td>%(desc)s</td>
<td>%(count)s</td>
<td>%(Pass)s</td>
<td>%(fail)s</td>
<td>%(error)s</td>
<td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
</tr>
""" # variables: (style, desc, count, Pass, fail, error, cid) REPORT_TEST_WITH_OUTPUT_TMPL = r"""
<tr id='%(tid)s' class='%(Class)s'>
<td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
<td colspan='5' align='center'> <!--css div popup start-->
<a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
%(status)s</a> <div id='div_%(tid)s' class="popup_window">
<div style='text-align: right; color:red;cursor:pointer'>
<a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
[x]</a>
</div>
<pre>
%(script)s
</pre>
</div>
<!--css div popup end--> </td>
</tr>
""" # variables: (tid, Class, style, desc, status) REPORT_TEST_NO_OUTPUT_TMPL = r"""
<tr id='%(tid)s' class='%(Class)s'>
<td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
<td colspan='5' align='center'>%(status)s</td>
</tr>
""" # variables: (tid, Class, style, desc, status) REPORT_TEST_OUTPUT_TMPL = r"""
%(id)s: %(output)s
""" # variables: (id, output) # ------------------------------------------------------------------------
# ENDING
# ENDING_TMPL = """<div id='ending'> </div>""" # -------------------- The end of the Template class ------------------- TestResult = unittest.TestResult class _TestResult(TestResult):
# note: _TestResult is a pure representation of results.
# It lacks the output and reporting ability compares to unittest._TextTestResult. def __init__(self, verbosity=1):
TestResult.__init__(self)
self.stdout0 = None
self.stderr0 = None
self.success_count = 0
self.failure_count = 0
self.error_count = 0
self.verbosity = verbosity # result is a list of result in 4 tuple
# (
# result code (0: success; 1: fail; 2: error),
# TestCase object,
# Test output (byte string),
# stack trace,
# )
self.result = [] def startTest(self, test):
TestResult.startTest(self, test)
# just one buffer for both stdout and stderr
self.outputBuffer = io.StringIO()
stdout_redirector.fp = self.outputBuffer
stderr_redirector.fp = self.outputBuffer
self.stdout0 = sys.stdout
self.stderr0 = sys.stderr
sys.stdout = stdout_redirector
sys.stderr = stderr_redirector def complete_output(self):
"""
Disconnect output redirection and return buffer.
Safe to call multiple times.
"""
if self.stdout0:
sys.stdout = self.stdout0
sys.stderr = self.stderr0
self.stdout0 = None
self.stderr0 = None
return self.outputBuffer.getvalue() def stopTest(self, test):
# Usually one of addSuccess, addError or addFailure would have been called.
# But there are some path in unittest that would bypass this.
# We must disconnect stdout in stopTest(), which is guaranteed to be called.
self.complete_output() def addSuccess(self, test):
self.success_count += 1
TestResult.addSuccess(self, test)
output = self.complete_output()
self.result.append((0, test, output, ''))
if self.verbosity > 1:
sys.stderr.write('ok ')
sys.stderr.write(str(test))
sys.stderr.write('\n')
else:
sys.stderr.write('.') def addError(self, test, err):
self.error_count += 1
TestResult.addError(self, test, err)
_, _exc_str = self.errors[-1]
output = self.complete_output()
self.result.append((2, test, output, _exc_str))
if self.verbosity > 1:
sys.stderr.write('E ')
sys.stderr.write(str(test))
sys.stderr.write('\n')
else:
sys.stderr.write('E') def addFailure(self, test, err):
self.failure_count += 1
TestResult.addFailure(self, test, err)
_, _exc_str = self.failures[-1]
output = self.complete_output()
self.result.append((1, test, output, _exc_str))
if self.verbosity > 1:
sys.stderr.write('F ')
sys.stderr.write(str(test))
sys.stderr.write('\n')
else:
sys.stderr.write('F') class HTMLTestRunner(Template_mixin):
"""
"""
def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
self.stream = stream
self.verbosity = verbosity
if title is None:
self.title = self.DEFAULT_TITLE
else:
self.title = title
if description is None:
self.description = self.DEFAULT_DESCRIPTION
else:
self.description = description self.startTime = datetime.datetime.now() def run(self, test):
"Run the given test case or test suite."
result = _TestResult(self.verbosity)
test(result)
self.stopTime = datetime.datetime.now()
self.generateReport(test, result)
# print >> sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime))
return result def sortResult(self, result_list):
# unittest does not seems to run in any particular order.
# Here at least we want to group them together by class.
rmap = {}
classes = []
for n,t,o,e in result_list:
cls = t.__class__
if not cls in rmap:
rmap[cls] = []
classes.append(cls)
rmap[cls].append((n,t,o,e))
r = [(cls, rmap[cls]) for cls in classes]
return r def getReportAttributes(self, result):
"""
Return report attributes as a list of (name, value).
Override this to add custom attributes.
"""
startTime = str(self.startTime)[:19]
duration = str(self.stopTime - self.startTime)
status = []
if result.success_count: status.append('Pass %s' % result.success_count)
if result.failure_count: status.append('Failure %s' % result.failure_count)
if result.error_count: status.append('Error %s' % result.error_count )
if status:
status = ' '.join(status)
else:
status = 'none'
return [
('Start Time', startTime),
('Duration', duration),
('Status', status),
] def generateReport(self, test, result):
report_attrs = self.getReportAttributes(result)
generator = 'HTMLTestRunner %s' % __version__
stylesheet = self._generate_stylesheet()
heading = self._generate_heading(report_attrs)
report = self._generate_report(result)
ending = self._generate_ending()
output = self.HTML_TMPL % dict(
title = saxutils.escape(self.title),
generator = generator,
stylesheet = stylesheet,
heading = heading,
report = report,
ending = ending,
)
self.stream.write(output.encode('utf8')) def _generate_stylesheet(self):
return self.STYLESHEET_TMPL def _generate_heading(self, report_attrs):
a_lines = []
for name, value in report_attrs:
line = self.HEADING_ATTRIBUTE_TMPL % dict(
name = saxutils.escape(name),
value = saxutils.escape(value),
)
a_lines.append(line)
heading = self.HEADING_TMPL % dict(
title = saxutils.escape(self.title),
parameters = ''.join(a_lines),
description = saxutils.escape(self.description),
)
return heading def _generate_report(self, result):
rows = []
sortedResult = self.sortResult(result.result)
for cid, (cls, cls_results) in enumerate(sortedResult):
# subtotal for a class
np = nf = ne = 0
for n,t,o,e in cls_results:
if n == 0: np += 1
elif n == 1: nf += 1
else: ne += 1 # format class description
if cls.__module__ == "__main__":
name = cls.__name__
else:
name = "%s.%s" % (cls.__module__, cls.__name__)
doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
desc = doc and '%s: %s' % (name, doc) or name row = self.REPORT_CLASS_TMPL % dict(
style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
desc = desc,
count = np+nf+ne,
Pass = np,
fail = nf,
error = ne,
cid = 'c%s' % (cid+1),
)
rows.append(row) for tid, (n,t,o,e) in enumerate(cls_results):
self._generate_report_test(rows, cid, tid, n, t, o, e) report = self.REPORT_TMPL % dict(
test_list = ''.join(rows),
count = str(result.success_count+result.failure_count+result.error_count),
Pass = str(result.success_count),
fail = str(result.failure_count),
error = str(result.error_count),
)
return report def _generate_report_test(self, rows, cid, tid, n, t, o, e):
# e.g. 'pt1.1', 'ft1.1', etc
has_output = bool(o or e)
tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
name = t.id().split('.')[-1]
doc = t.shortDescription() or ""
desc = doc and ('%s: %s' % (name, doc)) or name
tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL # o and e should be byte string because they are collected from stdout and stderr?
if isinstance(o,str):
# TODO: some problem with 'string_escape': it escape \n and mess up formating
# uo = unicode(o.encode('string_escape'))
# uo = o.decode('latin-1')
uo = e
else:
uo = o
if isinstance(e,str):
# TODO: some problem with 'string_escape': it escape \n and mess up formating
# ue = unicode(e.encode('string_escape'))
# ue = e.decode('latin-1')
ue = e
else:
ue = e script = self.REPORT_TEST_OUTPUT_TMPL % dict(
id = tid,
output = saxutils.escape(str(uo)+ue),
) row = tmpl % dict(
tid = tid,
Class = (n == 0 and 'hiddenRow' or 'none'),
style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
desc = desc,
script = script,
status = self.STATUS[n],
)
rows.append(row)
if not has_output:
return def _generate_ending(self):
return self.ENDING_TMPL ##############################################################################
# Facilities for running tests from the command line
############################################################################## # Note: Reuse unittest.TestProgram to launch test. In the future we may
# build our own launcher to support more specific command line
# parameters like test title, CSS, etc.
class TestProgram(unittest.TestProgram):
"""
A variation of the unittest.TestProgram. Please refer to the base
class for command line parameters.
"""
def runTests(self):
# Pick HTMLTestRunner as the default test runner.
# base class's testRunner parameter is not useful because it means
# we have to instantiate HTMLTestRunner before we know self.verbosity.
if self.testRunner is None:
self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
unittest.TestProgram.runTests(self) main = TestProgram ##############################################################################
# Executing this module from the command line
############################################################################## if __name__ == "__main__":
main(module=None)

HTMLTestRunner Code

2、示例接口说明:

演示接口:豆瓣电影搜索

文档说明:https://developers.douban.com/wiki/?title=movie_v2#search

api请求地址:https://api.douban.com/v2/movie/search?

1、按片名搜索:api请求地址 + q=钢铁侠

2、按类型搜索:api请求地址 + tag=喜剧

说明:在实际工作中接口说明开发会提供交到我们手上的,不用担心。

3、接口踩点:

1、拿到 api 先别着急测试,通过手工点点能否正常使用

2、编写对应的测试用例,例如上边测试按电影的片名和类型搜索:(演示)

testdemo1: https://api.douban.com/v2/movie/search?q=钢铁侠
testdemo2: https://api.douban.com/v2/movie/search?q=喜剧
testdemo3: https://api.douban.com/v2/movie/search?q=恐龙恐怖片
testdemo4: https://api.douban.com/v2/movie/search?q=

二. 接口调用(我会通过使用两个Python库来实现与接口通讯)

1、urllib发起请求:

本文版权归作者和博客园共有,原创作者:http://www.cnblogs.com/BenLam,未经作者同意必须在文章页面给出原文连接。

import urllib.request
params = "TomCruise" # 汤姆·克鲁斯的电影
url = "https://api.douban.com/v2/movie/search?"
html = urllib.request.urlopen(url + params) # 通过urllib发起请求
html = html.read().decode("utf-8")
print(html)

2、Requests发起请求:

import requests
params = "TomCruise" # 汤姆·克鲁斯的电影
url = "https://api.douban.com/v2/movie/search?"
req = requests.get(url) # 通过requests 发起请求
print(req)

打印结果:

{
"count": 20,
"start": 0,
"total": 40,
"subjects": [{
"rating": {
"max": 10,
"average": 7.7,
"stars": "40",
"min": 0
},
"genres": ["剧情", "悬疑", "惊悚"],
"title": "大开眼戒",
"casts": [{
"alt": "https:\/\/movie.douban.com\/celebrity\/1054435\/",
"avatars": {
"small": "https://img1.doubanio.com\/view\/celebrity\/s_ratio_celebrity\/public\/p567.jpg",
"large": "https://img1.doubanio.com\/view\/celebrity\/s_ratio_celebrity\/public\/p567.jpg",
"medium": "https://img1.doubanio.com\/view\/celebrity\/s_ratio_celebrity\/public\/p567.jpg"
},
"name": "汤姆·克鲁斯",
"id": "1054435"
.......... 省略很多东西 ...............
"title": "搜索 \"TomCruise\" 的结果"
}
json内容太长了,浓缩了重点

这样就完成了我们第一次请求api内容。

三. 结果校验

1、Python自身特性断言:

本文版权归作者和博客园共有,原创作者:http://www.cnblogs.com/BenLam,未经作者同意必须在文章页面给出原文连接。

通过Requests请求,用Python IF 判断 HTTP 状态码(只有验证码是200就通过)

import requests
actor_name = "q=TomCruise" # 汤姆·克鲁斯的电影
url = "https://api.douban.com/v2/movie/search?"
req = requests.get(url + actor_name) # 通过requests 发起请求
if req.status_code == 200:
# req.status_code 就是HTTP的状态码
print("测试通过,请求已成功,请求所希望的响应头或数据体将随此响应返回")
elif req.status_code == 301:
# 有些重定向的301 可以用 elif 再判断
print("重定向过,被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一")
elif req.status_code == 500:
print("服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。")
elif req.status_code == 404:
print("请求失败,请求所希望得到的资源未被在服务器上发现。")
else:
print("请求出现意外,未找到合适的 HTTP Status_code!") # 好处:通过Python自身的特性来判断返回的状态码是否正确
# 坏处:判断过于简单,无法精准检查接口

用Python IF 判断加强版

import requests,json
actor_name = "q=TomCruise" # 演员名
url = "https://api.douban.com/v2/movie/search?"
try:
req = requests.get(url + actor_name) # 通过requests 发起请求
req = json.dumps(req.json(), ensure_ascii=False, indent=4)
except requests.RequestException as e:
raise
name = "汤姆·克鲁斯"
if name in req: # 判断返回的数据是否包括我们搜索的条件,这里我们搜索是“汤姆克鲁斯”
print("通过测试")
else:
print("测试不通过")

2.2、Unittest断言:

使用unittest进行判断测试结果是否包含我们想要的信息

import requests,json
import unittest class demo(unittest.TestCase):
def setUp(self):
# 初始化信息
self.url = "https://api.douban.com/v2/movie/search?"
self.actor_name = "q=TomCruise"
self.style_tag = "tag=爱情"
self.name = "汤姆·克鲁斯"
self.action = "动作" def test_actor(self):
# 执行测试
req = requests.get(self.url + self.actor_name) # 通过requests 发起请求
req = json.dumps(req.json(), ensure_ascii=False, indent=4)
self.assertIn(self.name, req, msg=None) # 检查有没有 "汤姆·克鲁斯"这个演员 def test_style_tag(self):
# 执行测试
req = requests.get(self.url + self.actor_name) # 通过requests 发起请求
req = json.dumps(req.json(), ensure_ascii=False, indent=4)
self.assertIn(self.action, req, msg=None) # 检查有没有 "动作片" if __name__ == '__main__':
unittest.main()

这是通过测试的运行结果:

..
----------------------------------------------------------------------
Ran 2 tests in 1.610s OK
[Run in 1.9s]

如果测试结果不通过显示F (如下):

import unittest

class demo(unittest.TestCase):
def test_Error(self):
a = ""
b = ""
self.assertEqual(a,b) # 检查 a 和 b 是否相同 if __name__ == '__main__':
unittest.main() # ==============================
# 测试结果
# ==============================
F
======================================================================
FAIL: test_Error (__main__.demo)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\1\demo.py", line 45, in test_Error
self.assertEqual(a,b) # 检查 a 和 b 是否相同
AssertionError: '' != ''
- 1
+ 2 ----------------------------------------------------------------------
Ran 1 test in 0.000s FAILED (failures=1)
[Run in 0.3s with exit code 1]

四. 报告生成

本文版权归作者和博客园共有,原创作者:http://www.cnblogs.com/BenLam,未经作者同意必须在文章页面给出原文连接。

通过HTMLTestRunner快速生成:

import unittest
import HTMLTestRunner
import time
class Test(unittest.TestCase):
def test_case1(self):
# 填写你的测试用例 if __name__ == '__main__':
# 获取当前时间
now = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
# html存放文件路径
Report_path = (Report_path, "Report_"+now+".html")
# 打开一个文件,将result写入此file中
file = open(Report_path, "wb")
runner = HTMLTestRunner.HTMLTestRunner(stream=file, title=u'接口测试报告:', description=u'用例执行情况:')
# 这里填写你的测试用例名
runner.run(all_case())
file.close()

五. 邮件推送测试报告

本文版权归作者和博客园共有,原创作者:http://www.cnblogs.com/BenLam,未经作者同意必须在文章页面给出原文连接。

通过邮件发送接口测试结果

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @file : 邮件.py
# @Date : 2018-06-02 14:14:46
# @Author : Destroyers (https://github.com/lguobin)
# @Link : https://github.com/lguobin
# @Version : $1.0$ import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication class Myemail:
def __init__(self,mail_list,mail_title):
self.mail_list = mail_list
self.mail_title = mail_title
self.mail_host = "smtp.163.com"
self.mail_user = "你自己邮箱@163.com"
self.mail_pass = "你的密码"
self.mail_postfix = "163.com" def SendMail(self):
info = self.mail_user + "<" + self.mail_user + "@" + self.mail_postfix + ">"
msg = MIMEMultipart()
msg['Subject'] = '接口测试报告'
msg['From'] = info
msg['To'] = ";".join(self.mail_list) with open("/temp/Report.html", 'r') as f:
Report_content = f.read().encode("utf-8")
puretext = MIMEText(Report_content, 'html', 'utf-8')
msg.attach(puretext)
try:
s = smtplib.SMTP() #创建邮件服务器对象
s.connect(self.mail_host) #连接到指定的smtp服务器。参数分别表示smpt主机和端口
s.login(self.mail_user, self.mail_pass) #登录到你邮箱
s.sendmail(me, self.mail_list, msg.as_string()) #发送内容
s.close()
return True
except Exception, e:
print str(e)
return False if __name__ == '__main__':
# 邮件登录
mailto_list = ["你领导的邮件@163,com","aaaa@163.com"]
mail_title = '接口测试报告:'
test = Myemail(mailto_list, mail_title)
print(test.SendMail())

本章节 End————下章再会

最新文章

  1. Latex制作beamer
  2. Oracle 常用修改字段SQL语句
  3. 数据绑定时(&lt;%#Eval)单引号双引号嵌套问题
  4. TFTP服务
  5. YUV格式详解
  6. Java异常之try,catch,finally,throw,throws
  7. Python 数据处理扩展包: numpy 和 pandas 模块介绍
  8. 136 - Ugly Numbers
  9. Spring源深和六系列 CreateBean过程
  10. 团队作业八——第二次团队冲刺(Beta版本)第6天
  11. 使用npm安装依赖,尽量别使用cnpm,会漏掉很多依赖的
  12. 学习EtherCAT的感想
  13. .NET Core微服务之路:让我们对上一个Demo通讯进行修改,完成RPC通讯
  14. VS2010与Qt5.1.0集成(非源码方式)
  15. 20169207《Linux内核原理与分析》第六周作业
  16. python xlsxwriter写excel并操作各种格式属性
  17. pandas的merge函数
  18. ICMP类型和代号对照表
  19. SQL 根据生日和日期计算年龄
  20. my.cnf 配置文件参数解释

热门文章

  1. SOE 部署错误 ClassFactory cannot supply requested class问题及解决方案
  2. CDN的作用与基本过程
  3. unordered_map 遇到 vector subscript out of range 的错误提示
  4. Python基础 条件判断和循环
  5. MySQL进阶(视图)---py全栈
  6. linux 学习第十三天(screen不间断会话、apache服务、SELinux安全子系统)
  7. 关于json时间数据格式转换与修改
  8. BootStrapValidate 简单使用
  9. PHP 好用第三方库
  10. 基于OpenCV的微信跳一跳外挂