前言:

从前面五篇,对selenium大概也有了个理解,如何定位元素,如何操作元素,如何切换句柄。这时候就要做个小demo实战下了!

功能主要有:

1-PO模式,设计page、testcase、testsuit

2-打印log文件

3-生成测试报告

4-压缩测试报告,发送到邮箱

一、项目结构

二、pages讲解

pages是对要写自动化的页面进行分离,抽取出来定位元素,执行方法。以login为例子

打开页面--->进入登录页面--->选择账号登录---->输入框输入用户名密码--->点击登录

1 #!/usr/bin/env python

2 # -*- coding: utf-8 -*-

3 # @Time : 2019-08-05 14:44

4 # @Author : zhangxue

5 # @File : LoginPage.py

6 # @Desc : 登录页面

7

8 import time

9 from selenium.webdriver.common.by import By

10 from config.url import *

11 from config.config import *

12 from selenium import webdriver

13 from pages.BasePage import BasePage

14

15 class LoginPage(BasePage):

16 '''用户登录页面'''

17 # 定位器,通过元素属性定位元素对象

18 login_loc = (By.XPATH, "//a[@class='header_link console']") #首页控制台链接

19 account_loc = (By.XPATH, "//div[@class='ant-tabs-nav ant-tabs-nav-animated']//div[2]//span") #账号登录tab选项

20 username_loc = (By.XPATH, "//input[@id='username_or_email']") #用户名/邮箱/手机号input

21 password_loc = (By.XPATH, "//input[@id='password']")

22 submit_loc = (By.XPATH, "//div[@class='ant-tabs-tabpane ant-tabs-tabpane-active']//button[@class='ant-btn login-submit-btn ant-btn-primary ant-btn-lg']//span") #登录按钮

23

24 login_url = baseurl_console + "login" # 登录链接

25 console_dashboardUrl = baseurl_console + "dashboard" # 登录状态的url

26

27 """打开官网首页"""

28 def openPage(self):

29 self.open(baseurl_guanwang, title_guanwang)

30

31 """进入登录页面"""

32 def click_console(self):

33 self.find_element(*self.login_loc).click()

34 assert self.assert_page_title(title_console), u"title不正确! %s" % title_console

35 assert self.assert_page_url(self.login_url), "url地址不对! %s" % self.login_url

36

37 """选择账号登录tab"""

38 def click_accountLogin(self):

39 self.find_element(*self.account_loc).click()

40

41 # 输入用户名

42 def input_username(self, username):

43 self.find_element(*self.username_loc).clear()

44 self.find_element(*self.username_loc).click()

45 self.find_element(*self.username_loc).send_keys(username)

46

47 # 输入密码

48 def input_password(self, password):

49 self.find_element(*self.password_loc).clear()

50 self.find_element(*self.password_loc).click()

51 self.find_element(*self.password_loc).send_keys(password)

52

53 # 点击登录

54 def click_submit(self):

55 self.find_element(*self.submit_loc).click()

56

57 #统一登录入口,方便其他地方调用

58 def user_login(self, username, password):

59 self.openPage()

60 self.click_console()

61 time.sleep(3)

62

63 nowhandle = self.driver.current_window_handle # 在这里得到当前窗口句柄

64 allhandles = self.driver.window_handles # 获取所有窗口句柄

65 for handle in allhandles: # 在所有窗口中查找弹出窗口

66 if handle != nowhandle:

67 self.driver.switch_to.window(handle) # 这两步是在弹出窗口中进行的操作,证明我们确实进入了

68

69 self.click_accountLogin()

70 time.sleep(1)

71 self.input_username(username)

72 self.input_password(password)

73 time.sleep(1)

74 self.click_submit()

75

76 # 登录成功后比对url和title

77 assert self.assert_page_title(title_console), u"title不正确! %s" % title_console

78 assert self.assert_page_url(self.console_dashboardUrl), "url地址不对! %s" % self.console_dashboardUrl

79

80 time.sleep(2)

81

82 self.driver.get_screenshot_as_file(basedir + '/report/screen/登录.png')

