diff --git a/Public/BSTestRunner.py b/Public/BSTestRunner.py new file mode 100644 index 0000000..58bc4a3 --- /dev/null +++ b/Public/BSTestRunner.py @@ -0,0 +1,806 @@ +""" +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 BSTestRunner + + ... define your tests ... + + if __name__ == '__main__': + BSTestRunner.main() + + +For more customization options, instantiates a BSTestRunner object. +BSTestRunner is a counterpart to unittest's TextTestRunner. E.g. + + # output to a file + fp = file('my_report.html', 'wb') + runner = BSTestRunner.BSTestRunner( + stream=fp, + title='My unit test', + description='This demonstrates the report output by BSTestRunner.' + ) + + # Use an external stylesheet. + # See the Template_mixin class for more customizable options + runner.STYLESHEET_TMPL = '' + + # run the test + runner.run(my_test_suite) + + +------------------------------------------------------------------------ +Copyright (c) 2004-2007, Wai Yip Tung +Copyright (c) 2016, Eason Han +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. +""" + + +__author__ = "Wai Yip Tung && Eason Han" +__version__ = "0.8.4" + + +""" +Change History + +Version 0.8.3 +* Modify html style using bootstrap3. + +Version 0.8.3 +* Prevent crash on class or module-level exceptions (Darren Wurf). + +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 + +
+ %(heading)s + %(report)s + %(ending)s +
+ + + +""" + # variables: (title, generator, stylesheet, heading, report, ending) + + + # ------------------------------------------------------------------------ + # Stylesheet + # + # alternatively use a for external style sheet, e.g. + # + + STYLESHEET_TMPL = """ + +""" + + + + # ------------------------------------------------------------------------ + # Heading + # + + HEADING_TMPL = """
+

%(title)s

+%(parameters)s +

%(description)s

+
+ +""" # variables: (title, parameters, description) + + HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

+""" # variables: (name, value) + + + + # ------------------------------------------------------------------------ + # Report + # + + REPORT_TMPL = """ +

+Summary +Failed +All +

+ + + + + + + %(test_list)s + + + + + + + + + + + +
Test Group/Test case + Count + Pass + Fail + Error + View +
Total%(count)s%(Pass)s%(fail)s%(error)s 
+""" # variables: (test_list, count, Pass, fail, error) + + REPORT_CLASS_TMPL = r""" + + %(desc)s + %(count)s + %(Pass)s + %(fail)s + %(error)s + Detail + +""" # variables: (style, desc, count, Pass, fail, error, cid) + + + REPORT_TEST_WITH_OUTPUT_TMPL = r""" + +
%(desc)s
+ + + + + %(status)s + + + + + + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_NO_OUTPUT_TMPL = r""" + +
%(desc)s
+ %(status)s + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_OUTPUT_TMPL = r""" +%(id)s: %(output)s +""" # variables: (id, output) + + + + # ------------------------------------------------------------------------ + # ENDING + # + + ENDING_TMPL = """
 
""" + +# -------------------- 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.outputBuffer = StringIO() + 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 = 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 BSTestRunner(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) + try: + test(result) + except TypeError: + pass + self.stopTime = datetime.datetime.now() + self.generateReport(test, result) + print('\n测试耗时: %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 = 'BSTestRunner %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('utf-8')) + + + 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), + + value = 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 'text text-warning' or nf > 0 and 'text text-danger' or 'text text-success', + 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 + 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 + else: + ue = e + + script = self.REPORT_TEST_OUTPUT_TMPL % dict( + id = tid, + output = saxutils.escape(uo+ue), + ) + + row = tmpl % dict( + tid = tid, + Class = (n == 0 and 'hiddenRow' or 'none'), + # Class = (n == 0 and 'hiddenRow' or 'text text-success'), + # style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), + style = n == 2 and 'text text-warning' or (n == 1 and 'text text-danger' or 'text text-success'), + 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 BSTestRunner as the default test runner. + # base class's testRunner parameter is not useful because it means + # we have to instantiate BSTestRunner before we know self.verbosity. + if self.testRunner is None: + self.testRunner = BSTestRunner(verbosity=self.verbosity) + unittest.TestProgram.runTests(self) + +main = TestProgram + +############################################################################## +# Executing this module from the command line +############################################################################## + +if __name__ == "__main__": + main(module=None) diff --git a/Public/get_excel.py b/Public/get_excel.py index ed02197..a0884cf 100644 --- a/Public/get_excel.py +++ b/Public/get_excel.py @@ -7,7 +7,7 @@ import xlrd,xlwt import unittest,sys from xlutils.copy import copy from Interface.test_requests import requ -from .log import LOG,logger +from Public.log import LOG,logger @logger('解析测试用例文件') def datacel(): try: @@ -32,4 +32,15 @@ def datacel(): listfangshi.append((me.cell(i,5).value)) listqiwang.append((me.cell(i,6).value)) return listid,listkey,listconeent,listurl,listfangshi,listqiwang,listname - except:LOG.info('打开测试用例失败,原因是:%s'%Exception) \ No newline at end of file + except:LOG.info('打开测试用例失败,原因是:%s'%Exception) +@logger('生成数据驱动所用数据') +def makedata(): + listid, listkey, listconeent, listurl, listfangshi, listqiwang, listname=datacel() + i=0 + make_data=[] + for i in range(len(listid)): + make_data.append({'url':listurl[i],'key':listkey[i],'coneent':listconeent[i],'fangshi':listfangshi[i],'qiwang':listqiwang[i]}) + i+=1 + return make_data + + diff --git a/ceshi.py b/ceshi.py new file mode 100644 index 0000000..e6038c6 --- /dev/null +++ b/ceshi.py @@ -0,0 +1,21 @@ +list1=[1,2,3,4,5,6,7] +list2=['a','b','c','d'] +list3=[] +i=0 +for i in range(len(list2)): + list3.append({'id':list1[i],'password':list2[i]}) + i+=1 +print(list3) +# import ddt,unittest +# data=[{1:1, 2:2},{1:2, 2:3},{1:3, 2:4}] +# @ddt.ddt +# class Testcase(unittest.TestCase): +# def setUp(self): +# pass +# def tearDown(self): +# pass +# @ddt.data(*data) +# def test(self,data): +# self.assertEqual(data[1],data[2]) +# if __name__=='__main__': +# unittest.main() diff --git a/img/xin_testcase.png b/img/xin_testcase.png new file mode 100644 index 0000000..13ddbc8 Binary files /dev/null and b/img/xin_testcase.png differ diff --git a/img/xinlog.png b/img/xinlog.png new file mode 100644 index 0000000..052dfea Binary files /dev/null and b/img/xinlog.png differ diff --git a/log/jiekou-2017-10-31-21.log b/log/jiekou-2017-10-31-21.log new file mode 100644 index 0000000..a46ad1e --- /dev/null +++ b/log/jiekou-2017-10-31-21.log @@ -0,0 +1,10 @@ +[2017-10-31 21:17:01.191553] INFO: jiekou: 当前模块 requests封装 +[2017-10-31 21:17:01.273558] INFO: jiekou: 当前模块 生成数据驱动所用数据 +[2017-10-31 21:17:01.274558] INFO: jiekou: 当前模块 解析测试用例文件 +[2017-10-31 21:17:16.660438] INFO: jiekou: 当前模块 requests封装 +[2017-10-31 21:17:16.739442] INFO: jiekou: 当前模块 生成数据驱动所用数据 +[2017-10-31 21:17:16.740442] INFO: jiekou: 当前模块 解析测试用例文件 +[2017-10-31 21:17:16.754443] INFO: jiekou: 当前模块 执行测试用例 +[2017-10-31 21:18:18.624982] INFO: jiekou: 当前模块 requests封装 +[2017-10-31 21:18:18.704987] INFO: jiekou: 当前模块 生成数据驱动所用数据 +[2017-10-31 21:18:18.705987] INFO: jiekou: 当前模块 解析测试用例文件 diff --git a/run_new.py b/run_new.py new file mode 100644 index 0000000..b8286f8 --- /dev/null +++ b/run_new.py @@ -0,0 +1,13 @@ +from testCase.test import ApiTest +import unittest,time,os +from Public import BSTestRunner +if __name__=='__main__': + suite = unittest.TestSuite() + suite.addTests(unittest.TestLoader().loadTestsFromTestCase(ApiTest)) + now = time.strftime('%Y-%m%d', time.localtime(time.time())) + basedir = os.path.abspath(os.path.dirname(__file__)) + file_dir = os.path.join(basedir, 'test_Report') + file = os.path.join(file_dir, (now + '.html')) + re_open = open(file, 'wb') + runner = BSTestRunner.BSTestRunner(stream=re_open, title='接口测试报告', description='测试结果') + runner.run(suite) \ No newline at end of file diff --git a/testCase/test.py b/testCase/test.py new file mode 100644 index 0000000..ac2e4fb --- /dev/null +++ b/testCase/test.py @@ -0,0 +1,20 @@ +from Interface.testFengzhuang import TestApi +from Public.get_excel import datacel,makedata +from Public.log import LOG,logger +import ddt,unittest +data_test=makedata() +@logger('执行测试用例') +@ddt.ddt +class ApiTest(unittest.TestCase): + def setUp(self): + pass + def tearDown(self): + pass + @ddt.data(*data_test) + def test_api(self,data_test): + api = TestApi(url=data_test['url'], key=data_test['key'], connent=data_test['coneent'], fangshi=data_test['fangshi']) + LOG.info('输入参数:url:%s,key:%s,参数:%s,请求方式:%s'%(data_test['url'],data_test['key'],data_test['coneent'], LOG.info('输入参数:url:%s,key:%s,参数:%s,请求方式:%s'%(data_test['url'],data_test['key'],data_test['coneent'],data_test['fangshi'])))) + apijson = api.getJson() + LOG.info('返回结果:%s'%apijson) + self.assertEqual(data_test['qiwang'],apijson,msg='预期和返回不一致') + diff --git a/test_Report/2017-1031.html b/test_Report/2017-1031.html new file mode 100644 index 0000000..2c72daa --- /dev/null +++ b/test_Report/2017-1031.html @@ -0,0 +1,197 @@ + + + + + + + + 接口测试报告 + + + + + + + + + + + + + +
+
+

接口测试报告

+

Start Time: 2017-10-31 21:15:03

+

Duration: 0:00:00

+

Status: none

+ +

测试结果

+
+ + + +

+Summary +Failed +All +

+ + + + + + + + + + + + + + + + + + +
Test Group/Test case + Count + Pass + Fail + Error + View +
Total0000 
+ +
 
+
+ + +