パート1
モックしたい一連のクラスがあるセットアップがあります。これを行いたい場合は、mock
キーワード引数をコンストラクターに渡し、__new__
これをインターセプトして、代わりにモックされたバージョンのそのオブジェクト。
次のようになります ( @mgilsons の提案後にキーワード検索を編集しました):
class RealObject(object):
def __new__(cls, *args, **kwargs):
if kwargs.pop('mock', None):
return MockRealObject()
return super(RealObect, cls).__new__(cls, *args, **kwargs)
def __init__(self, whatever = None):
'''
Constructor
'''
#stuff happens
次に、コンストラクターを次のように呼び出します。
ro = RealObject(mock = bool)
ここでの問題は、次の場合に次のエラーが発生することbool
ですFalse
。
TypeError: __init__() got an unexpected keyword argument 'mock'
mock
これは、キーワード引数として追加すると機能します__init__
が、これを回避できるかどうかを尋ねています。mock
からをポップすることさえありkwargs
dict
ます。
これもデザインに関する質問です。これを行うより良い方法はありますか?(もちろん!)ファクトリーやスーパークラスなどを使わずに、この方法でやってみたかったのです。それでも、別のキーワードを使用する必要がありますか? __call__
?
jsbuenoの回答に基づくパート2
__new__
そこで、メタクラスと関数を別のモジュールに抽出したいと考えました。これは私がしました:
class Mockable(object):
def __new__(cls, *args, **kwargs):
if kwargs.pop('mock', None):
mock_cls = eval('{0}{1}'.format('Mock',cls.__name__))
return super(mock_cls, mock_cls).__new__(mock_cls)
return super(cls, cls).__new__(cls,*args, **kwargs)
class MockableMetaclass(type):
def __call__(self, *args, **kwargs):
obj = self.__new__(self, *args, **kwargs)
if "mock" in kwargs:
del kwargs["mock"]
obj.__init__(*args, **kwargs)
return obj
そして、別のモジュールでクラスRealObject
とMockRealObject
. 私は今2つの問題を抱えています:
MockableMetaclass
とがクラスMockable
と同じモジュールにない場合、 はif I provide を発生させます。RealObject
eval
NameError
mock = True
- コードが
mock = False
無限の再帰に入り、印象的なRuntimeError: maximum recursion depth exceeded while calling a Python objec
.RealObject
これは、のスーパークラスobject
が ではなく になったためだと思いMockable
ます。
これらの問題を解決するにはどうすればよいですか? 私のアプローチは間違っていますか?代わりにMockable
デコレーターとして持つべきですか?私はそれを試しましたが__new__
、インスタンスが読み取り専用であるため、機能していないようです。