83

84

85

86 # if __name__ == "__main__":

87 # driver = webdriver.Chrome()

88 # login = LoginPage(driver)

89 # login.openPage()

90 # login.click_console()

91 # login.click_accountLogin()

三、testcase讲解

testcase就是对pages对应的方法组装,进行case设计,因为login页面没写太多case,就以一个为例子

1 #!/usr/bin/env python

2 # -*- coding: utf-8 -*-

3 # @Time : 2019-08-05 15:31

4 # @Author : zhangxue

5 # @File : test_login.py

6 # @Desc : 登录校验

7

8 import unittest

9 import time

10 from selenium import webdriver

11 from config.url import *

12 from lib.publicMethod.Log import logger

13 from pages.LoginPage import *

14 from config.config import *

15

16 class CaseLogin(unittest.TestCase):

17 """控制台登录"""

18

19 @classmethod

20 def setUpClass(cls):

21 logger.info("##########登录测试 开始##############")

22 cls.driver = webdriver.Chrome()

23 cls.username = username

24 cls.password = password

25 cls.loginPage = LoginPage(cls.driver)

26

27 # 用例执行体

28 def test_login(self):

29 self.loginPage.user_login(self.username, self.password)

30

31

32 @classmethod

33 def tearDownClass(cls):

34 cls.driver.quit()

35 logger.info("##########登录测试 结束##############")

36

37

38 if __name__ == "__main__":

39 unittest.main()

四、testsuite讲解

testsuite是对testcase进行组装,统一的一个执行入口。testsuite有多种组合方法,可以详细的去了解下,代码的注释中有几种的讲解

1 #!/usr/bin/env python

2 # -*- coding: utf-8 -*-

3

4 import unittest

5 import os

6

7 from lib.publicMethod.HTMLTestRunner_PY3 import HTMLTestRunner

8 import time

9 from lib.publicMethod import Send_email

10

11 if __name__ == '__main__':

12 # 生成报告文件的参数

13 basedir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

14 print(basedir)

15 # discover这个方法可以递归的去识别路径下所有符合pattern的文件,将这些文件加入套件

16 suite = unittest.defaultTestLoader.discover(basedir+'/test/testcase/', pattern='*.py')

17

18 # 1、用TestSuite的addTests()方法,将测试用例组装成测试套件,不用TestLoader时,传入的是case里面的方法名。用TestLoader时传入类的类名

19 # addTest传入单个的Testcase方法,addTests传入Testcase方法数组

20 # suite.addTest(TestMathFunc('test_add'))

21 # tests = [TestMathFunc('test_add'),TestMathFunc('test_minus'),TestMathFunc('test_divide')]

22 # suite.addTests(tests)

23

24 # 2、使用addTests+TestLoader传入测试用例,但是TestLoader无法对case排序

25

26 # 2.1 loadTestsFromName传入‘模块名.TestCase名’(文件名.里面的类名)

27 # suite.addTests(unittest.TestLoader().loadTestsFromName('test_mathfunc.TestMathFunc'))

28 # # 2.2 loadTestsFromNames就是传入列表即有多个testcase时,依次传入文件

29 # suite.addTests(unittest.TestLoader().loadTestsFromNames(['test_mathfunc.TestMathFunc']))

30 # 2.3 loadTestsFromTestCase传入TestCase名,(testcae中文件里面的类名)

31 # suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))

32 # suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TwoMathFun))

33 # suite.addTests(unittest.TestLoader().loadTestsFromTestCase(Add))

34

35 # suite.addTest(unittest.TestLoader().loadTestsFromTestCase(UntitledTestCase))

36 # 运行suite,这样写是将结果输出到控制台

37 # verbosity参数可以控制输出的错误报告的详细程度,默认是1,

38 # 如果设为0,则不输出每一用例的执行结果,即没有上面的结果中的第1行;

39 # 如果设为2,则输出详细的执行结果

40 # runner = unittest.TextTestRunner(verbosity=2)

41 # runner.run(suite)

42

