研究多线程问题也是因为当时自动发送报告邮件导致的,那会还没找到解决方案就一直想着如果启用多进程运行呢,结果就遇到了个新的问题,就是本文的来源。
先贴个书本讲解多进程运行测试用例的源代码:
1 # coding=utf-8 2 import unittest,os,time,multiprocessing 3 import commands 4 from email.mime.text import MIMEText 5 import HTMLTestRunner 6 import sys 7 sys.path.append('..\selenium_process') 8 9 10 def EEEcreatsuite1():11 casedir=[]12 listaa=os.listdir('..\\selenium_process')13 for xx in listaa:14 if 'thread' in xx:15 casedir.append(xx)16 print casedir17 18 suite=[]19 for n in casedir:20 testunit=unittest.TestSuite()21 discover=unittest.defaultTestLoader.discover(str(n),pattern='start_*.py',top_level_dir=r'..\\..\\test_unittest')22 for test_suite in discover:23 for test_case in test_suite:24 testunit.addTest(test_case)25 print testunit26 suite.append(testunit)27 28 return suite,casedir29 30 31 def EEEEEmutiRunCase(suite,casedir):32 now=time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time()))33 #把当前时间加到报告中34 filename="..\\report\\"+now+'result.html'35 fp=file(filename,'wb')36 37 proclist=[]38 s=039 for i in suite:40 runner=HTMLTestRunner.HTMLTestRunner(41 stream=fp,42 title=str(casedir[s])+u'测试报告',43 description=u'用例执行情况:'44 )45 proc=multiprocessing.Process(target=runner.run,args=(i,)) 46 proclist.append(proc)47 s=s+148 print proclist49 50 for p in proclist: p.start()51 for p in proclist: p.join()52 fp.close()53 54 55 if __name__=='__main__':56 runtmp=EEEcreatsuite1()57 EEEEEmutiRunCase(runtmp[0],runtmp[1])
上面作者书本没有提示运行环境,由于书本例子代码基本除了特殊说明都是windows上运行的。于是我照着敲完后就运行报错,一直都是:ValueError: I/O operation on closed file。
我由于学习才入门,不明白这个报错到底为何,因为我检查代码确保没有敲错,照着作者的代码我敢肯定100%一致。
我研究了一整天就没整出个所以然来,后来百度搜索看到windows和linux多进程机制的区别,如下:
具体思路跟把文件输出集中在一起也差不多,就是把进程需要写入文件的内容作为返回值返回给惠和的回调函数,使用回调函数向文件中写入内容。这样做在windows下面还有一个好处,在windows环境下,python的多进程没有像linux环境下的多进程一样,linux环境下的multiprocessing库是基于fork函数,父进程fork了一个子进程之后会把自己的资源,比如文件句柄都传递给子进程。但是在windows环境下没有fork函数,所以如果你在父进程里打开了一个文件,在子进程中写入,会出现ValueError: I/O operation on closed file
这样的错误,而且在windows环境下最好加入if __name__ == '__main__'
这样的判断,以避免一些可能出现的RuntimeError或者死锁。(from 网址:http://blog.csdn.net/Q_AN1314/article/details/51923022)
我看到“但是在windows环境下没有fork函数,所以如果你在父进程里打开了一个文件,在子进程中写入,会出现ValueError: I/O operation on closed file
这样的错误”这句时就突然意识到运行书本样例出现的问题应该就是这个导致的,于是我仔细分析代码发现fp代开写文件是在主进程里操作的,而Process启动的进程是不跟主进程同一个的,所以无法共享到fp的状态的。
我左思右想如何把fp的状态通过process加载启动之前传过去,甚至考虑到了用进程共享变量的形式,最后我想到既然fp=file先在主进程里就打开准备写了,我可不可以把这步直接去process创建的进程里进行?我改写代码如下:
1 # coding=utf-8 2 import unittest,os,time,multiprocessing 3 import commands 4 from email.mime.text import MIMEText 5 import HTMLTestRunner 6 import send_mail 7 import sys 8 sys.path.append('..\selenium_process') 9 10 11 def EEEcreatsuite1():12 casedir=[]13 listaa=os.listdir('..\\selenium_process')14 for xx in listaa:15 if 'thread' in xx:16 casedir.append(xx)17 print casedir18 19 suite=[]20 for n in casedir:21 testunit=unittest.TestSuite()22 discover=unittest.defaultTestLoader.discover(str(n),pattern='start_*.py',top_level_dir=r'..\\..\\test_unittest')23 for test_suite in discover:24 for test_case in test_suite:25 testunit.addTest(test_case)26 print testunit27 suite.append(testunit)28 29 return suite,casedir30 31 32 def htmlrun(filename,testut,casedir):33 fp=file(filename,'a+') #改写、前模式是wb或w+结果都只是一个进程写进了报告,另外一个进程未写入,改成a+后,搞定,写入成功34 runner=HTMLTestRunner.HTMLTestRunner(35 stream=fp,36 title=str(casedir)+u'测试报告',37 description=u'用例执行情况:'38 )39 runner.run(testut)40 fp.close()41 42 def EEEEEmutiRunCase(suite,casedir):43 now=time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time()))44 #把当前时间加到报告中45 filename="..\\report\\"+now+'result.html'46 proclist=[]47 s=048 for i in suite:49 proc=multiprocessing.Process(target=htmlrun,args=(filename,i,casedir[s])) 50 proclist.append(proc)51 s=s+152 print proclist53 54 for p in proclist: p.start()55 for p in proclist: p.join()56 send_mail.sendreport()57 58 59 60 61 62 if __name__=='__main__':63 runtmp=EEEcreatsuite1()64 EEEEEmutiRunCase(runtmp[0],runtmp[1])
结果运行通过,报告写入成功。
中间还有句:fp=file(filename,'wb') 这是作者原来写的代码,我发现运行成功后,只有一个进程的报告写入了报告,另外一个还是没写入;于是查看file()函数帮助后+模式运行多任务读与写,改成fp=file(filename,'a+')后,运行成功,所有进程的结果都写入了报告。