5

Python で小さなジョブ スケジューラを作成しています。スケジューラには、一連の呼び出し可能オブジェクトと依存関係を与えることができ、呼び出し可能オブジェクトを実行する必要があります。これにより、先行タスクの前にタスクが実行されないようにする必要があります。

私はテスト駆動型のアプローチに従おうとしていますが、依存関係の処理をテストする際に問題が発生しました。私のテストコードは次のようになります。

def test_add_dependency(self):
    """Tasks can be added with dependencies"""
    # TODO: Unreliable test, may work sometimes because by default, task
    #       running order is indeterminate.
    self.done = []
    def test(id):
        self.done.append("Test " + id)
    s = Schedule()
    tA = Task("Test A", partial(test, "A"))
    tB = Task("Test B", partial(test, "B"))
    s.add_task(tA)
    s.add_task(tB)
    s.add_dependency(tA, tB)
    s.run()
    self.assertEqual(self.done, ["Test B", "Test A"])

問題は、依存関係処理コードを追加する前でも、このテストが (場合によっては) 機能していたことです。これは、タスクを特定の順序で実行する必要があると仕様に記載されていないためです。したがって、依存関係情報が無視されたとしても、正しい順序は完全に有効な選択です。

この種の「偶然の」成功を避けるためにテストを書く方法はありますか? これはかなり一般的な状況のように思えます。特に、テスト駆動型の「テストに失敗しない限りコードを書くな」というアプローチを取る場合はそうです。

4

4 に答える 4

2

あなたは、すべての研究者が不完全なデータの集まりを見て、それに関する仮説が正しいかどうかを言おうとしている状況にいます。

実行ごとに結果が異なる場合は、何度も再実行すると、統計を適用して機能しているかどうかを判断できるサンプルが得られます。ただし、実行のバッチでは同様の結果が得られるが、別の日の別のバッチでは異なる結果が得られる場合、非決定性はプログラム自体の外部のイベントに依存しており、方法を見つける必要があります。それらを制御し、理想的には、悪いアルゴリズムにつまずく可能性を最大化します。

これが非決定論の代償です。統計に頼る必要があり、統計を正しく取得する必要があります。ある程度の信頼度で仮説を受け入れ、帰無仮説を棄却できる必要があります。結果の分散を最大化できれば、必要なサンプル数は少なくなります。さまざまな CPU 負荷または IO 割り込みがあるか、ランダムなスリープを伴うタスクをスケジュールします。

いずれにせよ、価値のあるテストを定義するためには、そのようなスケジューラーが何によって影響を受けるかを調べることをお勧めします。

于 2013-04-12T14:05:42.547 に答える
1

テストを作成する前に、何をテストする必要があるかを判断することをお勧めします。

上記のコード サンプルでは、​​スケジューラの説明によると実際のシーケンスは非決定論的であるにもかかわらず、特定のタスク シーケンスがスケジューラによって生成されることがテストされているため、テストは実際には、コード: 合格する場合もあれば、合格しない場合もあります。合格する場合は、偶然です。

一方、より価値のあるテストは、タスクの位置について何も主張せずに、結果にタスクの存在 (または不在) を主張することです: 「セット内にある」対「配列の位置にある」

于 2013-04-12T15:55:26.363 に答える
1

1 つのオプションは、テスト目的でクラスの別の決定論的なバージョンを使用するSchedule(または既存のバージョンを決定論的にするオプションを追加する) ことですが、それは単体テストの目的に反する可能性があります。

もう 1 つのオプションは、非決定論的な結果のテスト ケースをわざわざ作成しないことです。


一般的に、しかし、あなたの質問への答えは...

この種の「偶然の」成功を避けるためにテストを書く方法はありますか?

...おそらく「いいえ」ですが、それを書くときは特に注意が必要です。ただし、疑わしいテスト ケースを作成しないように用心深くする能力があり、その用心深さを最初からコードの作成に適用した場合は、ほぼ間違いなく、単体テストさえ必要ありません。;-)

単体テストのポイントがコードのバグを検出することである場合、単体テストでバグを検出するにはどうすればよいでしょうか?

単体テスト用に「メタ」単体テストを作成することもできますが、「メタ」単体テストでバグを検出するにはどうすればよいでしょうか。等々...

単体テストが役に立たないというわけではありませんが、コードが「正しい」ことを「証明」するには単体テストだけでは十分ではありません。実際には、ピア ベースのコード レビューは、コードの欠陥を検出するためのはるかに効果的な手段であることがわかりました。

于 2013-04-12T14:03:13.510 に答える
1

この戦略は、ほとんどの場合有効です。

まず、エントロピーの外部ソースをすべて排除します (単一のスレッドを使用するようにスレッド プールを設定します。事前にシードされた PRNG を使用して RNG をモックするなど)。次に、テストを繰り返し実行して、出力のすべての組み合わせを生成し、機械への入力のみを変更します。テスト中:

from itertools import permutations
def test_add_dependency(self):
    """Tasks can be added with dependencies"""
    for p in permutations("AB"):
        self.done = []
        def test(id):
            self.done.append("Test " + id)
        s = Schedule(threads=1)
        tasks = {id: Task("Test " + id, partial(test, id)) for id in "AB"}
        s.add_task(tasks['A'])
        s.add_task(tasks['B'])
        s.add_dependency(tasks[p[0]], tasks[p[1]])
        s.run()
        self.assertEqual(self.done, ["Test " + p[1], "Test " + p[0]])

Scheduleからの情報を使用できない場合、このテストは失敗します。これadd_dependencyは、テスト実行間で異なるエントロピー (情報) の唯一のソースであるためです。

于 2013-04-12T14:45:32.733 に答える