43 #运行suite,并将结果输出到Html报告里

44 #生成报告文件的参数

45 report_title = 'facepp-UI自动化测试'

46 desc = '饼图统计测试执行情况'

47 report_file = basedir+'/report/faceppUI.html'

48 with open(report_file, 'wb') as report:

49 runner = HTMLTestRunner(stream=report, title=report_title, description=desc)

50 runner.run(suite)

51

52

53 # 发送报告到邮箱

54 time.sleep(1)

55 Send_email.cr_zip('TestReport.zip',basedir + '/report/')

56 Send_email.send_mail_report("facepp-UI自动化测试!!!")

五、生成html报告

忽略我的报错,因为我现在的电脑上没有安装浏览器的驱动

大概就是这个样子,饼图会将通过的失败的错误的。下面的表格会展示出所有的case,详情里会将错误信息展示出来

六、发送到邮箱

执行成功后,会发送到邮箱一封邮件,发送人、收件人都是可以设置的。邮件内容为测试结果,html报告打包后的压缩文件,可以下载后,打开查看html报告

1 # 发送报告到邮箱

2 time.sleep(1)

3 Send_email.cr_zip('TestReport.zip',basedir + '/report/')

4 Send_email.send_mail_report("facepp-UI自动化测试!!!")

七、生成日志

日志格式如上,年月日时分秒,然后info、error等信息

使用时,引入logger类,然后logger.info("##########登录测试 开始##############")即可

八、生成html报告源码|发送到邮箱邮件源码 |日志源码

 

生成html报告

1 # -*- coding: utf-8 -*-

2 """

3 A TestRunner for use with the Python unit testing framework. It

4 generates a HTML report to show the result at a glance.

5

6 The simplest way to use this is to invoke its main method. E.g.

7

8 import unittest

9 import HTMLTestRunner

10

11 ... define your tests ...

12

13 if __name__ == '__main__':

14 HTMLTestRunner.main()

15

16

17 For more customization options, instantiates a HTMLTestRunner object.

18 HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.

19

20 # output to a file

21 fp = file('my_report.html', 'wb')

22 runner = HTMLTestRunner.HTMLTestRunner(

23 stream=fp,

24 title='My unit test',

25 description='This demonstrates the report output by HTMLTestRunner.'

26 )

27

28 # Use an external stylesheet.

29 # See the Template_mixin class for more customizable options

30 runner.STYLESHEET_TMPL = ''

31

32 # run the test

33 runner.run(my_test_suite)

34

35

36 ------------------------------------------------------------------------

37 Copyright (c) 2004-2007, Wai Yip Tung

38 All rights reserved.

39

40 Redistribution and use in source and binary forms, with or without

41 modification, are permitted provided that the following conditions are

42 met:

43

44 * Redistributions of source code must retain the above copyright notice,

45 this list of conditions and the following disclaimer.

46 * Redistributions in binary form must reproduce the above copyright

47 notice, this list of conditions and the following disclaimer in the

48 documentation and/or other materials provided with the distribution.

49 * Neither the name Wai Yip Tung nor the names of its contributors may be

50 used to endorse or promote products derived from this software without

51 specific prior written permission.

52

53 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS

54 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED

55 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A

56 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER

57 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,

58 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,

59 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR

60 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF

61 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING

62 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

63 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

64 """

65

66 # URL: http://tungwaiyip.info/software/HTMLTestRunner.html

67

68 __author__ = "Wai Yip Tung"

69 __version__ = "0.9.1"

70

71 """

72 Change History

73 Version 0.9.1

74 * 用Echarts添加执行情况统计图 (灰蓝)

75

76 Version 0.9.0

77 * 改成Python 3.x (灰蓝)

78

79 Version 0.8.3

80 * 使用 Bootstrap稍加美化 (灰蓝)

81 * 改为中文 (灰蓝)

82

83 Version 0.8.2

84 * Show output inline instead of popup window (Viorel Lupu).

85

86 Version in 0.8.1

87 * Validated XHTML (Wolfgang Borgert).

88 * Added description of test classes and test cases.

89

90 Version in 0.8.0

91 * Define Template_mixin class for customization.

92 * Workaround a IE 6 bug that it does not treat

205

206

207 %(stylesheet)s

208

209

210

211

305

306

307 %(heading)s

308 %(report)s

309 %(ending)s

310 %(chart_script)s

311

312

313

314 """ # variables: (title, generator, stylesheet, heading, report, ending, chart_script)

315

316 ECHARTS_SCRIPT = """

317

362 """ # variables: (Pass, fail, error)

363

364 # ------------------------------------------------------------------------

365 # Stylesheet

366 #

367 # alternatively use a for external style sheet, e.g.

368 #

369

370 STYLESHEET_TMPL = """

371

457 """

458

459 # ------------------------------------------------------------------------

460 # Heading

461 #

462

463 HEADING_TMPL = """

464

468

%(description)s

469

470 """ # variables: (title, parameters, description)

471

472 HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

473 """ # variables: (name, value)

474

475 # ------------------------------------------------------------------------

476 # Report

477 #

478

479 REPORT_TMPL = u"""

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503 %(test_list)s

504

505

506

507

508

509

510

511

512

测试套件/测试用例总数通过失败错误查看
总计%(count)s%(Pass)s%(fail)s%(error)s 

513 """ # variables: (test_list, count, Pass, fail, error)

514

515 REPORT_CLASS_TMPL = u"""

516

517 %(desc)s

518 %(count)s

519 %(Pass)s

520 %(fail)s

521 %(error)s

522 详情

523

524 """ # variables: (style, desc, count, Pass, fail, error, cid)

525

526 REPORT_TEST_WITH_OUTPUT_TMPL = r"""

527

528

%(desc)s

529

530

531

532

533 %(status)s

534

535

538

539

540

541

542 """ # variables: (tid, Class, style, desc, status)

543

544 REPORT_TEST_NO_OUTPUT_TMPL = r"""

545

546

%(desc)s

547 %(status)s

548

549 """ # variables: (tid, Class, style, desc, status)

550

551 REPORT_TEST_OUTPUT_TMPL = r"""%(id)s: %(output)s""" # variables: (id, output)

552

553 # ------------------------------------------------------------------------

554 # ENDING

555 #

556

557 ENDING_TMPL = """

 
"""

558

559 # -------------------- The end of the Template class -------------------

560

561

562 TestResult = unittest.TestResult

563

564

565 class _TestResult(TestResult):

566 # note: _TestResult is a pure representation of results.

567 # It lacks the output and reporting ability compares to unittest._TextTestResult.

568

569 def __init__(self, verbosity=1):

570 TestResult.__init__(self)

571 self.stdout0 = None

572 self.stderr0 = None

573 self.success_count = 0

574 self.failure_count = 0

575 self.error_count = 0

576 self.verbosity = verbosity

577

578 # result is a list of result in 4 tuple

579 # (

580 # result code (0: success; 1: fail; 2: error),

581 # TestCase object,

582 # Test output (byte string),

583 # stack trace,

584 # )

585 self.result = []

586 self.subtestlist = []

587

588 def startTest(self, test):

589 TestResult.startTest(self, test)

590 # just one buffer for both stdout and stderr

591 self.outputBuffer = io.StringIO()

592 stdout_redirector.fp = self.outputBuffer

593 stderr_redirector.fp = self.outputBuffer

594 self.stdout0 = sys.stdout

595 self.stderr0 = sys.stderr

596 sys.stdout = stdout_redirector

597 sys.stderr = stderr_redirector

598

599 def complete_output(self):

600 """

601 Disconnect output redirection and return buffer.

602 Safe to call multiple times.

