首页 > 解决方案 > 如何使用pytest模拟完全重启

问题描述

如何测试我的程序对意外关闭的鲁棒性?

我的 python 代码将在一个意外关闭的微控制器上运行。我想测试代码的每个部分意外重启并验证它是否正确处理。

尝试:我尝试将代码放入它自己的进程中,然后提前终止它,但这不起作用,因为 MyClass 从命令行调用 7zip ,即使在进程终止后仍会继续:

import multiprocessing
import os

def MyClass(multiprocessing.Process):
   ...
   def run():
      os.system("7z a myfile.7z myfile")


process = MyClass()
process.start()
time.sleep(4)
print("terminating early")
process.terminate()
print("done")

我想要的是:

class TestMyClass(unittest.TestCase):
    def test_MyClass_continuity(self):
        myclass = MyClass().start()
        myclass.kill_everything()
        myclass = MyClass().start()
        self.assert_everything_worked_as_expected()

是否有捷径可寻?如果不是,你如何设计可以在任何时候终止的健壮代码(例如测试状态机)?

类似问题(截至 21 年 10 月 26 日未回答):模拟 pytest 中的异常终止

非常感谢!

标签: pythonerror-handlingmultiprocessingpytestmicrocontroller

解决方案


您的逻辑启动一个包含在对象中的进程,该MyClass对象本身通过os.system调用产生一个新进程。

当你终止MyClass进程时,你杀死了父进程,但你让7zip进程作为孤儿运行。

此外,该方法向子进程process.terminate发送信号。SIGTERM子进程可以拦截所述信号并在终止之前执行一些清理例程。如果您想模拟没有机会清理的情况(断电),这并不理想。您很可能想要发送SIGKILL信号(在 Linux 上)。

要杀死父子进程,需要处理整个进程组。

import os
import time
import signal
import multiprocessing


class MyClass(multiprocessing.Process):
    def run(self):
        # Ping localhost for a limited amount of time
        os.system("ping -c 12 127.0.0.1")


process = MyClass()
process.start()

time.sleep(4)

print("terminating early")

# Send SIGKILL signal to the entire process group
group_id = os.getpgid(process.pid)
os.killpg(group_id, signal.SIGKILL)

print("done")

以上仅适用于 Unix 操作系统,不适用于 Windows 操作系统。

对于 Windows,您需要使用psutil模块。

import os
import time
import multiprocessing

import psutil


class MyClass(multiprocessing.Process):
    def run(self):
        # Ping localhost for a limited amount of time
        os.system("ping -c 12 127.0.0.1")


def kill_process_group(pid):
    process = psutil.Process(pid)
    children = process.children(recursive=True)

    # First terminate all children
    for child in children:
        child.kill()
    psutil.wait_procs(children)

    # Then terminate the parent process
    process.kill()
    process.wait()


process = MyClass()
process.start()

time.sleep(4)

print("terminating early")

kill_process_group(process.pid)

print("done")

推荐阅读