[英]How to write a gRPC client in Python for a gRPC service written in Java
[英]How to write a functional test for a DBUS service written in Python?
(標題是:“如何為用Python編寫的DBUS服務編寫單元測試?”)
我已經開始使用dbus-python編寫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工具從外部查詢服務,但我無法在啟動它的測試中自動執行此操作。
我正在考慮在測試中進行某種處理以處理這個問題,但我希望有人可能有一個更整潔的解決方案,或者至少是一個很好的起點,我將如何編寫這樣的測試。
在Ali A的帖子的幫助下,我設法解決了我的問題。 阻塞事件循環需要啟動到一個單獨的進程中,以便它可以在不阻塞測試的情況下偵聽事件。
請注意我的問題標題包含一些不正確的術語,我試圖編寫功能測試,而不是單元測試。 我知道這種區別,但直到后來才意識到我的錯誤。
我在我的問題中調整了這個例子。 它松散地類似於“test_pidavim.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()
簡單的解決方案:不要通過dbus進行單元測試。
而是編寫單元測試以直接調用您的方法。 這更符合單元測試的本質。
您可能還需要一些自動集成測試,它們檢查通過dbus運行,但它們不需要如此完整,也不需要單獨運行。 您可以在單獨的進程中進行設置以啟動服務器的實際實例。
我可能有點離開我的聯盟,因為我不知道python並且只知道這個神奇的“dbus”是什么,但如果我理解正確,它需要你用runloops創建一個相當不尋常的測試環境,擴展設置/拆卸,等等。
你的問題的答案是使用模擬 。 創建一個定義接口的抽象類,然后構建一個用於實際代碼的對象。 出於測試目的,您構建一個模擬對象通過相同的接口進行通信,但具有您為測試目的定義的行為。 您可以使用此方法“模擬”通過事件循環運行的dbus對象,執行某些工作等,然后只關注測試您的類應該如何對該對象完成的“工作”結果做出反應。
您只需要確保正確處理主循環。
def refresh_ui():
while gtk.events_pending():
gtk.main_iteration_do(False)
這將運行gtk主循環,直到它完成處理所有內容,而不是僅運行它並阻止。
有關它在實踐中的完整示例,請對dbus接口進行單元測試,請訪問: http : //pida.co.uk/trac/browser/pida/editors/vim/test_pidavim.py
你也可以在setUp方法中非常簡單地在一個單獨的線程中啟動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()
看看python-dbusmock庫。
它隱藏了丑陋的子進程邏輯,因此您不必在測試中擔心它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.