603 """

604 if self.stdout0:

605 sys.stdout = self.stdout0

606 sys.stderr = self.stderr0

607 self.stdout0 = None

608 self.stderr0 = None

609 return self.outputBuffer.getvalue()

610

611 def stopTest(self, test):

612 # Usually one of addSuccess, addError or addFailure would have been called.

613 # But there are some path in unittest that would bypass this.

614 # We must disconnect stdout in stopTest(), which is guaranteed to be called.

615 self.complete_output()

616

617 def addSuccess(self, test):

618 if test not in self.subtestlist:

619 self.success_count += 1

620 TestResult.addSuccess(self, test)

621 output = self.complete_output()

622 self.result.append((0, test, output, ''))

623 if self.verbosity > 1:

624 sys.stderr.write('ok ')

625 sys.stderr.write(str(test))

626 sys.stderr.write('\n')

627 else:

628 sys.stderr.write('.')

629

630 def addError(self, test, err):

631 self.error_count += 1

632 TestResult.addError(self, test, err)

633 _, _exc_str = self.errors[-1]

634 output = self.complete_output()

635 self.result.append((2, test, output, _exc_str))

636 if self.verbosity > 1:

637 sys.stderr.write('E ')

638 sys.stderr.write(str(test))

639 sys.stderr.write('\n')

640 else:

641 sys.stderr.write('E')

642

643 def addFailure(self, test, err):

644 self.failure_count += 1

645 TestResult.addFailure(self, test, err)

646 _, _exc_str = self.failures[-1]

647 output = self.complete_output()

648 self.result.append((1, test, output, _exc_str))

649 if self.verbosity > 1:

650 sys.stderr.write('F ')

651 sys.stderr.write(str(test))

652 sys.stderr.write('\n')

653 else:

654 sys.stderr.write('F')

655

656 def addSubTest(self, test, subtest, err):

657 if err is not None:

658 if getattr(self, 'failfast', False):

659 self.stop()

660 if issubclass(err[0], test.failureException):

661 self.failure_count += 1

662 errors = self.failures

663 errors.append((subtest, self._exc_info_to_string(err, subtest)))

664 output = self.complete_output()

665 self.result.append((1, test, output + '\nSubTestCase Failed:\n' + str(subtest),

666 self._exc_info_to_string(err, subtest)))

667 if self.verbosity > 1:

668 sys.stderr.write('F ')

669 sys.stderr.write(str(subtest))

670 sys.stderr.write('\n')

671 else:

672 sys.stderr.write('F')

673 else:

674 self.error_count += 1

675 errors = self.errors

676 errors.append((subtest, self._exc_info_to_string(err, subtest)))

677 output = self.complete_output()

678 self.result.append(

679 (2, test, output + '\nSubTestCase Error:\n' + str(subtest), self._exc_info_to_string(err, subtest)))

680 if self.verbosity > 1:

681 sys.stderr.write('E ')

682 sys.stderr.write(str(subtest))

683 sys.stderr.write('\n')

684 else:

685 sys.stderr.write('E')

686 self._mirrorOutput = True

687 else:

688 self.subtestlist.append(subtest)

689 self.subtestlist.append(test)

690 self.success_count += 1

691 output = self.complete_output()

692 self.result.append((0, test, output + '\nSubTestCase Pass:\n' + str(subtest), ''))

693 if self.verbosity > 1:

694 sys.stderr.write('ok ')

695 sys.stderr.write(str(subtest))

696 sys.stderr.write('\n')

697 else:

698 sys.stderr.write('.')

699

700

701 class HTMLTestRunner(Template_mixin):

702

703 def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):

704 self.stream = stream

705 self.verbosity = verbosity

706 if title is None:

707 self.title = self.DEFAULT_TITLE

708 else:

709 self.title = title

710 if description is None:

711 self.description = self.DEFAULT_DESCRIPTION

712 else:

713 self.description = description

714

715 self.startTime = datetime.datetime.now()

716

717 def run(self, test):

718 "Run the given test case or test suite."

719 result = _TestResult(self.verbosity)

720 test(result)

721 self.stopTime = datetime.datetime.now()

722 self.generateReport(test, result)

723 print('\nTime Elapsed: %s' % (self.stopTime-self.startTime), file=sys.stderr)

724 return result

725

726 def sortResult(self, result_list):

727 # unittest does not seems to run in any particular order.

728 # Here at least we want to group them together by class.

729 rmap = {}

730 classes = []

731 for n,t,o,e in result_list:

732 cls = t.__class__

733 if cls not in rmap:

734 rmap[cls] = []

735 classes.append(cls)

736 rmap[cls].append((n,t,o,e))

737 r = [(cls, rmap[cls]) for cls in classes]

738 return r

739

740 def getReportAttributes(self, result):

741 """

