python-3.x - 如何在 python 单元测试中子处理 pymodbus tcp 服务器并在执行所有测试后终止它?
问题描述
在介绍我面临的问题之前,我将尝试为您提供一些背景信息。我有一个名为 Actuator 的组件,它依赖于模块 pymodbus。当我测试这个组件时,我使用基于 pymodbus 的 modbus TCP 服务器以最简单的方式进行了测试。基本上,我在一个新的 shell 和我的应用程序中将服务器作为 python 脚本(正是这个:https ://pymodbus.readthedocs.io/en/latest/source/example/synchronous_server.html )运行,其中包括执行器,在另一个外壳中。一切都像魅力一样。
我现在想做的是为执行器编写一个单元测试,使用 python unittest 和 pymodbus 并尝试自动化我之前所做的事情。我的想法是在 setUp 方法中将 modbus 服务器作为子进程运行(考虑到我不需要服务器的输出),在我的测试套件中使用它,然后在 tearDown 方法中终止它,如下所示:
class TestActuator(unittest.TestCase):
"""Class to test the actuator module"""
def setUp(self):
"""
Setup a real Actuator object with a modbus server running
in background for tests
"""
modbus_server_path = "path/to/modbus_server.py"
cmd = "python3 {}".format(modbus_server_path)
self.modbus_server = Popen(cmd.split(), shell=False, stdout=DEVNULL, stderr=DEVNULL)
# other stuff
def test1(self):
def test2(self):
def tearDown(self):
"""Cleanup everything before exiting"""
self.modbus_server.terminate()
if __name__ == '__main__':
unittest.main()
不幸的是,这似乎比我预期的更具挑战性。我从 stackoverflow 上的其他主题尝试的一切都失败了:
- 使用 Popen.kill() 而不是终止。我还尝试在杀死或终止后“删除”或使用 os.kill(self.modbus_server.pid, SIGTERM) 代替。
- 在 Popen 命令中添加或更改 args,例如 shell=True 而不是 shell=False 和 close_fds=True。
- 使用子进程 Popen 的其他变体,例如 check_output、run、call 等...
- 使用 os.spawnl 而不是子进程 Popen。
这些都不起作用。大多数时候发生的情况是服务器无法正常启动,因此所有其他测试都失败并且 modbus_server 进程没有终止(我必须手动终止它)。
你有什么主意吗?谢谢你。
解决方案
我将分享我如何解决我的问题以及到目前为止我学到了什么:
关于在运行测试时使 modbus TCP 服务器作为子进程运行的尝试,您需要更改一些内容以使其正常工作。
第一件事是使用“setUpClass”和“tearDownClass”而不是setUp和tearDown,(你也可以使用它们,但在我的情况下我需要前两个)这样你就可以运行你的服务器设置一次所有测试套件,而不是每个测试用例之前和之后。不幸的是,setUpClass 和 tearDownClass 的语法并不是那么简单。这个答案对我帮助很大:为一组自动化测试只运行一次 setUp
第二件事是关于 TCP 套接字。基本上,我想确保我的测试运行良好并连续运行几次。因此,有时一切正常,有时却失败了。过了一会儿,我发现如果我在再次尝试之前至少等待一分钟就运行测试,它们将会失败。这是由于本地地址和端口 5020 上的 modbus 服务器绑定的 TCP 套接字的 TIME_WAIT(默认设置为 60 秒)。如果您想在之前使用的相同 TCP 地址和端口上重新绑定套接字,则必须等待 60 秒。这个答案有助于理解为什么会发生这种情况:Setting TIME_WAIT TCP
在我弄清楚如何使所有这些东西一起工作之后,我决定使用模拟,因为我的解决方案对于我的口味来说似乎太老套了。