【Python+selenium】自动发邮件功能

自动发邮件功能也是自动化策划四项目的重要需求之一。例如,我们想在自动化脚本运行完成之后,邮箱就可以收到最新的测试报告结果。假设生成的测试报告与多人相关,每个人都去测试服务器查看就会比较麻烦,如果把这种自动的且不及时的查看变成被动且及时的查收,就方便多了。

SMTP(Simple Mail Transfer Protocol)是简单邮件传输协议,它是一组用于由于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。

Python的smtplib模块提供了一种很方便的途径用来发送电子邮件。它对SMTP协议进行了简单的封装。我们可以使用SMTP对象的sendmail方法发送邮件,通过help()查看SMTP所提供的方法如下。

输出结果:

Help on class SMTP in module smtplib:

class SMTP(builtins.object)
 |  This class manages a connection to an SMTP or ESMTP server.
 |  SMTP Objects:
 |      SMTP objects have the following attributes:
 |          helo_resp
 |              This is the message given by the server in response to the
 |              most recent HELO command.
 |  
 |          ehlo_resp
 |              This is the message given by the server in response to the
 |              most recent EHLO command. This is usually multiline.
 |  
 |          does_esmtp
 |              This is a True value _after you do an EHLO command_, if the
 |              server supports ESMTP.
 |  
 |          esmtp_features
 |              This is a dictionary, which, if the server supports ESMTP,
 |              will _after you do an EHLO command_, contain the names of the
 |              SMTP service extensions this server supports, and their
 |              parameters (if any).
 |  
 |              Note, all extension names are mapped to lower case in the
 |              dictionary.
 |  
 |      See each method's docstrings for details.  In general, there is a
 |      method of the same name to perform each SMTP command.  There is also a
 |      method called 'sendmail' that will do an entire mail transaction.
 |  
 |  Methods defined here:
 |  
 |  __enter__(self)
 |  
 |  __exit__(self, *args)
 |  
 |  __init__(self, host='', port=0, local_hostname=None, timeout=, source_address=None)
 |      Initialize a new instance.
 |      
 |      If specified, `host' is the name of the remote host to which to
 |      connect.  If specified, `port' specifies the port to which to connect.
 |      By default, smtplib.SMTP_PORT is used.  If a host is specified the
 |      connect method is called, and if it returns anything other than a
 |      success code an SMTPConnectError is raised.  If specified,
 |      `local_hostname` is used as the FQDN of the local host in the HELO/EHLO
 |      command.  Otherwise, the local hostname is found using
 |      socket.getfqdn(). The `source_address` parameter takes a 2-tuple (host,
 |      port) for the socket to bind to as its source address before
 |      connecting. If the host is '' and port is 0, the OS default behavior
 |      will be used.
 |  
 |  auth(self, mechanism, authobject, *, initial_response_ok=True)
 |      Authentication command - requires response processing.
 |      
 |      'mechanism' specifies which authentication mechanism is to
 |      be used - the valid values are those listed in the 'auth'
 |      element of 'esmtp_features'.
 |      
 |      'authobject' must be a callable object taking a single argument:
 |      
 |              data = authobject(challenge)
 |      
 |      It will be called to process the server's challenge response; the
 |      challenge argument it is passed will be a bytes.  It should return
 |      bytes data that will be base64 encoded and sent to the server.
 |      
 |      Keyword arguments:
 |          - initial_response_ok: Allow sending the RFC 4954 initial-response
 |            to the AUTH command, if the authentication methods supports it.
 |  
 |  auth_cram_md5(self, challenge=None)
 |      Authobject to use with CRAM-MD5 authentication. Requires self.user
 |      and self.password to be set.
 |  
 |  auth_login(self, challenge=None)
 |      Authobject to use with LOGIN authentication. Requires self.user and
 |      self.password to be set.
 |  
 |  auth_plain(self, challenge=None)
 |      Authobject to use with PLAIN authentication. Requires self.user and
 |      self.password to be set.
 |  
 |  close(self)
 |      Close the connection to the SMTP server.
 |  
 |  connect(self, host='localhost', port=0, source_address=None)
 |      Connect to a host on a given port.
 |      
 |      If the hostname ends with a colon (`:') followed by a number, and
 |      there is no port specified, that suffix will be stripped off and the
 |      number interpreted as the port number to use.
 |      
 |      Note: This method is automatically invoked by __init__, if a host is
 |      specified during instantiation.
 |  
 |  data(self, msg)
 |      SMTP 'DATA' command -- sends message data to server.
 |      
 |      Automatically quotes lines beginning with a period per rfc821.
 |      Raises SMTPDataError if there is an unexpected reply to the
 |      DATA command; the return value from this method is the final
 |      response code received when the all data is sent.  If msg
 |      is a string, lone '\r' and '\n' characters are converted to
 |      '\r\n' characters.  If msg is bytes, it is transmitted as is.
 |  
 |  docmd(self, cmd, args='')
 |      Send a command, and return its response code.
 |  
 |  ehlo(self, name='')
 |      SMTP 'ehlo' command.
 |      Hostname to send for this command defaults to the FQDN of the local
 |      host.
 |  
 |  ehlo_or_helo_if_needed(self)
 |      Call self.ehlo() and/or self.helo() if needed.
 |      
 |      If there has been no previous EHLO or HELO command this session, this
 |      method tries ESMTP EHLO first.
 |      
 |      This method may raise the following exceptions:
 |      
 |       SMTPHeloError            The server didn't reply properly to
 |                                the helo greeting.
 |  
 |  expn(self, address)
 |      SMTP 'expn' command -- expands a mailing list.
 |  
 |  getreply(self)
 |      Get a reply from the server.
 |      
 |      Returns a tuple consisting of:
 |      
 |        - server response code (e.g. '250', or such, if all goes well)
 |          Note: returns -1 if it can't read response code.
 |      
 |        - server response string corresponding to response code (multiline
 |          responses are converted to a single, multiline string).
 |      
 |      Raises SMTPServerDisconnected if end-of-file is reached.
 |  
 |  has_extn(self, opt)
 |      Does the server support a given SMTP service extension?
 |  
 |  helo(self, name='')
 |      SMTP 'helo' command.
 |      Hostname to send for this command defaults to the FQDN of the local
 |      host.
 |  
 |  help(self, args='')
 |      SMTP 'help' command.
 |      Returns help text from server.
 |  
 |  login(self, user, password, *, initial_response_ok=True)
 |      Log in on an SMTP server that requires authentication.
 |      
 |      The arguments are:
 |          - user:         The user name to authenticate with.
 |          - password:     The password for the authentication.
 |      
 |      Keyword arguments:
 |          - initial_response_ok: Allow sending the RFC 4954 initial-response
 |            to the AUTH command, if the authentication methods supports it.
 |      
 |      If there has been no previous EHLO or HELO command this session, this
 |      method tries ESMTP EHLO first.
 |      
 |      This method will return normally if the authentication was successful.
 |      
 |      This method may raise the following exceptions:
 |      
 |       SMTPHeloError            The server didn't reply properly to
 |                                the helo greeting.
 |       SMTPAuthenticationError  The server didn't accept the username/
 |                                password combination.
 |       SMTPNotSupportedError    The AUTH command is not supported by the
 |                                server.
 |       SMTPException            No suitable authentication method was
 |                                found.
 |  
 |  mail(self, sender, options=[])
 |      SMTP 'mail' command -- begins mail xfer session.
 |      
 |      This method may raise the following exceptions:
 |      
 |       SMTPNotSupportedError  The options parameter includes 'SMTPUTF8'
 |                              but the SMTPUTF8 extension is not supported by
 |                              the server.
 |  
 |  noop(self)
 |      SMTP 'noop' command -- doesn't do anything :>
 |  
 |  putcmd(self, cmd, args='')
 |      Send a command to the server.
 |  
 |  quit(self)
 |      Terminate the SMTP session.
 |  
 |  rcpt(self, recip, options=[])
 |      SMTP 'rcpt' command -- indicates 1 recipient for this mail.
 |  
 |  rset(self)
 |      SMTP 'rset' command -- resets session.
 |  
 |  send(self, s)
 |      Send `s' to the server.
 |  
 |  send_message(self, msg, from_addr=None, to_addrs=None, mail_options=[], rcpt_options={})
 |      Converts message to a bytestring and passes it to sendmail.
 |      
 |      The arguments are as for sendmail, except that msg is an
 |      email.message.Message object.  If from_addr is None or to_addrs is
 |      None, these arguments are taken from the headers of the Message as
 |      described in RFC 2822 (a ValueError is raised if there is more than
 |      one set of 'Resent-' headers).  Regardless of the values of from_addr and
 |      to_addr, any Bcc field (or Resent-Bcc field, when the Message is a
 |      resent) of the Message object won't be transmitted.  The Message
 |      object is then serialized using email.generator.BytesGenerator and
 |      sendmail is called to transmit the message.  If the sender or any of
 |      the recipient addresses contain non-ASCII and the server advertises the
 |      SMTPUTF8 capability, the policy is cloned with utf8 set to True for the
 |      serialization, and SMTPUTF8 and BODY=8BITMIME are asserted on the send.
 |      If the server does not support SMTPUTF8, an SMPTNotSupported error is
 |      raised.  Otherwise the generator is called without modifying the
 |      policy.
 |  
 |  sendmail(self, from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
 |      This command performs an entire mail transaction.
 |      
 |      The arguments are:
 |          - from_addr    : The address sending this mail.
 |          - to_addrs     : A list of addresses to send this mail to.  A bare
 |                           string will be treated as a list with 1 address.
 |          - msg          : The message to send.
 |          - mail_options : List of ESMTP options (such as 8bitmime) for the
 |                           mail command.
 |          - rcpt_options : List of ESMTP options (such as DSN commands) for
 |                           all the rcpt commands.
 |      
 |      msg may be a string containing characters in the ASCII range, or a byte
 |      string.  A string is encoded to bytes using the ascii codec, and lone
 |      \r and \n characters are converted to \r\n characters.
 |      
 |      If there has been no previous EHLO or HELO command this session, this
 |      method tries ESMTP EHLO first.  If the server does ESMTP, message size
 |      and each of the specified options will be passed to it.  If EHLO
 |      fails, HELO will be tried and ESMTP options suppressed.
 |      
 |      This method will return normally if the mail is accepted for at least
 |      one recipient.  It returns a dictionary, with one entry for each
 |      recipient that was refused.  Each entry contains a tuple of the SMTP
 |      error code and the accompanying error message sent by the server.
 |      
 |      This method may raise the following exceptions:
 |      
 |       SMTPHeloError          The server didn't reply properly to
 |                              the helo greeting.
 |       SMTPRecipientsRefused  The server rejected ALL recipients
 |                              (no mail was sent).
 |       SMTPSenderRefused      The server didn't accept the from_addr.
 |       SMTPDataError          The server replied with an unexpected
 |                              error code (other than a refusal of
 |                              a recipient).
 |       SMTPNotSupportedError  The mail_options parameter includes 'SMTPUTF8'
 |                              but the SMTPUTF8 extension is not supported by
 |                              the server.
 |      
 |      Note: the connection will be open even after an exception is raised.
 |      
 |      Example:
 |      
 |       >>> import smtplib
 |       >>> s=smtplib.SMTP("localhost")
 |       >>> tolist=["[email protected]","[email protected]","[email protected]","[email protected]"]
 |       >>> msg = '''\
 |       ... From: [email protected]
 |       ... Subject: testin'...
 |       ...
 |       ... This is a test '''
 |       >>> s.sendmail("[email protected]",tolist,msg)
 |       { "[email protected]" : ( 550 ,"User unknown" ) }
 |       >>> s.quit()
 |      
 |      In the above example, the message was accepted for delivery to three
 |      of the four addresses, and one was rejected, with the error code
 |      550.  If all addresses are accepted, then the method will return an
 |      empty dictionary.
 |  
 |  set_debuglevel(self, debuglevel)
 |      Set the debug output level.
 |      
 |      A non-false value results in debug messages for connection and for all
 |      messages sent to and received from the server.
 |  
 |  starttls(self, keyfile=None, certfile=None, context=None)
 |      Puts the connection to the SMTP server into TLS mode.
 |      
 |      If there has been no previous EHLO or HELO command this session, this
 |      method tries ESMTP EHLO first.
 |      
 |      If the server supports TLS, this will encrypt the rest of the SMTP
 |      session. If you provide the keyfile and certfile parameters,
 |      the identity of the SMTP server and client can be checked. This,
 |      however, depends on whether the socket module really checks the
 |      certificates.
 |      
 |      This method may raise the following exceptions:
 |      
 |       SMTPHeloError            The server didn't reply properly to
 |                                the helo greeting.
 |  
 |  verify(self, address)
 |      SMTP 'verify' command -- checks for address validity.
 |  
 |  vrfy = verify(self, address)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  debuglevel = 0
 |  
 |  default_port = 25
 |  
 |  does_esmtp = 0
 |  
 |  ehlo_msg = 'ehlo'
 |  
 |  ehlo_resp = None
 |  
 |  file = None
 |  
 |  helo_resp = None

None

Process finished with exit code 0
==========================================================================================

导入SMTP对象,通过help()查看对象的注释,从中找到sendmail()方法的使用说明。

connect(host,port)方法参数说明如下。

  • host:指定连接的邮箱服务器。
  • port:指定连接服务器的端口号。

login(user,password)方法参数说明如下。

  • user:登录邮箱用户用。
  • password:登录邮箱密码。

sendmail(from_addr,to_addrs,msg,..)方法参数说明如下。

  • from_addr:邮件发送者地址。
  • to_addrs:字符串列表,邮件发送地址。
  • msg:发送消息。

quit()方法:用于结束SMTP会话。

一般我们发邮件时有两种方式。方式一:自己邮箱的Web页面(如:mail.126.com),输入自己邮箱的用户名和密码登录,打开发邮件页面,填写对方的邮箱地址及邮件标题与正文,完成后单击发送。方式二:下载安装邮箱客户端(如Outlook、Foxmail等),填写邮箱帐号、密码及邮箱服务器(如:smtp.126.com),一般的邮箱客户端会默认记下这些信息,所以,这个过程只需填写一次,后面发邮件的过程与方法一相同。

而我们通过Python的SMTP对象发邮件则更像方式二,因为需要填写邮箱服务器。

当然,在具体发邮件时会涉及诸多需求,例如,邮件的正文的格式、是否带图片、邮件是否需要添加附件(及多附近)、邮件是否需要同时向多人发送等。

一、发送HTML格式的邮件

send_mail.py

import smtplib
from email.mime.text import MIMEText
from email.header import Header

#发送邮箱服务器
smtpserver='smtp.163.com'
#发送邮箱用户/密码
user='****@163.com'
password='*********'
#发送邮箱
sender='*****@163.com'
#接收邮箱
receiver='*****@163.com'
#发送邮件主题
subject='Python email test'
#编写HTML类型的邮件正文
msg=MIMEText('

你好!

','html','utf-8') msg['Subject']=Header(subject,'utf-8') #连接发送邮件 smtp=smtplib.SMTP() smtp.connect(smtpserver) smtp.login(user,password) smtp.sendmail(sender,receiver,msg.as_string()) smtp.quit()

本例中,除SMTP模块外,我们还用到了email模块,它主要用来定义邮件的标题和正文;Header()方法用来定义邮件标题;MIMEText()用于定义邮件正文,参数为html格式的文本。登录[email protected]邮箱,查看邮箱内容如图所示。

【Python+selenium】自动发邮件功能_第1张图片

二、发送带附件的邮件

在发送文件时,有时需要发送附件。下面的实例实现了带附件的邮件发送。

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

#发送邮箱服务器
smtpserver='smtp.163.com'
#发送邮箱
sender='******@163.com'
#接收邮箱
receiver='*********@163.com'
#发送邮箱用户/密码
user='********@163.com'
password='*******'
#发送邮件主题
subject='Python send email test'
#发送的附件
sendfile=open('D:\\下载包\\build.xml','rb').read()
att=MIMEText(sendfile,'base64','utf-8')
att["Content-Type"]='application/octet-stream'
att["Content-Disposition"]='attachment;filename="build.xml"'
msgRoot=MIMEMultipart('related')
msgRoot['Subject']=subject
msgRoot.attach(att)

#连接发送邮件
smtp=smtplib.SMTP()
smtp.connect(smtpserver)
smtp.login(user,password)
smtp.sendmail(sender,receiver,msgRoot.as_string())
smtp.quit()

相比上一个实例,通过MIMEMultipart()模块构造的带附件的邮件如下图。

【Python+selenium】自动发邮件功能_第2张图片

三、查找最新的测试报告

现在已经知道如何通过Python编写发邮件程序,但要想和自动化测试项目结合还需要解决一个问题,因为测试报告的名称是根据当前时间生成的,所以如何找到最新生成的测试报告是实现发邮件功能的关键。

import os
#定义文件目录
result_dir='C:\\Users\\Administrator\\PycharmProjects\\test2\\HTML测试报告'

lists=os.listdir(result_dir)
#重新按时间对应目录下的文件进行排序
lists.sort(key=lambda fn:os.path.getmtime(result_dir+"\\"+fn))

print(("最新的文件为:"+lists[-1]))
file=os.path.join(result_dir,lists[-1])
print(file)

首先定义测试报告的目录result_dir,os.listdir()可以获取目录下的所有文件及文件夹。利用sort()方法对目录下的文件夹按时间重新排序。list[-1]取到的就是最新生成的文件或文件夹。程序运行结果如下。

四、整合自动发邮件功能

解决了前面的问题后,现在就可以将自动发邮件功能集成到自动化测试项目中了。下面打开runtest.py文件重新进行编辑。

from HTMLTestRunner import HTMLTestRunner
from email.mime.text import MIMEText
from email.header import Header
import smtplib
import unittest
import time
import os
#===================定义发送邮件==================
def send_mail(file_new):
    f=open(file_new,'rb')
    mail_body=f.read()
    f.close()

    msg=MIMEText(mail_body,'html','utf-8')
    msg['Subject']=Header("自动化测试报告","utf-8")
    smtp=smtplib.SMTP()

    smtp.connect("smtp.163.com")
    smtp.login("******@163.com","*******")   #密码为授权码
    smtp.sendmail("******@163.com","******@163.com",msg.as_string())
    smtp.quit()
    print('email has send out!')
#===============查找测试报告目录,找到最新生成的测试报告文件============================
def new_report(testreport):
    lists=os.listdir(testreport)
    lists.sort(key=lambda fn:os.path.getmtime(testreport+"\\"+fn))
    file_new = os.path.join(testreport, lists[-1])
    print(file_new)
    return file_new
if __name__=='__main__':
    test_dir='C:\\Users\\Administrator\\PycharmProjects\\test2\\HTML测试报告\\test_case'
    test_report='C:\\Users\\Administrator\\PycharmProjects\\test2\\HTML测试报告'

    discover=unittest.defaultTestLoader.discover(test_dir,pattern='test_*.py')

    now=time.strftime("%Y-%m-%d %H_%M_%S")
    filename=test_report+"\\"+now+'result.html'
    fp=open(filename,'wb')
    runner = HTMLTestRunner(stream=fp,
                            title='百度搜索测试报告',
                            description='用例执行情况:')
    runner.run(discover)  # 运行测试用例
    fp.close()  # 关闭报告文件

    new_report=new_report(test_report)
    send_mail(new_report)  #发送测试报告

整个程序的执行过程可以分为三个步骤:

①通过unittest框架discover()找到匹配测试用例,由HTMLTestRunner的run()方法执行测试用例并生成最新的测试报告。

②调用new_report()函数找到测试报告目录(report)下最新生成的测试报告,返回测试报告的路径。

③将得到的最新测试报告的完整路径传给send_mail()函数,实现发邮件功能。

整个脚本执行完成后,打开接收邮箱,即可看到最新测试执行的测试报告。

【Python+selenium】自动发邮件功能_第3张图片

你可能感兴趣的:(Python+Selenium)