742 Return report attributes as a list of (name, value).

743 Override this to add custom attributes.

744 """

745 startTime = str(self.startTime)[:19]

746 duration = str(self.stopTime - self.startTime)

747 status = []

748 if result.success_count: status.append(u'通过 %s' % result.success_count)

749 if result.failure_count: status.append(u'失败 %s' % result.failure_count)

750 if result.error_count: status.append(u'错误 %s' % result.error_count )

751 if status:

752 status = ' '.join(status)

753 else:

754 status = 'none'

755 return [

756 (u'开始时间', startTime),

757 (u'运行时长', duration),

758 (u'状态', status),

759 ]

760

761 def generateReport(self, test, result):

762 report_attrs = self.getReportAttributes(result)

763 generator = 'HTMLTestRunner %s' % __version__

764 stylesheet = self._generate_stylesheet()

765 heading = self._generate_heading(report_attrs)

766 report = self._generate_report(result)

767 ending = self._generate_ending()

768 chart = self._generate_chart(result)

769 output = self.HTML_TMPL % dict(

770 title = saxutils.escape(self.title),

771 generator = generator,

772 stylesheet = stylesheet,

773 heading = heading,

774 report = report,

775 ending = ending,

776 chart_script = chart

777 )

778 self.stream.write(output.encode('utf8'))

779

780 def _generate_stylesheet(self):

781 return self.STYLESHEET_TMPL

782

783 def _generate_heading(self, report_attrs):

784 a_lines = []

785 for name, value in report_attrs:

786 line = self.HEADING_ATTRIBUTE_TMPL % dict(

787 name = saxutils.escape(name),

788 value = saxutils.escape(value),

789 )

790 a_lines.append(line)

791 heading = self.HEADING_TMPL % dict(

792 title = saxutils.escape(self.title),

793 parameters = ''.join(a_lines),

794 description = saxutils.escape(self.description),

795 )

796 return heading

797

798 def _generate_report(self, result):

799 rows = []

800 sortedResult = self.sortResult(result.result)

801 for cid, (cls, cls_results) in enumerate(sortedResult):

802 # subtotal for a class

803 np = nf = ne = 0

804 for n,t,o,e in cls_results:

805 if n == 0: np += 1

806 elif n == 1: nf += 1

807 else: ne += 1

808

809 # format class description

810 if cls.__module__ == "__main__":

811 name = cls.__name__

812 else:

813 name = "%s.%s" % (cls.__module__, cls.__name__)

814 doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""

815 desc = doc and '%s: %s' % (name, doc) or name

816

817 row = self.REPORT_CLASS_TMPL % dict(

818 style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',

819 desc = desc,

820 count = np+nf+ne,

821 Pass = np,

822 fail = nf,

823 error = ne,

824 cid = 'c%s' % (cid+1),

825 )

826 rows.append(row)

827

828 for tid, (n,t,o,e) in enumerate(cls_results):

829 self._generate_report_test(rows, cid, tid, n, t, o, e)

830

831 report = self.REPORT_TMPL % dict(

832 test_list = ''.join(rows),

833 count = str(result.success_count+result.failure_count+result.error_count),

834 Pass = str(result.success_count),

835 fail = str(result.failure_count),

836 error = str(result.error_count),

837 )

838 return report

839

840 def _generate_chart(self, result):

841 chart = self.ECHARTS_SCRIPT % dict(

842 Pass=str(result.success_count),

843 fail=str(result.failure_count),

844 error=str(result.error_count),

845 )

846 return chart

847

848 def _generate_report_test(self, rows, cid, tid, n, t, o, e):

849 # e.g. 'pt1.1', 'ft1.1', etc

850 has_output = bool(o or e)

851 tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)

852 name = t.id().split('.')[-1]

853 doc = t.shortDescription() or ""

854 desc = doc and ('%s: %s' % (name, doc)) or name

855 tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL

856

857 script = self.REPORT_TEST_OUTPUT_TMPL % dict(

858 id=tid,

859 output=saxutils.escape(o+e),

860 )

861

862 row = tmpl % dict(

863 tid=tid,

864 Class=(n == 0 and 'hiddenRow' or 'none'),

865 style=(n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none')),

866 desc=desc,

867 script=script,

868 status=self.STATUS[n],

869 )

870 rows.append(row)

871 if not has_output:

872 return

873

874 def _generate_ending(self):

875 return self.ENDING_TMPL

876

877

878 ##############################################################################

879 # Facilities for running tests from the command line

880 ##############################################################################

881

882 # Note: Reuse unittest.TestProgram to launch test. In the future we may

883 # build our own launcher to support more specific command line

884 # parameters like test title, CSS, etc.

885 class TestProgram(unittest.TestProgram):

886 """

