├── .gitignore ├── HTMLTestRunner.py ├── README.md ├── __init__.py ├── db_config.ini ├── db_fixture ├── __init__.py ├── mysql_db.py └── test_data.py ├── interface ├── __init__.py ├── add_event_test.py ├── add_guest_test.py ├── get_event_list_test.py ├── get_guest_list_test.py └── user_sign_test.py ├── report └── 2020-04-19 10_28_01_result.html └── run_tests.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | __pycache__/ 3 | -------------------------------------------------------------------------------- /HTMLTestRunner.py: -------------------------------------------------------------------------------- 1 | """ 2 | A TestRunner for use with the Python unit testing framework. It 3 | generates a HTML report to show the result at a glance. 4 | The simplest way to use this is to invoke its main method. E.g. 5 | import unittest 6 | import HTMLTestRunner 7 | ... define your tests ... 8 | if __name__ == '__main__': 9 | HTMLTestRunner.main() 10 | For more customization options, instantiates a HTMLTestRunner object. 11 | HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. 12 | # output to a file 13 | fp = file('my_report.html', 'wb') 14 | runner = HTMLTestRunner.HTMLTestRunner( 15 | stream=fp, 16 | title='My unit test', 17 | description='This demonstrates the report output by HTMLTestRunner.' 18 | ) 19 | # Use an external stylesheet. 20 | # See the Template_mixin class for more customizable options 21 | runner.STYLESHEET_TMPL = '' 22 | # run the test 23 | runner.run(my_test_suite) 24 | ------------------------------------------------------------------------ 25 | Copyright (c) 2004-2007, Wai Yip Tung 26 | All rights reserved. 27 | Redistribution and use in source and binary forms, with or without 28 | modification, are permitted provided that the following conditions are 29 | met: 30 | * Redistributions of source code must retain the above copyright notice, 31 | this list of conditions and the following disclaimer. 32 | * Redistributions in binary form must reproduce the above copyright 33 | notice, this list of conditions and the following disclaimer in the 34 | documentation and/or other materials provided with the distribution. 35 | * Neither the name Wai Yip Tung nor the names of its contributors may be 36 | used to endorse or promote products derived from this software without 37 | specific prior written permission. 38 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 39 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 40 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 41 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 42 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 43 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 44 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 45 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 46 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 47 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 48 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 49 | """ 50 | 51 | # URL: http://tungwaiyip.info/software/HTMLTestRunner.html 52 | 53 | __author__ = "Wai Yip Tung , bugmaster" 54 | __version__ = "0.9.0" 55 | 56 | """ 57 | Change History 58 | 59 | Version 0.9.0 60 | * Increased repeat execution 61 | * Added failure screenshots 62 | 63 | Version 0.8.2 64 | * Show output inline instead of popup window (Viorel Lupu). 65 | 66 | Version in 0.8.1 67 | * Validated XHTML (Wolfgang Borgert). 68 | * Added description of test classes and test cases. 69 | 70 | Version in 0.8.0 71 | * Define Template_mixin class for customization. 72 | * Workaround a IE 6 bug that it does not treat 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | %(stylesheet)s 193 | 194 | 195 | 377 | %(heading)s 378 | %(report)s 379 | %(ending)s 380 | %(chart_script)s 381 | 382 | 383 | """ 384 | # variables: (title, generator, stylesheet, heading, report, ending) 385 | 386 | # ------------------------------------------------------------------------ 387 | # Stylesheet 388 | # 389 | # alternatively use a for external style sheet, e.g. 390 | # 391 | 392 | STYLESHEET_TMPL = """ 393 | 545 | """ 546 | 547 | # ------------------------------------------------------------------------ 548 | # Heading 549 | # 550 | 551 | HEADING_TMPL = """ 552 | 563 |
564 |
565 |
566 |
567 | 568 | 569 | %(parameters)s 570 | 571 | 572 |
Description:%(description)s
573 |
574 |
575 |
576 | 577 |
578 |

Test Case Pie charts

579 |

%(pass_count)s

580 | PASSED
581 |

%(fail_count)s

582 | FAILED 583 |

%(error_count)s

584 | ERRORS
585 |

%(skip_count)s

586 | SKIPED
587 |
588 |
589 | 590 |
591 | 592 |
593 | """ # variables: (title, parameters, description) 594 | 595 | # ------------------------------------------------------------------------ 596 | # Pie chart 597 | # 598 | 599 | ECHARTS_SCRIPT = """ 600 | 639 | """ 640 | 641 | HEADING_ATTRIBUTE_TMPL = """%(name)s:%(value)s 642 | """ # variables: (name, value) 643 | 644 | # ------------------------------------------------------------------------ 645 | # Report 646 | # 647 | 648 | REPORT_TMPL = """ 649 |

650 | Summary 651 | Pass 652 | Failed 653 | Error 654 | Skip 655 | All 656 |

657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | %(test_list)s 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 |
Test Group/Test caseCountPassFailErrorViewScreenshots
Total%(count)s%(Pass)s%(fail)s%(error)s  
680 | """ # variables: (test_list, count, Pass, fail, error) 681 | 682 | REPORT_CLASS_TMPL = r""" 683 | 684 | %(desc)s 685 | %(count)s 686 | %(Pass)s 687 | %(fail)s 688 | %(error)s 689 | Detail 690 |   691 | 692 | """ # variables: (style, desc, count, Pass, fail, error, cid) 693 | 694 | REPORT_TEST_WITH_OUTPUT_TMPL = r""" 695 | 696 |
%(desc)s
697 | 698 | 699 | 700 | %(status)s 701 | 710 | 711 | 712 | %(img)s 713 | 714 | """ # variables: (tid, Class, style, desc, status) 715 | 716 | REPORT_TEST_NO_OUTPUT_TMPL = r""" 717 | 718 |
%(desc)s
719 | %(status)s 720 | %(img)s 721 | 722 | """ # variables: (tid, Class, style, desc, status) 723 | 724 | REPORT_TEST_OUTPUT_TMPL = r""" 725 | %(id)s: %(output)s 726 | """ # variables: (id, output) 727 | 728 | IMG_TMPL = r""" 729 | show 730 | 735 | """ 736 | # ------------------------------------------------------------------------ 737 | # ENDING 738 | # 739 | 740 | ENDING_TMPL = """
 
