1

簡単にテストできるようにコードを配置することに問題があります。私のコードには、キャッシュ ジェネレーターとモディファイア ビルダーの 2 つの主要なモジュールがあり、どちらもほぼ同じレベルの複雑さを持っています。モディファイヤ ビルダーは、キャッシュ ジェネレータの子オブジェクトのメソッドの 1 つで使用されます。

モディファイヤ ビルダーの機能をカバーする完全なテスト スイートが既にあります。キャッシュ ジェネレーターのすべての機能をカバーするテストを追加したいのですが、これらのテストの複雑さを大幅に軽減するために、修飾子ビルダーを、渡した引数に基づいて事前定義された「既定のデータ」を返すスタブに置き換える必要があります。

私の実際の問題は、実際の修飾子ビルダーをスタブに置き換える方法を選択することにあります。これはコード的に見栄えがよく、テストにも便利です。次のコードを見てください。

GitHubからのコード:

cacheGenerator / generator.py:

class CacheGenerator:
    def __init__(self, logger):
        ...
        self._converter = Converter(logger)

    def run(self, dataHandler):
        ...
        data = self._converter.convert(data)

cacheGenerator/converter.py:

class Converter:
    ...

    def convert(self, data):
        ...
        self._buildModifiers(data)

    def _buildModifiers(self, data):
        ...
        builder = ModifierBuilder(data['expressions'], self._logger)
        ...
           modifiers, buildStatus = builder.buildEffect(...)

モディファイヤ ビルダーをスタブに置き換える方法は何ですか? 少なくとも次のいくつかの亜種が存在すると思います。

  1. コードの変更: コンバーターのinit () で修飾子ビルダーをインスタンス化し、そのインスタンスをオブジェクト属性として割り当てます。テストのために、実際のコンバーターのサブクラスを作成し、 init ()をオーバーライドします。ここで、実際の修飾子ビルダーをスタブに置き換えてから、キャッシュ ジェネレーターをサブクラス化し、同様の方法で実際のコンバーターをサブクラス化されたコンバーターに置き換えます。ただし、このようなアプローチにはモディファイア ビルダーの変更が必要です。データの読み込みをinit () メソッドから分割する必要がありますが、これは望ましくありません。
  2. 1) と同様ですが、Modifier Builder で動作する Converter()._buildModifiers() メソッドの一部を別のメソッドに移動して、簡単にオーバーライドできるようにします。
  3. 1) と同様ですが、クリーナーのinit ()で修飾子ビルダー クラス (インスタンスではなく) のみを指定します。これにより、モディファイア ビルダーをそのまま維持できます。
  4. キャッシュ ジェネレーターの最上部から修飾子ビルダー クラスを渡す (テストのために置き換える必要があるクラスがキャッシュ ジェネレーターのインスタンス化によって制御できるようにするため)
  5. 他の亜種は存在しますか?いくつかの輸入魔法のように?

1 ~ 4 のバリアントの一部は受け入れられるように見えますが、理想的にはコードを (オリジナルに) できるだけ近づけたいので、子オブジェクトをスタブ化する別の方法を調査しています。

4

2 に答える 2

1

テストでオブジェクトをモック/偽造する必要がある場合は、Fudgeを使用します。

あなたの場合、patched_contextの使用をお勧めします。これにより、メソッドへの呼び出しにパッチを適用できConverterます。

次に、これを行うことができます:

パッチ呼び出し_converter.convert

test.py:

from cacheGenerator.generator import CacheGenerator
from cacheGenerator.converter import Converter
from fudge import patched_context

import unittest

class Test_cacheGenerator(unittest.testCase):

    def test_run(self):

        def fakeData(convertself, data):
            # Create data to be returned to 
            # data = self._converter.convert(data)
            fakedata = ...
            return fakedata


        # We tell Fudge to patch the call to `Converter.convert`
        # and instead call our defined function 
        cache = cacheGenerator(...)
        with patched_context(Converter, 'convert', fakeData)
            cache.run()

self._buildModifiersまたは、呼び出しをinside にパッチできますConverter:

def test_run(self):
        cache = cacheGenerator(...)

        def fakeBuildModifiers(convertself, data):
            # set up variables that convert._buildModifiers usually sets up
            convertself.modifiers = ...
            convertself.buildStatus = ...

        # We tell Fudge to patch the call to `Coverter._buildModifiers`
        # and instead call our defined function 
        cache = cacheGenerator(...)
        with patched_context(Converter, '_buildModifiers', fakeBuildModifiers):
            cache.run()

または、 Fudge フェイク オブジェクトを使用することもできます。

from fuge import Fake

...
    def test_run(self):
        cache = cacheGenerator(...)

        fakeData = ...
        fakeConverter = Fake('Converter').provides('convert').returns(fakeData)

        # Fake our `Converter` so that our any calls to `_converter.convert` are
        # made to `fakeConverter.convert` instead.
        cache._converter = fakeConverter

        cache.run()

この最後のケースでは、_converterオブジェクト全体にパッチを適用しているため、他のメソッドを呼び出している場合は、それらにもパッチを適用する必要があります。

(Fake('Converter'.provides('convert').returns(fakeData)
                 .provides(....).returns()
                 .provides(....).returns()
)
于 2013-01-27T20:49:04.560 に答える
1

意図が明確になり、小さな変更であり、私の作業を再利用する他のコードに役立つ可能性があるため、通常は 2 を好みます。

または、依存性注入またはsをビルドするファクトリModifierBuilderをご覧ください。

最後に、モジュールをインポートしてシンボルに新しい値を割り当てることで、モンキー パッチを使用できます。

import cacheGenerator.converter
cacheGenerator.converter.ModifierBuilder = ...

もちろん、これにより全員のシンボルが変更されるため (つまり、他のすべてのテストでも)、古い値を保存し、テスト後に復元する必要があります。

そして、この解決策に不満や不安を感じているなら、その通りです。これは必死の対策です。元のコードを本当に変更できない場合は、一種の最後の手段として使用してください。

于 2013-01-23T11:11:40.713 に答える