887 A variation of the unittest.TestProgram. Please refer to the base

888 class for command line parameters.

889 """

890 def runTests(self):

891 # Pick HTMLTestRunner as the default test runner.

892 # base class's testRunner parameter is not useful because it means

893 # we have to instantiate HTMLTestRunner before we know self.verbosity.

894 if self.testRunner is None:

895 self.testRunner = HTMLTestRunner(verbosity=self.verbosity)

896 unittest.TestProgram.runTests(self)

897

898 main = TestProgram

899

900 ##############################################################################

901 # Executing this module from the command line

902 ##############################################################################

903

904 if __name__ == "__main__":

905 main(module=None)

View Code

发送到邮箱邮件

1 # -*- coding: utf-8 -*-

2 import smtplib

3 import zipfile

4 from email.mime.text import MIMEText #发送纯文本信息

5 from email.mime.multipart import MIMEMultipart #发送带附件的信息

6 from email.header import Header #导入配置库

7 from config.config import basedir

8 import sys

9 from config import config

10 import os

11

12 # 1、压缩文件

13 def cr_zip(outputName, inputPath):

14 # 将inputPath路径下的文件压缩成名字为outputName的文件,放到outputpath目录下

15

16 outputpath = inputPath + outputName

17 filelist = []

18 isfp = os.path.basename(inputPath)

19 if isfp:

20 print('%s is not path' % inputPath)

21 sys.exit(0)

22 else:

23 for root, subdirs, files in os.walk(inputPath):

24 for file in files:

25 filelist.append(os.path.join(root, file))

26

27 # 参数1,压缩后的文件夹路径加名字(如果只加name的话,会压缩到调用这个方法的时候的文件路径下);

28 # 参数2,'r' ----- 打开一个存在的只读ZIP文件'w' ----- 清空并打开一个只写的zip文件,或创建一个只写的ZIP文件'a' ----- 表示打开一个文件,并添加内容

29 # 参数3,压缩格式 ,可选的压缩格式只有2个:ZIP_STORE、ZIP_DEFLATED。ZIP_STORE是默认的,表示不压缩。ZIP_DEFLATED表示压缩

30 zf = zipfile.ZipFile(outputpath, 'w', zipfile.ZIP_DEFLATED)

31 for f in filelist:

32 zf.write(f)

33 zf.close()

34

35 # 2、发送邮件

36 def send_mail_report(title):

37 """1、获取测试报告邮件服务器、发件人、收件人、发件人账号密码等信息"""

38 sender = config.sender #发件人

39 receiver = config.receiver #收件人

40 #第三方SMTP服务

41 server = config.server #设置服务器

42 username = config.emailusername #用户名

43 password = config.emailpassword #口令

44 port = config.smtp_server_port

45

46 """2、获取最新测试报告"""

47 reportPath=config.basedir+"/report/"

48 newReport = ""

49 for root, subdirs, files in os.walk(reportPath):

50 for file in files:

51 if os.path.splitext(file)[1] == ".html": # 判断该目录下的文件扩展名是否为html

52 newReport=file

53

54 """2.1调用cr_zip()方法,将测试报告压缩一下。"""

55 cr_zip('TestReport.zip', basedir + '/report/')

56

57 """3、生成邮件的内容"""

58 msg = MIMEMultipart() #MIMEMultipart(),创建一个带附件的实例

59 msg["subject"] = title #"""邮件需要三个头部信息: From, To, 和 Subject"""

60 msg["from"] = Header(config.sender,'utf-8')

61 msg["to"] = Header(",".join(config.receiver),'utf-8')

62 with open(os.path.join(reportPath,newReport), 'rb') as f:

63 mailbody = f.read()

64 html = MIMEText(mailbody, _subtype='html', _charset='utf-8')

65 msg.attach(html)

66

67 """4、将测试报告压缩文件添加到邮件附件"""

68

69 att = MIMEText(open(basedir + '/report/' + 'TestReport.zip', 'rb').read(), 'base64', 'utf-8')

70 att["Content-Type"] = 'application/octet-stream' #application/octet-stream : 二进制流数据(如常见的文件下载)

71 att.add_header("Content-Disposition", "attachment", filename="TestReport.zip") #filename为附件名

72 msg.attach(att)

73

74 """5、发送邮件"""

75 """5、发送邮件"""

76 try:

77 s = smtplib.SMTP(server, port) # port=25 为 SMTP 端口号

78 s.ehlo() # 向Gamil发送SMTP 'ehlo' 命令

79 s.starttls()

80 """s.set_debuglevel(1)认证"""

81 s.login(username, password)

82 """发送邮件"""

83 s.sendmail(sender, receiver, msg.as_string())

84 s.quit()

85 print("邮件发送成功")

86 except smtplib.SMTPException as e:

87 print(e)

88 print("Error :无法发送邮件")

89

90

91 if __name__ =='__main__':

92 print(basedir+'/report/')

93 send_mail_report("接口测试报告")

View Code

日志文件

1 # coding=utf-8

2 import logging

3 import time

4 import os

5 from config.config import basedir

6 # proj_path = os.path.dirname(os.path.dirname(__file__))

7

8

9 # os.path.join(a,b)注:第一个绝对路径之前的参数将被忽略

10

11 log_path = basedir +"/log/"

12 logname = os.path.join(log_path, '{0}.log'.format(time.strftime('%Y-%m-%d--%H_%M_%S')))

13

14 class Log:

15 def __printconsole(self, level, message):

16 """创建一个logger"""

17 logger = logging.getLogger()

18 logger.setLevel(logging.DEBUG)

19 """创建一个handler,用于写入日志文件"""

20 fh = logging.FileHandler(logname, 'a', encoding='utf-8')

21 fh.setLevel(logging.DEBUG)

22 """再创建一个handler,用于输出到控制台"""

23 ch = logging.StreamHandler()

24 ch.setLevel(logging.DEBUG)

25 """定义handler的输出格式"""

26 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

27 fh.setFormatter(formatter)

28 ch.setFormatter(formatter)

29 """给logger添加handler"""

30 logger.addHandler(fh)

31 logger.addHandler(ch)

32 """记录一条日志"""

33 if level == 'info':

34 logger.info(message)

35 elif level == 'debug':

36 logger.debug(message)

37 elif level == 'warning':

38 logger.warning(message)

39 elif level == 'error':

40 logger.error(message)

41 logger.removeHandler(ch)

42 logger.removeHandler(fh)

43 """关闭打开的文件"""

44 fh.close()

45

46 def debug(self, message):

47 self.__printconsole('debug', message)

48

49 def info(self, message):

50 self.__printconsole('info', message)

51

52 def warning(self, message):

53 self.__printconsole('warning', message)

54

55 def error(self, message):

56 self.__printconsole('error', message)

57

58 logger = Log()

View Code

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: