Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/84.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何为用Python编写的DBUS服务编写功能测试?_Python_Unit Testing_Dbus - Fatal编程技术网

如何为用Python编写的DBUS服务编写功能测试?

如何为用Python编写的DBUS服务编写功能测试?,python,unit-testing,dbus,Python,Unit Testing,Dbus,(标题是:“如何为用Python编写的DBUS服务编写单元测试?”) 我已经开始使用dbuspython编写DBUS服务,但是我在为它编写测试用例时遇到了困难 下面是我试图创建的测试的一个示例。请注意,我在setUp()中放置了一个GLib事件循环,这就是问题所在: import unittest import gobject import dbus import dbus.service import dbus.glib class MyDBUSService(dbus.service.O

(标题是:“如何为用Python编写的DBUS服务编写单元测试?”)

我已经开始使用dbuspython编写DBUS服务,但是我在为它编写测试用例时遇到了困难

下面是我试图创建的测试的一个示例。请注意,我在setUp()中放置了一个GLib事件循环,这就是问题所在:

import unittest

import gobject
import dbus
import dbus.service
import dbus.glib

class MyDBUSService(dbus.service.Object):
    def __init__(self):
        bus_name = dbus.service.BusName('test.helloservice', bus = dbus.SessionBus())
        dbus.service.Object.__init__(self, bus_name, '/test/helloservice')

    @dbus.service.method('test.helloservice')
    def hello(self):
        return "Hello World!"


class BaseTestCase(unittest.TestCase):

    def setUp(self):
        myservice = MyDBUSService()
        loop = gobject.MainLoop()
        loop.run()
        # === Test blocks here ===

    def testHelloService(self):
        bus = dbus.SessionBus()
        helloservice = bus.get_object('test.helloservice', '/test/helloservice')
        hello = helloservice.get_dbus_method('hello', 'test.helloservice')
        assert hello() == "Hello World!"

if __name__ == '__main__':
    unittest.main()
我的问题是DBUS实现要求您启动事件循环,以便它可以开始调度事件。常用的方法是使用GLib的gobject.MainLoop().start()(尽管我不喜欢这种方法,如果有人有更好的建议的话)。如果不启动事件循环,服务仍然会阻塞,并且您也无法查询它

如果在测试中启动服务,事件循环将阻止测试完成。我知道该服务正在工作,因为我可以使用qdbus工具在外部查询该服务,但我无法在启动该服务的测试中实现自动化


我正在考虑在测试内部进行某种过程分叉来处理这个问题,但我希望有人能有一个更简洁的解决方案,或者至少是一个好的起点,来指导我如何编写这样的测试。

简单的解决方案:不要通过dbus进行单元测试

而是编写单元测试来直接调用方法。这更符合单元测试的本质


您可能还需要一些自动化集成测试,这些测试通过DBU进行检查,但它们不需要如此完整,也不需要单独运行。您可以在单独的过程中启动服务器的真实实例。

我在这里可能有点力不从心,因为我不懂python,只是稍微了解这个神奇的“dbus”是什么,但如果我理解正确,它需要您创建一个非常不寻常的测试环境,包括运行循环、扩展安装/拆卸,等等


你的问题的答案是使用。创建一个定义接口的抽象类,然后从该类构建一个对象以在实际代码中使用。出于测试的目的,您构建了一个模拟对象,该对象通过同一接口进行通信,但具有您将为测试目的定义的行为。您可以使用这种方法“模拟”运行在事件循环中的dbus对象,进行一些工作,等等,然后只需专注于测试您的类应该如何对该对象完成的“工作”的结果作出反应。

您只需确保正确处理主循环即可

def refresh_ui():
    while gtk.events_pending():
       gtk.main_iteration_do(False)
这将运行gtk主循环,直到它完成所有处理,而不仅仅是运行它并阻塞


关于实践中的完整示例,单元测试dbus接口,请点击此处:

在Ali a的帖子的帮助下,我成功地解决了我的问题。阻塞事件循环需要启动到一个单独的进程中,这样它就可以在不阻塞测试的情况下侦听事件

请注意,我的问题标题包含一些不正确的术语,我试图编写一个功能测试,而不是单元测试。我意识到了这种区别,但直到后来才意识到我的错误

我已经调整了我问题中的例子。它大致类似于“test_pidavm.py”示例,但使用“dbus.glib”的导入来处理glib循环依赖项,而不是在所有DBusGMainLoop内容中编码:

import unittest

import os
import sys
import subprocess
import time

import dbus
import dbus.service
import dbus.glib
import gobject

class MyDBUSService(dbus.service.Object):

    def __init__(self):
        bus_name = dbus.service.BusName('test.helloservice', bus = dbus.SessionBus())
        dbus.service.Object.__init__(self, bus_name, '/test/helloservice')

    def listen(self):
        loop = gobject.MainLoop()
        loop.run()

    @dbus.service.method('test.helloservice')
    def hello(self):
        return "Hello World!"


class BaseTestCase(unittest.TestCase):

    def setUp(self):
        env = os.environ.copy()
        self.p = subprocess.Popen(['python', './dbus_practice.py', 'server'], env=env)
        # Wait for the service to become available
        time.sleep(1)
        assert self.p.stdout == None
        assert self.p.stderr == None

    def testHelloService(self):
        bus = dbus.SessionBus()
        helloservice = bus.get_object('test.helloservice', '/test/helloservice')
        hello = helloservice.get_dbus_method('hello', 'test.helloservice')
        assert hello() == "Hello World!"

    def tearDown(self):
        # terminate() not supported in Python 2.5
        #self.p.terminate()
        os.kill(self.p.pid, 15)

if __name__ == '__main__':

    arg = ""
    if len(sys.argv) > 1:
        arg = sys.argv[1]

    if arg == "server":
        myservice = MyDBUSService()
        myservice.listen()

    else:
        unittest.main()

您也可以在安装方法内的单独线程中启动mainloop

大概是这样的:

import threading
class BaseTestCase(unittest.TestCase):
    def setUp(self):
        myservice = MyDBUSService()
        self.loop = gobject.MainLoop()
        threading.Thread(name='glib mainloop', target=self.loop.run)
    def tearDown(self):
        self.loop.quit()
去图书馆看看


它将丑陋的子流程逻辑隐藏在你的眼睛后面,因此你不必在测试中担心它。

我认为你是对的。反省一下,我认为不管怎样,只要直接测试服务方法,测试dbus接口就不会有什么好处。你必须这么做。不要仅仅因为有人告诉你就忽略单元测试,人们给出这种建议真的令人沮丧。是的,你应该在没有dbus的情况下测试你的东西,是的,你应该用dbus测试它。但是如果它们依赖于dbus的启动和工作,那么它们就不是单元测试。因此,为功能编写单元测试,并使用dbus编写具有更复杂设置(多进程等)的集成测试。我选择了更大的“单元”。根据定义,将DBU放在两者之间使其超越了单元测试。我喜欢这个概念。我认为如果我的DBUS接口类开始包含一些逻辑,我将使用mocking来模拟行为。您引用的测试似乎将DBUS服务器作为一个单独的进程(在Vim内部)进行分叉。我的事件循环还可以,但是这个例子证明整个过程不能在测试中完成。谢谢你指出。谢谢-非常方便。尽管我建议使用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu