python多进程运行测试用例的写报告为空的问题与解决

研究多线程问题也是因为当时自动发送报告邮件导致的,那会还没找到解决方案就一直想着如果启用多进程运行呢,结果就遇到了个新的问题,就是本文的来源。

先贴个书本讲解多进程运行测试用例的源代码:

 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 casedir
17 
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 testunit
26         suite.append(testunit)
27 
28     return suite,casedir
29 
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=0
39     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+1
48         print proclist
49 
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 casedir
18 
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 testunit
27         suite.append(testunit)
28 
29     return suite,casedir
30 
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=0
48     for i in suite:
49         proc=multiprocessing.Process(target=htmlrun,args=(filename,i,casedir[s])) 
50         proclist.append(proc)
51         s=s+1
52         print proclist
53 
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+')后,运行成功,所有进程的结果都写入了报告。

posted on 2017-04-19 23:19  sugus33  阅读(558)  评论(0)    收藏  举报