12

(タイトルは「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 ツールを使用して外部からサービスにクエリを実行できるため、サービスが機能していることはわかっていますが、サービスを開始するテスト内でこれを自動化することはできません。

これを処理するためにテスト内である種のプロセスフォークを行うことを検討していますが、誰かがよりきちんとした解決策を持っているか、少なくともこのようなテストを書くための良い出発点を持っていることを望んでいました.

4

6 に答える 6

7

Ali A の投稿の助けを借りて、問題を解決することができました。ブロッキング イベント ループは、テストをブロックせずにイベントをリッスンできるように、別のプロセスで起動する必要がありました。

質問のタイトルに間違った用語が含まれていることに注意してください。単体テストではなく、機能テストを作成しようとしていました。私はその違いに気づいていましたが、後になるまで自分の間違いに気づきませんでした。

質問の例を調整しました。「test_pidavim.py」の例と大まかに似ていますが、「dbus.glib」のインポートを使用して、すべての DBusGMainLoop をコーディングする代わりに、glib ループの依存関係を処理します。

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()
于 2009-02-04T18:46:09.540 に答える
3

簡単な解決策: dbus を使用して単体テストを行わないでください。

代わりに、メソッドを直接呼び出す単体テストを記述します。これは、単体テストの性質により自然に適合します。

また、dbus を介して実行されていることを確認する自動化された統合テストが必要になる場合もありますが、それらはそれほど完全である必要も、分離して実行する必要もありません。別のプロセスで、サーバーの実際のインスタンスを開始するセットアップを行うことができます。

于 2009-02-04T10:52:45.753 に答える
2

setUp メソッド内で非常に簡単に別のスレッドでメインループを開始することもできます。

このようなもの:

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()
于 2009-04-17T20:27:17.623 に答える
2

私はPythonを知らず、この魔法の「dbus」が何であるかをある程度しか理解していないため、ここでは少し外れているかもしれませんが、正しく理解していれば、実行ループを拡張したかなり変わったテスト環境を作成する必要がありますセットアップ/ティアダウンなど。

あなたの問題に対する答えは、mockingを使用することです。インターフェイスを定義する抽象クラスを作成し、それから実際のコードで使用するオブジェクトを構築します。テストの目的で、同じインターフェイスを介して通信するモック オブジェクトを構築しますが、テストの目的で定義する動作を持ちます。このアプローチを使用して、イベント ループを実行する dbus オブジェクトを "シミュレート" したり、何らかの作業を行ったりしてから、そのオブジェクトによって行われた "作業" の結果にクラスがどのように反応するかをテストすることに専念できます。

于 2009-02-04T11:10:34.920 に答える
2

メインループを適切に処理していることを確認する必要があります。

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を参照してください。

于 2009-02-04T12:36:02.613 に答える
0

python-dbusmockライブラリを確認してください。

見苦しいサブプロセス ロジックを隠してくれるので、テストで気にする必要はありません。

于 2013-09-30T23:13:35.360 に答える