""" 741 | 742 | 743 | # -------------------- The end of the Template class ------------------- 744 | 745 | 746 | TestResult = unittest.TestResult 747 | 748 | 749 | class _TestResult(TestResult): 750 | # note: _TestResult is a pure representation of results. 751 | # It lacks the output and reporting ability compares to unittest._TextTestResult. 752 | 753 | def __init__(self, verbosity=1, rerun=0, save_last_run=False): 754 | TestResult.__init__(self) 755 | self.stdout0 = None 756 | self.stderr0 = None 757 | self.success_count = 0 758 | self.failure_count = 0 759 | self.error_count = 0 760 | self.skip_count = 0 761 | self.verbosity = verbosity 762 | 763 | # result is a list of result in 4 tuple 764 | # ( 765 | # result code (0: success; 1: fail; 2: error; 3: skip), 766 | # TestCase object, 767 | # Test output (byte string), 768 | # stack trace, 769 | # ) 770 | self.rerun = rerun 771 | self.save_last_run = save_last_run 772 | self.status = 0 773 | self.runs = 0 774 | self.result = [] 775 | 776 | def startTest(self, test): 777 | test.imgs = getattr(test, "imgs", []) 778 | # TestResult.startTest(self, test) 779 | # just one buffer for both stdout and stderr 780 | self.outputBuffer = io.StringIO() 781 | stdout_redirector.fp = self.outputBuffer 782 | stderr_redirector.fp = self.outputBuffer 783 | self.stdout0 = sys.stdout 784 | self.stderr0 = sys.stderr 785 | sys.stdout = stdout_redirector 786 | sys.stderr = stderr_redirector 787 | 788 | def complete_output(self): 789 | """ 790 | Disconnect output redirection and return buffer. 791 | Safe to call multiple times. 792 | """ 793 | if self.stdout0: 794 | sys.stdout = self.stdout0 795 | sys.stderr = self.stderr0 796 | self.stdout0 = None 797 | self.stderr0 = None 798 | return self.outputBuffer.getvalue() 799 | 800 | def stopTest(self, test): 801 | # Usually one of addSuccess, addError or addFailure would have been called. 802 | # But there are some path in unittest that would bypass this. 803 | # We must disconnect stdout in stopTest(), which is guaranteed to be called. 804 | if self.rerun and self.rerun >= 1: 805 | if self.status == 1: 806 | self.runs += 1 807 | if self.runs <= self.rerun: 808 | if self.save_last_run: 809 | t = self.result.pop(-1) 810 | if t[0] == 1: 811 | self.failure_count -= 1 812 | else: 813 | self.error_count -= 1 814 | test = copy.copy(test) 815 | sys.stderr.write("Retesting... ") 816 | sys.stderr.write(str(test)) 817 | sys.stderr.write('..%d \n' % self.runs) 818 | doc = getattr(test, '_testMethodDoc', u"") or u'' 819 | if doc.find('->rerun') != -1: 820 | doc = doc[:doc.find('->rerun')] 821 | desc = "%s->rerun:%d" % (doc, self.runs) 822 | if isinstance(desc, str): 823 | desc = desc 824 | test._testMethodDoc = desc 825 | test(self) 826 | else: 827 | self.status = 0 828 | self.runs = 0 829 | self.complete_output() 830 | 831 | def addSuccess(self, test): 832 | self.success_count += 1 833 | self.status = 0 834 | TestResult.addSuccess(self, test) 835 | output = self.complete_output() 836 | self.result.append((0, test, output, '')) 837 | if self.verbosity > 1: 838 | sys.stderr.write('ok ') 839 | sys.stderr.write(str(test)) 840 | sys.stderr.write('\n') 841 | else: 842 | sys.stderr.write('.' + str(self.success_count)) 843 | 844 | def addError(self, test, err): 845 | self.error_count += 1 846 | self.status = 1 847 | TestResult.addError(self, test, err) 848 | _, _exc_str = self.errors[-1] 849 | output = self.complete_output() 850 | self.result.append((2, test, output, _exc_str)) 851 | if not getattr(test, "driver", ""): 852 | pass 853 | else: 854 | try: 855 | driver = getattr(test, "driver") 856 | test.imgs.append(driver.get_screenshot_as_base64()) 857 | except BaseException: 858 | pass 859 | if self.verbosity > 1: 860 | sys.stderr.write('E ') 861 | sys.stderr.write(str(test)) 862 | sys.stderr.write('\n') 863 | else: 864 | sys.stderr.write('E') 865 | 866 | def addFailure(self, test, err): 867 | self.failure_count += 1 868 | self.status = 1 869 | TestResult.addFailure(self, test, err) 870 | _, _exc_str = self.failures[-1] 871 | output = self.complete_output() 872 | self.result.append((1, test, output, _exc_str)) 873 | if not getattr(test, "driver", ""): 874 | pass 875 | else: 876 | try: 877 | driver = getattr(test, "driver") 878 | test.imgs.append(driver.get_screenshot_as_base64()) 879 | except BaseException: 880 | pass 881 | if self.verbosity > 1: 882 | sys.stderr.write('F ') 883 | sys.stderr.write(str(test)) 884 | sys.stderr.write('\n') 885 | else: 886 | sys.stderr.write('F') 887 | 888 | def addSkip(self, test, reason): 889 | self.skip_count += 1 890 | self.status = 0 891 | TestResult.addSkip(self, test, reason) 892 | output = self.complete_output() 893 | self.result.append((3, test, output, reason)) 894 | if self.verbosity > 1: 895 | sys.stderr.write('S') 896 | sys.stderr.write(str(test)) 897 | sys.stderr.write('\n') 898 | else: 899 | sys.stderr.write('S') 900 | 901 | 902 | class HTMLTestRunner(Template_mixin): 903 | """ 904 | """ 905 | 906 | def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None, save_last_run=True): 907 | self.stream = stream 908 | self.verbosity = verbosity 909 | self.save_last_run = save_last_run 910 | self.run_times = 0 911 | if title is None: 912 | self.title = self.DEFAULT_TITLE 913 | else: 914 | self.title = title 915 | if description is None: 916 | self.description = self.DEFAULT_DESCRIPTION 917 | else: 918 | self.description = description 919 | 920 | self.startTime = datetime.datetime.now() 921 | 922 | def run(self, test, rerun, save_last_run): 923 | """Run the given test case or test suite.""" 924 | result = _TestResult(self.verbosity, rerun=rerun, save_last_run=save_last_run) 925 | test(result) 926 | self.stopTime = datetime.datetime.now() 927 | self.run_times += 1 928 | self.generateReport(test, result) 929 | # print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)) 930 | return result 931 | 932 | def sortResult(self, result_list): 933 | # unittest does not seems to run in any particular order. 934 | # Here at least we want to group them together by class. 935 | rmap = {} 936 | classes = [] 937 | for n, t, o, e in result_list: 938 | cls = t.__class__ 939 | if not cls in rmap: 940 | rmap[cls] = [] 941 | classes.append(cls) 942 | rmap[cls].append((n, t, o, e)) 943 | r = [(cls, rmap[cls]) for cls in classes] 944 | return r 945 | 946 | def getReportAttributes(self, result): 947 | """ 948 | Return report attributes as a list of (name, value). 949 | Override this to add custom attributes. 950 | """ 951 | startTime = str(self.startTime)[:19] 952 | duration = str(self.stopTime - self.startTime) 953 | status = [] 954 | 955 | if result.success_count: 956 | status.append('Passed:%s' % result.success_count) 957 | if result.failure_count: 958 | status.append('Failed:%s' % result.failure_count) 959 | if result.error_count: 960 | status.append('Errors:%s' % result.error_count) 961 | if result.skip_count: 962 | status.append('Skiped:%s' % result.skip_count) 963 | if status: 964 | status = ' '.join(status) 965 | else: 966 | status = 'none' 967 | result = { 968 | "pass": result.success_count, 969 | "fail": result.failure_count, 970 | "error": result.error_count, 971 | "skip": result.skip_count, 972 | } 973 | return [ 974 | ('Start Time', startTime), 975 | ('Duration', duration), 976 | ('Status', status), 977 | ('Result', result), 978 | ] 979 | 980 | def generateReport(self, test, result): 981 | report_attrs = self.getReportAttributes(result) 982 | generator = 'HTMLTestRunner %s' % __version__ 983 | stylesheet = self._generate_stylesheet() 984 | heading = self._generate_heading(report_attrs) 985 | report = self._generate_report(result) 986 | ending = self._generate_ending() 987 | chart = self._generate_chart(result) 988 | output = self.HTML_TMPL % dict( 989 | title=saxutils.escape(self.title), 990 | generator=generator, 991 | stylesheet=stylesheet, 992 | heading=heading, 993 | report=report, 994 | ending=ending, 995 | chart_script=chart, 996 | channel=self.run_times, 997 | ) 998 | self.stream.write(output.encode('utf8')) 999 | 1000 | def _generate_stylesheet(self): 1001 | return self.STYLESHEET_TMPL 1002 | 1003 | def _generate_heading(self, report_attrs): 1004 | a_lines = [] 1005 | for name, value in report_attrs: 1006 | result = {} 1007 | if name == "Result": 1008 | result = value 1009 | else: 1010 | line = self.HEADING_ATTRIBUTE_TMPL % dict( 1011 | name=saxutils.escape(name), 1012 | value=saxutils.escape(value), 1013 | ) 1014 | a_lines.append(line) 1015 | heading = self.HEADING_TMPL % dict( 1016 | title=saxutils.escape(self.title), 1017 | parameters=''.join(a_lines), 1018 | description=saxutils.escape(self.description), 1019 | pass_count=saxutils.escape(str(result["pass"])), 1020 | fail_count=saxutils.escape(str(result["fail"])), 1021 | error_count=saxutils.escape(str(result["error"])), 1022 | skip_count=saxutils.escape(str(result["skip"])), 1023 | ) 1024 | return heading 1025 | 1026 | def _generate_report(self, result): 1027 | rows = [] 1028 | sortedResult = self.sortResult(result.result) 1029 | for cid, (cls, cls_results) in enumerate(sortedResult): 1030 | # subtotal for a class 1031 | np = nf = ne = ns = 0 1032 | for n, t, o, e in cls_results: 1033 | if n == 0: 1034 | np += 1 1035 | elif n == 1: 1036 | nf += 1 1037 | elif n == 2: 1038 | ne += 1 1039 | else: 1040 | ns += 1 1041 | 1042 | # format class description 1043 | if cls.__module__ == "__main__": 1044 | name = cls.__name__ 1045 | else: 1046 | name = "%s.%s" % (cls.__module__, cls.__name__) 1047 | doc = cls.__doc__ or "" 1048 | desc = doc and '%s: %s' % (name, doc) or name 1049 | 1050 | row = self.REPORT_CLASS_TMPL % dict( 1051 | style=ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', 1052 | desc=desc, 1053 | count=np + nf + ne, 1054 | Pass=np, 1055 | fail=nf, 1056 | error=ne, 1057 | cid='c%s.%s' % (self.run_times, cid + 1), 1058 | ) 1059 | rows.append(row) 1060 | 1061 | for tid, (n, t, o, e) in enumerate(cls_results): 1062 | self._generate_report_test(rows, cid, tid, n, t, o, e) 1063 | 1064 | report = self.REPORT_TMPL % dict( 1065 | test_list=''.join(rows), 1066 | count=str(result.success_count + result.failure_count + result.error_count), 1067 | Pass=str(result.success_count), 1068 | fail=str(result.failure_count), 1069 | error=str(result.error_count), 1070 | skip=str(result.skip_count), 1071 | total=str(result.success_count + result.failure_count + result.error_count), 1072 | channel=str(self.run_times), 1073 | ) 1074 | return report 1075 | 1076 | def _generate_chart(self, result): 1077 | chart = self.ECHARTS_SCRIPT % dict( 1078 | Pass=str(result.success_count), 1079 | fail=str(result.failure_count), 1080 | error=str(result.error_count), 1081 | skip=str(result.skip_count), 1082 | ) 1083 | return chart 1084 | 1085 | def _generate_report_test(self, rows, cid, tid, n, t, o, e): 1086 | # e.g. 'pt1.1', 'ft1.1','et1.1', 'st1.1' etc 1087 | has_output = bool(o or e) 1088 | if n == 0: 1089 | tmp = "p" 1090 | elif n == 1: 1091 | tmp = "f" 1092 | elif n == 2: 1093 | tmp = "e" 1094 | else: 1095 | tmp = "s" 1096 | tid = tmp + 't%d.%d.%d' % (self.run_times, cid + 1, tid + 1) 1097 | # tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid + 1, tid + 1) 1098 | name = t.id().split('.')[-1] 1099 | doc = t.shortDescription() or "" 1100 | desc = doc and ('%s: %s' % (name, doc)) or name 1101 | tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL 1102 | 1103 | # o and e should be byte string because they are collected from stdout and stderr? 1104 | if isinstance(o, str): 1105 | # TODO: some problem with 'string_escape': it escape \n and mess up formating 1106 | # uo = unicode(o.encode('string_escape')) 1107 | uo = o 1108 | else: 1109 | uo = o 1110 | if isinstance(e, str): 1111 | # TODO: some problem with 'string_escape': it escape \n and mess up formating 1112 | # ue = unicode(e.encode('string_escape')) 1113 | ue = e 1114 | else: 1115 | ue = e 1116 | 1117 | script = self.REPORT_TEST_OUTPUT_TMPL % dict( 1118 | id=tid, 1119 | output=saxutils.escape(uo + ue), 1120 | ) 1121 | if getattr(t, 'imgs', []): 1122 | # 判断截图列表,如果有则追加 1123 | tmp = "" 1124 | for i, img in enumerate(t.imgs): 1125 | if i == 0: 1126 | tmp += """\n""".format(img) 1127 | else: 1128 | tmp += """\n""".format(img) 1129 | screenshots_html = self.IMG_TMPL.format(imgs=tmp) 1130 | else: 1131 | screenshots_html = """""" 1132 | 1133 | row = tmpl % dict( 1134 | tid=tid, 1135 | Class=(n == 0 and 'hiddenRow' or 'none'), 1136 | style=n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'passCase'), 1137 | desc=desc, 1138 | script=script, 1139 | status=self.STATUS[n], 1140 | img=screenshots_html 1141 | ) 1142 | rows.append(row) 1143 | if not has_output: 1144 | return 1145 | 1146 | def _generate_ending(self): 1147 | return self.ENDING_TMPL 1148 | 1149 | 1150 | ############################################################################## 1151 | # Facilities for running tests from the command line 1152 | ############################################################################## 1153 | 1154 | # Note: Reuse unittest.TestProgram to launch test. In the future we may 1155 | # build our own launcher to support more specific command line 1156 | # parameters like test title, CSS, etc. 1157 | class TestProgram(unittest.TestProgram): 1158 | """ 1159 | A variation of the unittest.TestProgram. Please refer to the base 1160 | class for command line parameters. 1161 | """ 1162 | 1163 | def runTests(self): 1164 | # Pick HTMLTestRunner as the default test runner. 1165 | # base class's testRunner parameter is not useful because it means 1166 | # we have to instantiate HTMLTestRunner before we know self.verbosity. 1167 | if self.testRunner is None: 1168 | self.testRunner = HTMLTestRunner(verbosity=self.verbosity) 1169 | unittest.TestProgram.runTests(self) 1170 | 1171 | 1172 | main = TestProgram 1173 | 1174 | ############################################################################## 1175 | # Executing this module from the command line 1176 | ############################################################################## 1177 | 1178 | if __name__ == "__main__": 1179 | main(module=None) 1180 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyrequest 2 | 3 | 介绍: 4 | 本项目为《Web接口开发与自动化测试--基于Python语言》一书中接口自动化测试项目代码。主要针对发布会签到系统的接口进行测试。 5 | 发布会签到系统:http://github.com/defnngj/guest 6 | 7 | 它包含功能: 8 | * 测试数据初始化,并对数据的插入做了封装。 9 | * unittest单元测试框架运行测试 10 | * HTMLTestRunner生成接口测试报告 11 | 12 | 13 | Python版本与依赖库: 14 | * python3.5+ :https://www.python.org/ 15 | * Requests : https://github.com/kennethreitz/requests 16 | * PyMySQL : https://github.com/PyMySQL/PyMySQL 17 | 18 | 对应教材购买: 19 | 《Web接口开发与自动化测试--基于Python语言》https://item.jd.com/12164814.html 20 | 21 | 22 | ```MySQL 23 | ALTER TABLE `sign_event` CHANGE `create_time` `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 24 | ALTER TABLE `sign_guest` CHANGE `create_time` `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP 25 | ``` 26 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/pyrequest/ab080f9f57fdbfb51cb8b7a4e474d478d454e99d/__init__.py -------------------------------------------------------------------------------- /db_config.ini: -------------------------------------------------------------------------------- 1 | [mysqlconf] 2 | host=127.0.0.1 3 | port=3306 4 | user=root 5 | password= 6 | db_name=guest_test 7 | -------------------------------------------------------------------------------- /db_fixture/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/pyrequest/ab080f9f57fdbfb51cb8b7a4e474d478d454e99d/db_fixture/__init__.py -------------------------------------------------------------------------------- /db_fixture/mysql_db.py: -------------------------------------------------------------------------------- 1 | # coding=utf8 2 | import pymysql.cursors 3 | from os.path import abspath, dirname 4 | import configparser as cparser 5 | 6 | 7 | # ======== Reading db_config.ini setting =========== 8 | base_dir = dirname(dirname(abspath(__file__))) 9 | base_dir = base_dir.replace('\\', '/') 10 | file_path = base_dir + "/db_config.ini" 11 | 12 | cf = cparser.ConfigParser() 13 | 14 | cf.read(file_path) 15 | host = cf.get("mysqlconf", "host") 16 | port = cf.get("mysqlconf", "port") 17 | db = cf.get("mysqlconf", "db_name") 18 | user = cf.get("mysqlconf", "user") 19 | password = cf.get("mysqlconf", "password") 20 | 21 | 22 | # ======== MySql base operating =================== 23 | class DB: 24 | 25 | def __init__(self): 26 | try: 27 | # Connect to the database 28 | self.connection = pymysql.connect(host=host, 29 | port=int(port), 30 | user=user, 31 | password=password, 32 | db=db, 33 | charset='utf8mb4', 34 | cursorclass=pymysql.cursors.DictCursor) 35 | except pymysql.err.OperationalError as e: 36 | print("Mysql Error %d: %s" % (e.args[0], e.args[1])) 37 | 38 | # clear table data 39 | def clear(self, table_name): 40 | # real_sql = "truncate table " + table_name + ";" 41 | real_sql = "delete from " + table_name + ";" 42 | with self.connection.cursor() as cursor: 43 | cursor.execute("SET FOREIGN_KEY_CHECKS=0;") 44 | cursor.execute(real_sql) 45 | self.connection.commit() 46 | 47 | # insert sql statement 48 | def insert(self, table_name, table_data): 49 | for key in table_data: 50 | table_data[key] = "'"+str(table_data[key])+"'" 51 | key = ','.join(table_data.keys()) 52 | value = ','.join(table_data.values()) 53 | real_sql = "INSERT INTO " + table_name + " (" + key + ") VALUES (" + value + ")" 54 | #print(real_sql) 55 | 56 | with self.connection.cursor() as cursor: 57 | cursor.execute(real_sql) 58 | 59 | self.connection.commit() 60 | 61 | # close database 62 | def close(self): 63 | self.connection.close() 64 | 65 | # init data 66 | def init_data(self, datas): 67 | for table, data in datas.items(): 68 | self.clear(table) 69 | for d in data: 70 | self.insert(table, d) 71 | self.close() 72 | 73 | 74 | if __name__ == '__main__': 75 | 76 | db = DB() 77 | table_name = "sign_event" 78 | data = {'id':1,'name':'红米','`limit`':2000,'status':1,'address':'北京会展中心','start_time':'2016-08-20 00:25:42'} 79 | table_name2 = "sign_guest" 80 | data2 = {'realname':'alen','phone':12312341234,'email':'alen@mail.com','sign':0,'event_id':1} 81 | 82 | db.clear(table_name) 83 | db.insert(table_name, data) 84 | db.close() 85 | -------------------------------------------------------------------------------- /db_fixture/test_data.py: -------------------------------------------------------------------------------- 1 | import sys, time 2 | sys.path.append('../db_fixture') 3 | try: 4 | from mysql_db import DB 5 | except ImportError: 6 | from .mysql_db import DB 7 | 8 | # 定义过去时间 9 | past_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()-100000)) 10 | 11 | # 定义将来时间 12 | future_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()+10000)) 13 | 14 | 15 | 16 | # create data 17 | datas = { 18 | 'sign_event':[ 19 | {'id':1,'name':'红米Pro发布会','`limit`':2000,'status':1,'address':'北京会展中心','start_time':future_time}, 20 | {'id':2,'name':'可参加人数为0','`limit`':0,'status':1,'address':'北京会展中心','start_time':future_time}, 21 | {'id':3,'name':'当前状态为0关闭','`limit`':2000,'status':0,'address':'北京会展中心','start_time':future_time}, 22 | {'id':4,'name':'发布会已结束','`limit`':2000,'status':1,'address':'北京会展中心','start_time':past_time}, 23 | {'id':5,'name':'小米5发布会','`limit`':2000,'status':1,'address':'北京国家会议中心','start_time':future_time}, 24 | ], 25 | 'sign_guest':[ 26 | {'id':1,'realname':'alen','phone':13511001100,'email':'alen@mail.com','sign':0,'event_id':1}, 27 | {'id':2,'realname':'has sign','phone':13511001101,'email':'sign@mail.com','sign':1,'event_id':1}, 28 | {'id':3,'realname':'tom','phone':13511001102,'email':'tom@mail.com','sign':0,'event_id':5}, 29 | ], 30 | } 31 | 32 | 33 | # Inster table datas 34 | def init_data(): 35 | DB().init_data(datas) 36 | 37 | 38 | if __name__ == '__main__': 39 | init_data() 40 | -------------------------------------------------------------------------------- /interface/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defnngj/pyrequest/ab080f9f57fdbfb51cb8b7a4e474d478d454e99d/interface/__init__.py -------------------------------------------------------------------------------- /interface/add_event_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import requests 3 | import os, sys 4 | parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | sys.path.insert(0, parentdir) 6 | from db_fixture import test_data 7 | 8 | 9 | class AddEventTest(unittest.TestCase): 10 | ''' 添加发布会 ''' 11 | 12 | def setUp(self): 13 | self.base_url = "http://127.0.0.1:8000/api/add_event/" 14 | 15 | def tearDown(self): 16 | print(self.result) 17 | 18 | def test_add_event_all_null(self): 19 | ''' 所有参数为空 ''' 20 | payload = {'eid':'','':'','limit':'','address':"",'start_time':''} 21 | r = requests.post(self.base_url, data=payload) 22 | self.result = r.json() 23 | self.assertEqual(self.result['status'], 10021) 24 | self.assertEqual(self.result['message'], 'parameter error') 25 | 26 | def test_add_event_eid_exist(self): 27 | ''' id已经存在 ''' 28 | payload = {'eid':1,'name':'一加4发布会','limit':2000,'address':"深圳宝体",'start_time':'2017'} 29 | r = requests.post(self.base_url, data=payload) 30 | self.result = r.json() 31 | self.assertEqual(self.result['status'], 10022) 32 | self.assertEqual(self.result['message'], 'event id already exists') 33 | 34 | def test_add_event_name_exist(self): 35 | ''' 名称已经存在 ''' 36 | payload = {'eid':11,'name':'红米Pro发布会','limit':2000,'address':"深圳宝体",'start_time':'2017'} 37 | r = requests.post(self.base_url,data=payload) 38 | self.result = r.json() 39 | self.assertEqual(self.result['status'], 10023) 40 | self.assertEqual(self.result['message'], 'event name already exists') 41 | 42 | def test_add_event_data_type_error(self): 43 | ''' 日期格式错误 ''' 44 | payload = {'eid':11,'name':'一加4手机发布会','limit':2000,'address':"深圳宝体",'start_time':'2017'} 45 | r = requests.post(self.base_url,data=payload) 46 | self.result = r.json() 47 | self.assertEqual(self.result['status'], 10024) 48 | self.assertIn('start_time format error.', self.result['message']) 49 | 50 | def test_add_event_success(self): 51 | ''' 添加成功 ''' 52 | payload = {'eid':11,'name':'一加4手机发布会','limit':2000,'address':"深圳宝体",'start_time':'2017-05-10 12:00:00'} 53 | r = requests.post(self.base_url,data=payload) 54 | self.result = r.json() 55 | self.assertEqual(self.result['status'], 200) 56 | self.assertEqual(self.result['message'], 'add event success') 57 | 58 | 59 | if __name__ == '__main__': 60 | test_data.init_data() # 初始化接口测试数据 61 | unittest.main() 62 | -------------------------------------------------------------------------------- /interface/add_guest_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import requests 3 | import os, sys 4 | parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | sys.path.insert(0, parentdir) 6 | from db_fixture import test_data 7 | 8 | 9 | class AddGuessTest(unittest.TestCase): 10 | ''' 添加嘉宾 ''' 11 | 12 | def setUp(self): 13 | self.base_url = "http://127.0.0.1:8000/api/add_guest/" 14 | 15 | def tearDown(self): 16 | print(self.result) 17 | 18 | def test_add_guest_all_null(self): 19 | ''' 参数为空 ''' 20 | payload = {'eid':'','realname':'','phone':''} 21 | r = requests.get(self.base_url, data=payload) 22 | self.result = r.json() 23 | self.assertEqual(self.result['status'], 10021) 24 | self.assertEqual(self.result['message'], 'parameter error') 25 | 26 | def test_add_guest_eid_null(self): 27 | ''' eid=901 查询为空 ''' 28 | payload = {'eid':901,'realname':'tom','phone':13711001100} 29 | r = requests.post(self.base_url, data=payload) 30 | self.result = r.json() 31 | self.assertEqual(self.result['status'], 10022) 32 | self.assertEqual(self.result['message'], 'event id null') 33 | 34 | def test_add_guest_status_close(self): 35 | ''' eid=2 状态未开启 ''' 36 | payload = {'eid':3,'realname':'tom','phone':13711001100} 37 | r = requests.post(self.base_url,data=payload) 38 | self.result = r.json() 39 | self.assertEqual(self.result['status'], 10023) 40 | self.assertEqual(self.result['message'], 'event status is not available') 41 | 42 | def test_add_guest_limit_full(self): 43 | ''' eid=2 发布会人数已满 ''' 44 | payload = {'eid':2,'realname':'tom','phone':13711001100} 45 | r = requests.post(self.base_url,data=payload) 46 | self.result = r.json() 47 | self.assertEqual(self.result['status'], 10024) 48 | self.assertEqual(self.result['message'], 'event number is full') 49 | 50 | def test_add_guest_time_start(self): 51 | ''' eid=4 发布会已开始 ''' 52 | payload = {'eid':4,'realname':'tom','phone':13711001100} 53 | r = requests.post(self.base_url,data=payload) 54 | self.result = r.json() 55 | self.assertEqual(self.result['status'], 10025) 56 | self.assertEqual(self.result['message'], 'event has started') 57 | 58 | def test_add_guest_phone_repeat(self): 59 | ''' phone=13800113001 手机号重复 ''' 60 | payload = {'eid':1,'realname':'tom','phone':13511001100} 61 | r = requests.post(self.base_url,data=payload) 62 | self.result = r.json() 63 | self.assertEqual(self.result['status'], 10026) 64 | self.assertEqual(self.result['message'], 'the event guest phone number repeat') 65 | 66 | def test_add_guest_success(self): 67 | ''' 添加成功 ''' 68 | payload = {'eid':1,'realname':'tom','phone':13511001199} 69 | r = requests.post(self.base_url,data=payload) 70 | self.result = r.json() 71 | self.assertEqual(self.result['status'], 200) 72 | self.assertEqual(self.result['message'], 'add guest success') 73 | 74 | 75 | if __name__ == '__main__': 76 | test_data.init_data() # 初始化接口测试数据 77 | unittest.main() 78 | -------------------------------------------------------------------------------- /interface/get_event_list_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import requests 3 | import os, sys 4 | parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | sys.path.insert(0, parentdir) 6 | from db_fixture import test_data 7 | 8 | 9 | class GetEventListTest(unittest.TestCase): 10 | ''' 获得发布会列表 ''' 11 | 12 | 13 | def setUp(self): 14 | self.base_url = "http://127.0.0.1:8000/api/get_event_list/" 15 | 16 | def tearDown(self): 17 | print(self.result) 18 | 19 | def test_get_event_list_eid_error(self): 20 | ''' eid=901 查询结果为空 ''' 21 | r = requests.get(self.base_url, params={'eid':901}) 22 | self.result = r.json() 23 | self.assertEqual(self.result['status'], 10022) 24 | self.assertEqual(self.result['message'], 'query result is empty') 25 | 26 | def test_get_event_list_eid_success(self): 27 | ''' 根据 eid 查询结果成功 ''' 28 | r = requests.get(self.base_url, params={'eid':1}) 29 | self.result = r.json() 30 | self.assertEqual(self.result['status'], 200) 31 | self.assertEqual(self.result['message'], 'success') 32 | self.assertEqual(self.result['data']['name'],u'红米Pro发布会') 33 | self.assertEqual(self.result['data']['address'],u'北京会展中心') 34 | 35 | def test_get_event_list_nam_result_null(self): 36 | ''' 关键字‘abc’查询 ''' 37 | r = requests.get(self.base_url, params={'name':'abc'}) 38 | self.result = r.json() 39 | self.assertEqual(self.result['status'], 10022) 40 | self.assertEqual(self.result['message'], 'query result is empty') 41 | 42 | def test_get_event_list_name_find(self): 43 | ''' 关键字‘发布会’模糊查询 ''' 44 | r = requests.get(self.base_url, params={'name':'发布会'}) 45 | self.result = r.json() 46 | self.assertEqual(self.result['status'], 200) 47 | self.assertEqual(self.result['message'], 'success') 48 | self.assertEqual(self.result['data'][0]['name'],u'红米Pro发布会') 49 | self.assertEqual(self.result['data'][0]['address'],u'北京会展中心') 50 | 51 | 52 | if __name__ == '__main__': 53 | test_data.init_data() # 初始化接口测试数据 54 | unittest.main() 55 | -------------------------------------------------------------------------------- /interface/get_guest_list_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import requests 3 | import os, sys 4 | parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | sys.path.insert(0, parentdir) 6 | from db_fixture import test_data 7 | 8 | 9 | class GetGuestListTest(unittest.TestCase): 10 | ''' 获得嘉宾列表 ''' 11 | 12 | def setUp(self): 13 | self.base_url = "http://127.0.0.1:8000/api/get_guest_list/" 14 | 15 | def tearDown(self): 16 | print(self.result) 17 | 18 | def test_get_guest_list_eid_null(self): 19 | ''' eid 参数为空 ''' 20 | r = requests.get(self.base_url, params={'eid':''}) 21 | self.result = r.json() 22 | self.assertEqual(self.result['status'], 10021) 23 | self.assertEqual(self.result['message'], 'eid cannot be empty') 24 | 25 | def test_get_event_list_eid_error(self): 26 | ''' 根据 eid 查询结果为空 ''' 27 | r = requests.get(self.base_url, params={'eid':901}) 28 | self.result = r.json() 29 | self.assertEqual(self.result['status'], 10022) 30 | self.assertEqual(self.result['message'], 'query result is empty') 31 | 32 | def test_get_event_list_eid_success(self): 33 | ''' 根据 eid 查询结果成功 ''' 34 | r = requests.get(self.base_url, params={'eid':1}) 35 | self.result = r.json() 36 | self.assertEqual(self.result['status'], 200) 37 | self.assertEqual(self.result['message'], 'success') 38 | self.assertEqual(self.result['data'][0]['realname'],'alen') 39 | self.assertEqual(self.result['data'][0]['phone'],'13511001100') 40 | 41 | def test_get_event_list_eid_phone_null(self): 42 | ''' 根据 eid 和phone 查询结果为空 ''' 43 | r = requests.get(self.base_url, params={'eid':1,'phone':'10000000000'}) 44 | self.result = r.json() 45 | self.assertEqual(self.result['status'], 10022) 46 | self.assertEqual(self.result['message'], 'query result is empty') 47 | 48 | def test_get_event_list_eid_phone_success(self): 49 | ''' 根据 eid 和phone 查询结果成功 ''' 50 | r = requests.get(self.base_url, params={'eid':1,'phone':'13511001100'}) 51 | self.result = r.json() 52 | self.assertEqual(self.result['status'], 200) 53 | self.assertEqual(self.result['message'], 'success') 54 | self.assertEqual(self.result['data']['realname'],'alen') 55 | self.assertEqual(self.result['data']['phone'],'13511001100') 56 | 57 | 58 | if __name__ == '__main__': 59 | test_data.init_data() # 初始化接口测试数据 60 | unittest.main() 61 | -------------------------------------------------------------------------------- /interface/user_sign_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import requests 3 | import os, sys 4 | parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | sys.path.insert(0, parentdir) 6 | from db_fixture import test_data 7 | 8 | 9 | class UserSignTest(unittest.TestCase): 10 | ''' 用户签到 ''' 11 | 12 | def setUp(self): 13 | self.base_url = "http://127.0.0.1:8000/api/user_sign/" 14 | 15 | def tearDown(self): 16 | print(self.result) 17 | 18 | def test_user_sign_all_null(self): 19 | ''' 参数为空 ''' 20 | payload = {'eid':'','phone':''} 21 | r = requests.post(self.base_url,data=payload) 22 | self.result = r.json() 23 | self.assertEqual(self.result['status'], 10021) 24 | self.assertEqual(self.result['message'], 'parameter error') 25 | 26 | def test_user_sign_eid_error(self): 27 | ''' eid=901 查询结果不存在 ''' 28 | payload = {'eid':901,'phone':13711001100} 29 | r = requests.post(self.base_url,data=payload) 30 | self.result = r.json() 31 | self.assertEqual(self.result['status'], 10022) 32 | self.assertEqual(self.result['message'], 'event id null') 33 | 34 | def test_user_sign_status_close(self): 35 | ''' eid=3 发布会状态关闭 ''' 36 | payload = {'eid':3,'phone':13711001100} 37 | r = requests.post(self.base_url,data=payload) 38 | self.result = r.json() 39 | self.assertEqual(self.result['status'], 10023) 40 | self.assertEqual(self.result['message'], 'event status is not available') 41 | 42 | def test_user_sign_time_start(self): 43 | ''' eid=4 发布会已开始 ''' 44 | payload = {'eid':4,'phone':13711001100} 45 | r = requests.post(self.base_url,data=payload) 46 | self.result = r.json() 47 | self.assertEqual(self.result['status'], 10024) 48 | self.assertEqual(self.result['message'], 'event has started') 49 | 50 | def test_user_sign_phone_error(self): 51 | ''' phone=10100001111 手机号不存在 ''' 52 | payload = {'eid':1,'phone':10100001111} 53 | r = requests.post(self.base_url,data=payload) 54 | self.result = r.json() 55 | self.assertEqual(self.result['status'], 10025) 56 | self.assertEqual(self.result['message'], 'user phone null') 57 | 58 | def test_user_sign_eid_phone_error(self): 59 | '''eid=1, phone=13511001102 手机号与发布会不匹配 ''' 60 | payload = {'eid':1,'phone':13511001102} 61 | r = requests.post(self.base_url,data=payload) 62 | self.result = r.json() 63 | self.assertEqual(self.result['status'], 10026) 64 | self.assertEqual(self.result['message'], 'user did not participate in the conference') 65 | 66 | def test_user_sign_has_sign_in(self): 67 | ''' 已签到 ''' 68 | payload = {'eid':1,'phone':13511001101} 69 | r = requests.post(self.base_url,data=payload) 70 | self.result = r.json() 71 | self.assertEqual(self.result['status'], 10027) 72 | self.assertEqual(self.result['message'], 'user has sign in') 73 | 74 | def test_user_sign_success(self): 75 | ''' 签到成功 ''' 76 | payload = {'eid':1,'phone':13511001100} 77 | r = requests.post(self.base_url,data=payload) 78 | self.result = r.json() 79 | self.assertEqual(self.result['status'], 200) 80 | self.assertEqual(self.result['message'], 'sign success') 81 | 82 | 83 | if __name__ == '__main__': 84 | test_data.init_data() # 初始化接口测试数据 85 | unittest.main() 86 | -------------------------------------------------------------------------------- /report/2020-04-19 10_28_01_result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 发布会签到系统接口自动化测试 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 170 | 171 | 172 | 173 | 355 | 356 | 367 |
368 |
369 |
370 |
371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 |
Start Time:2020-04-19 10:28:01
Duration:0:00:00.241355
Status:Passed:13 Failed:16
Description:运行环境:MySQL(PyMySQL), Requests, unittest
380 |
381 |
382 |
383 | 384 |
385 |

Test Case Pie charts

386 |

13

387 | PASSED
388 |

16

389 | FAILED 390 |

0

391 | ERRORS
392 |

0

393 | SKIPED
394 |
395 |
396 | 397 |
398 | 399 |
400 | 401 | 402 |

403 | Summary 404 | Pass 405 | Failed 406 | Error 407 | Skip 408 | All 409 |

410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 453 | 454 | 455 | 456 | 457 | 458 | 480 | 481 | 482 | 483 | 484 | 485 | 503 | 504 | 505 | 506 | 507 | 508 | 530 | 531 | 532 | 533 | 534 | 535 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 590 | 591 | 592 | 593 | 594 | 595 | 613 | 614 | 615 | 616 | 617 | 618 | 640 | 641 | 642 | 643 | 644 | 645 | 667 | 668 | 669 | 670 | 671 | 672 | 694 | 695 | 696 | 697 | 698 | 699 | 721 | 722 | 723 | 724 | 725 | 726 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 777 | 778 | 779 | 780 | 781 | 782 | 807 | 808 | 809 | 810 | 811 | 812 | 830 | 831 | 832 | 833 | 834 | 835 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 893 | 894 | 895 | 896 | 897 | 898 | 916 | 917 | 918 | 919 | 920 | 921 | 943 | 944 | 945 | 946 | 947 | 948 | 973 | 974 | 975 | 976 | 977 | 978 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 |
Test Group/Test caseCountPassFailErrorViewScreenshots
add_event_test.AddEventTest: 添加发布会 5230Detail 
test_add_event_all_null: 所有参数为空
436 | 437 | 438 | pass 439 | 451 | 452 |
test_add_event_data_type_error: 日期格式错误
459 | 460 | 461 | fail 462 | 478 | 479 |
test_add_event_eid_exist: id已经存在
486 | 487 | 488 | pass 489 | 501 | 502 |
test_add_event_name_exist: 名称已经存在
509 | 510 | 511 | fail 512 | 528 | 529 |
test_add_event_success: 添加成功
536 | 537 | 538 | fail 539 | 555 | 556 |
add_guest_test.AddGuessTest: 添加嘉宾 7340Detail 
test_add_guest_all_null: 参数为空
573 | 574 | 575 | pass 576 | 588 | 589 |
test_add_guest_eid_null: eid=901 查询为空
596 | 597 | 598 | pass 599 | 611 | 612 |
test_add_guest_limit_full: eid=2 发布会人数已满
619 | 620 | 621 | fail 622 | 638 | 639 |
test_add_guest_phone_repeat: phone=13800113001 手机号重复
646 | 647 | 648 | fail 649 | 665 | 666 |
test_add_guest_status_close: eid=2 状态未开启
673 | 674 | 675 | fail 676 | 692 | 693 |
test_add_guest_success: 添加成功
700 | 701 | 702 | fail 703 | 719 | 720 |
test_add_guest_time_start: eid=4 发布会已开始
727 | 728 | 729 | pass 730 | 742 | 743 |
get_event_list_test.GetEventListTest: 获得发布会列表 4220Detail 
test_get_event_list_eid_error: eid=901 查询结果为空
760 | 761 | 762 | pass 763 | 775 | 776 |
test_get_event_list_eid_success: 根据 eid 查询结果成功
783 | 784 | 785 | fail 786 | 805 | 806 |
test_get_event_list_nam_result_null: 关键字‘abc’查询
813 | 814 | 815 | pass 816 | 828 | 829 |
test_get_event_list_name_find: 关键字‘发布会’模糊查询
836 | 837 | 838 | fail 839 | 858 | 859 |
get_guest_list_test.GetGuestListTest: 获得嘉宾列表 5320Detail 
test_get_event_list_eid_error: 根据 eid 查询结果为空
876 | 877 | 878 | pass 879 | 891 | 892 |
test_get_event_list_eid_phone_null: 根据 eid 和phone 查询结果为空
899 | 900 | 901 | pass 902 | 914 | 915 |
test_get_event_list_eid_phone_success: 根据 eid 和phone 查询结果成功
922 | 923 | 924 | fail 925 | 941 | 942 |
test_get_event_list_eid_success: 根据 eid 查询结果成功
949 | 950 | 951 | fail 952 | 971 | 972 |
test_get_guest_list_eid_null: eid 参数为空
979 | 980 | 981 | pass 982 | 994 | 995 |
user_sign_test.UserSignTest: 用户签到 8350Detail 
test_user_sign_all_null: 参数为空
1012 | 1013 | 1014 | pass 1015 | 1027 | 1028 |
test_user_sign_eid_error: eid=901 查询结果不存在
1035 | 1036 | 1037 | pass 1038 | 1050 | 1051 |
test_user_sign_eid_phone_error: eid=1, phone=13511001102 手机号与发布会不匹配
1058 | 1059 | 1060 | fail 1061 | 1077 | 1078 |
test_user_sign_has_sign_in: 已签到
1085 | 1086 | 1087 | fail 1088 | 1104 | 1105 |
test_user_sign_phone_error: phone=10100001111 手机号不存在
1112 | 1113 | 1114 | fail 1115 | 1131 | 1132 |
test_user_sign_status_close: eid=3 发布会状态关闭
1139 | 1140 | 1141 | fail 1142 | 1158 | 1159 |
test_user_sign_success: 签到成功
1166 | 1167 | 1168 | fail 1169 | 1185 | 1186 |
test_user_sign_time_start: eid=4 发布会已开始
1193 | 1194 | 1195 | pass 1196 | 1208 | 1209 |
Total2913160  
1223 | 1224 |
 
1225 | 1226 | 1265 | 1266 | 1267 | 1268 | -------------------------------------------------------------------------------- /run_tests.py: -------------------------------------------------------------------------------- 1 | import time, sys 2 | sys.path.append('./interface') 3 | sys.path.append('./db_fixture') 4 | from HTMLTestRunner import HTMLTestRunner 5 | from unittest import defaultTestLoader 6 | from db_fixture import test_data 7 | 8 | 9 | # 指定测试用例为当前文件夹下的 interface 目录 10 | test_dir = './interface' 11 | testsuit = defaultTestLoader.discover(test_dir, pattern='*_test.py') 12 | 13 | 14 | if __name__ == "__main__": 15 | test_data.init_data() # 初始化接口测试数据 16 | 17 | now = time.strftime("%Y-%m-%d %H_%M_%S") 18 | filename = './report/' + now + '_result.html' 19 | fp = open(filename, 'wb') 20 | runner = HTMLTestRunner(stream=fp, 21 | title='发布会签到系统接口自动化测试', 22 | description='运行环境:MySQL(PyMySQL), Requests, unittest ') 23 | runner.run(testsuit, rerun=0, save_last_run=False) 24 | fp.close() 25 | --------------------------------------------------------------------------------