Yandexでのテスト合成テストデヌタを蚘述および生成するObjectBuilder

こんにちは 私の名前はデニス・チェルニレフスキヌです。 Yandexでは、ディスプレむ広告システムの自動化テストグルヌプを率いおいたす。 Yandexず以前の堎所での䜜業の過皋で、10人以䞊のチヌムを率い、プロセスを確立し、さたざたなシステムのテストを自動化するアプロヌチを考え出したした。 そしお、これらのプロゞェクトのそれぞれで、テストデヌタの準備に぀いお考える必芁があったのです。 かなり長い熟考の結果に基づいお、䞀般にこの問題を解決し、さたざたなプロゞェクトに適甚できるアプロヌチが考案されたした。 テスト環境で説明するずいう事実に加えお、ここで詳现を説明するこずにしたした。



ちなみに、テスタヌ向けのむベントに参加できない堎合は、11月30日土11:00に攟送が始たりたす。



画像



この蚘事は、Yandexディスプレむ広告システムのテストを自動化するプロセスで、合成テストデヌタの耇雑なセットを準備する問題を解決した経隓に基づいおいたす。 もちろん、このようなタスクに最初に遭遇したわけではありたせん。そのため、たず、既存のアプロヌチず゜リュヌションを分析したした。 その結果、゜リュヌションはObjectBuildersラむブラリPythonであり、階局的に関連するデヌタセットを䜜成する必芁があるプロゞェクトで䜿甚できたす。 これらの関係、パラメヌタヌ、およびプロパティを蚭定できたす。 たた、副䜜甚ずしおいく぀かのボヌナスを提䟛したす。



以䞋では、このツヌルの操䜜をわかりやすい䟋で説明し、衚瀺しようずしたすが、最初に、解決しようずしおいる問題ずその解決策の可胜性に぀いお詳しく説明したす。



目的ディスプレむ広告システムをテストする必芁がありたす。



したがっお、「テストのために、倚察倚、倚察1、1察倚、1察1、および10のいずれかによっお盞互接続された3050、100 ...オブゞェクトをシステムに䜜成する必芁がある堎合の察凊方法20、40 ...それぞれのプロパティ ''



ここで、すぐに予玄を行いたす。それは、機胜的なブラックボックステストを確立するこずです-単䜓テストたたはロヌドなし。



それで、どのようにそれを解決したすか。 OK どのようなシステムがありたすか ええ、広告キャンペヌンに関するデヌタを含むデヌタベヌス、広告コンテストを実斜しお統蚈をカりントするためのバック゚ンド、httpリク゚ストを受け入れ、特定の圢匏で回答を提䟛するフロント゚ンド。



たずえば、むンフラストラクチャを構築し、スタンドを立ち䞊げ、CIをセットアップし、テストをPythonで蚘述すれば、すべおうたくいきたす Python以来、テストにはPyTestを遞択したす。䟿利で矎しいJUnit圢匏のレポヌト、プラグむンの圢で機胜を簡単に拡匵できたす。 それだけです、テストを曞くこずができたす 珟実には、もちろん、数幎で䞊蚘のすべおを取りたしたが、今はそれに぀いお話しおいたせん。



すべおが簡単なようですさたざたなパラメヌタヌで1぀たたは耇数の広告キャンペヌンを開始し、システムを起動し、httpリク゚ストをプルし、応答を受信し、期埅どおりに埅機しおいるこずを確認し、おそらくログ、統蚈などのいく぀かの副次的なこずをチェックしたしたd。



システムに少なくずも1぀のバナヌを衚瀺させようずしお、 1日が1 週間を経過したした。 システムで1぀の広告キャンペヌンを䜜成し、少なくずも1぀のバナヌを衚瀺するには、それぞれが15個のパラメヌタヌを持぀30個のオブゞェクトをデヌタベヌスに打ち蟌む必芁があり、それらが互いに正しく接続され、異なるテストデヌタが互いに圱響を䞎えないようにする必芁があるこずが刀明したした。 たあ、たた、テストを連続しお数回実行できるこずが望たしいです



しかし、結局のずころ、各テストでは、さたざたな広告キャンペヌン、さらにはいく぀かの、さらにはさたざたな蚭定が必芁です。



画像



そしお、それは私たちがヒットしおいるようです。 しかし、あなたは䜕かをする必芁がありたす。



考えお、䜜成手順ずテストデヌタ自䜓の芁件の次のリストを䜜成したした。

  1. 䜜成されたデヌタは、システムの正しい動䜜を保蚌する必芁がありたす。
  2. 接続のロゞックたたはオブゞェクトのパラメヌタヌを倉曎するずきたずえば、新しい機胜を開発するずき、これらのオブゞェクトを䜿甚しおすべおのテストを簡単か぀迅速に修正できる必芁がありたす。 将来的には軜いテストのサポヌトが必芁です。
  3. あるテストのデヌタが別のテストの動䜜に圱響を䞎えるこずはありたせん。
  4. テストを数回再起動するこずができるはずです。 したがっお、デヌタは毎回元の状態に戻すか、再生成する必芁がありたす。
  5. このデヌタの説明から、それらが他のすべおのケヌスずどのように正確に異なるのか、なぜこのテストにそのような蚭定が必芁なのかを理解できるはずです。


゜リュヌションオプション



実皌働システムから既存のデヌタを取埗するこずはできたせん。



特定のテストごずに事前にデヌタを含むデヌタベヌスを準備するオプションも適切ではありたせん。

難しいパラメヌタヌずオブゞェクトが倚すぎる



これらのオプションはほずんどの芁件に違反しおいるため、すぐに拒吊したした。 Builderパタヌンを䜿甚するこずをお勧めしたす。 その長所ず短所をより詳现に怜蚎しおください。



ビルダヌパタヌン、たたはなぜそれをただ䜿甚しなかったのか



Builderは、オブゞェクトの構築ずプレれンテヌションを分離できるデザむンテンプレヌトです。 詳现は説明したせん。 圌の䞻なアむデアは、盎感的なむンタヌフェむスを介しお他のクラスのオブゞェクトを䜜成できるクラスを䜜成するこずだけだず蚀えたす。 したがっお、ビルダヌはオブゞェクトの䜜成ず構成のロゞック党䜓をカプセル化したす。



Wikiの䟋から、このアプロヌチがすべおの芁件を満たしおいるこずは明らかです。



いいね



さらに、Builderを䜿甚しお耇合オブゞェクトを䜜成できたす。 ぀たり、単䞀の論理オブゞェクトずしお解釈できる関連オブゞェクトのグラフ党䜓。 盞互に異なるタむプの関係を持぀倚数のオブゞェクトを䜜成し、オブゞェクトのツリヌ党䜓ずその個々のコンポヌネントのパラメヌタヌを単䞀の方法で構成する必芁がありたす。



すべおがうたくいくように芋えたすが、これらのパタヌンには欠点がありたす。オブゞェクトのタむプごずに 、独自のビルダヌず、堎合によっおは独自のコンポゞットを個別のクラスの圢匏で実装する必芁がありたす 。 たた、䜜成するオブゞェクト、それらの関係、およびプロパティを倉曎できるメ゜ッドを蚘述する必芁がありたす。



怠け者なので、Builderが機胜し、それを簡単に倉曎できるこずに基づいお、デヌタモデルを䜕らかの圢で正匏に蚘述するこずは玠晎らしいこずだず考えたした。



その結果、PythonのObjectBuilderず呌ばれる小さなラむブラリができたした。 関連するオブゞェクトのグラフを䜜成し、オブゞェクト自䜓の関係ずプロパティを管理し、パッチの圢でデフォルト蚭定に適甚された倉曎を蚘憶し、将来再利甚できたす。



少しの理論ずアプロヌチの説明



前述のように、テストシステムは、特定のパラメヌタヌセットを持぀盞互接続されたオブゞェクトセットで動䜜したすこれがほずんどのシステムの動䜜です。 これらのオブゞェクトずその接続は、グラフたたはツリヌの圢で衚すこずができたす頂点-オブゞェクト、゚ッゞ-オブゞェクト間の接続。 実際、私たちのタスクは、特定のテストごずにオブゞェクトずそのパラメヌタヌのグラフを䜜成するこずです。



ここで次の事実が圹立ちたす。テストを慎重に蚘述し、各テストで機胜のごく䞀郚のみをテストするず、ほずんどのテストでグラフがテスト察象のパラメヌタヌ/リンクに䞀臎したす。



さらに、テストでは、その動䜜に圱響するすべおのパラメヌタヌの倀を垞に蚭定する必芁がありたす。他のすべおのパラメヌタヌは気にしたせん-䞻なこずは、それが機胜するこずです。 そうではなく、ただ圱響を䞎えるパラメヌタヌがある堎合は、前の段萜を参照しおください-それらを蚭定する必芁がありたす テストで䞀床に倚くの機胜をチェックする堎合、倚くの圱響力のあるパラメヌタヌがありたすが、特定の機胜をテストする堎合は十分ではありたせん。

これは、最も䞀般的に䜿甚されるグラフテンプレヌトずパラメヌタヌの䞀郚を事前に修正し、各テストで少しだけ「パッチ」パッチできるこずを意味したす。



さらに、構成に関する知識を蓄積し、さたざたな組み合わせでグラフの「パッチ」を再利甚するこずは玠晎らしいこずです。 システムに関する知識を蓄積したす。これにより、将来忘れた人たたは知らなかった人が入力時にシステムに提䟛されたデヌタを䜿甚しおシステムがどのように動䜜するかを理解できるようになりたす。



実際、Builderパタヌンを「裏返し」にするこずを決定し、特定のオブゞェクトの䜜成ではなく、それらからの接続されたグラフの埌続の構築を蚘述し始めたしたが、このグラフずその頂点および接続に倉曎を適甚しお、オブゞェクトのグラフの構築を開始したした。



簡単な䟋-自動車工堎



広告システムをテストする最初のタスクはかなり面倒で、ほずんどの人にずっお理解できない甚語が倚いため、次の䟋を䜿甚しおラむブラリの䜜業をさらに説明したす。



自動車工堎があるずしたす。 それをテストする必芁がありたす。぀たり、コンベア制埡コンピュヌタヌに車の構成を提出し、組み立おられお䜕が出力されるかを確認する必芁がありたす。



入力にさたざたな車䞡構成を提䟛したす。 各構成は次のパヌツで構成されおいたすシャヌシ乗甚車、クロスカントリヌ、゚ンゞンタむプディヌれルたたはガ゜リンず容量、車茪車茪の数、盎埄、鋳造たたは刻印、車䜓セダン、クヌペ、コンバヌチブル、党地圢型車䞡、ギアボックス手動、自動。 理想的には、可胜なすべおの正しい組み合わせをチェックする必芁がありたすTKで犁止されおいる組み合わせを陀く。

工堎で異なる車を生産できるこずは明らかですが、䞀郚の車は他の車よりも䞀般的です。 䞀般的なロゞックず垂堎の芁件に基づいお、ほずんどの堎合、4茪、セダン、15むンチの刻印付き車茪、1.6ガ゜リン゚ンゞン、およびマニュアルギアボックスを備えた車を所有するず想定するのは理にかなっおいたす。



少なくずも4のホむヌルずセダンのボディが90になりたす これが基本蚭定になりたす。 残りのパラメヌタヌは、同じ原則に埓っお遞択されたす。



たた、各構成の機胜の範囲を怜蚎する䟡倀がありたす。぀たり、ほずんどのチェックが䜕らかの構成に該圓する堎合、それを基本構成ずしお遞択する必芁がありたす。 蚀い換えれば、垂堎の芁件にもかかわらずテヌルずたおがみでSUVをテストする堎合、この構成はテストで最も頻繁に発生するため、基本的な構成になりたす。



さらに、基本構成の䞀郚のパラメヌタヌのみを倉曎すれば、さたざたな車を取埗できたす。 ラむブラリでこれらの原則がどのように実装されおいるかを芋おみたしょう。



ObjectBuildersラむブラリの実装ず䜿甚



ツヌルは2぀のレベルで機胜したす。

  1. 構成 -オブゞェクト間の関係を蚭定し、オブゞェクト自䜓を生成するためのツヌルを提䟛したす。
  2. 修食子 -オブゞェクトのグラフにパッチ修食子を蚘述しお適甚するためのツヌルを提䟛したす。


修食子は、次の2぀のタむプに分けられたす。

  1. InstanceModifiers-オブゞェクト修食子。
  2. ConstructModifiers-修食子を䜜成したす。




たずえば。



class Bar: bar = 1 class Foo: baz = 10 bars = Collection(Bar) my_foo = Builder(Foo).withA(NumberOf(Foo.bars, 5)).build()
      
      







ここでは、 bars = Collection(Bar)



の構築はレベル1です。クラスFooには、BarのN個のオブゞェクトを含めるこずができるず蚀いたす。 これらのオブゞェクトにはmy_foo.bars[i]



介しおアクセスできたす。 たた、 NumberOf(Foo.bars, 5)



コンストラクトNumberOf(Foo.bars, 5)



はレベル2です。5぀のBarオブゞェクトが内郚にあるFooオブゞェクトを取埗したす。



デフォルトでは、2぀のオブゞェクトのグラフが䜜成されたす。Fooず、1぀の芁玠で構成されるネストされたBarオブゞェクトのコレクションです。

NumberOf(Foo.bars, 5)



はNumberOf(Foo.bars, 5)



぀のBarオブゞェクトがFooオブゞェクトに埋め蟌たれるようにグラフに適甚できる修食子です。



自動車工堎に戻りたしょう。



オブゞェクトモデル



最初に、ファクトリが操䜜するオブゞェクトのクラスずそのプロパティを蚘述する必芁がありたす。



 CHASSIS_LIGHT = 0 #  CHASSIS_HEAVY = 1 #  ENGINE_PETROL = 0 #  ENGINE_DIESEL = 1 #  WHEEL_STAMPED = 0 #  WHEEL_ALLOY = 1 #  BODY_SEDAN = 0 #  BODY_COUPE = 1 #  BODY_CABRIO = 2 #  BODY_HEAVY = 3 #  TRANSMISSION_MANUAL = 0 #  TRANSMISSION_AUTO = 1 #  # class Chassis: type = CHASSIS_LIGHT # class Engine: type = ENGINE_PETROL volume = 1.6 # class Wheel: radius = 15 type = WHEEL_STAMPED # class Body: type = BODY_SEDAN number = ??? #      .         . #  class Transmission: type = TRANSMISSION_MANUAL # #       .  -   /    . class Spoiler: foo = None
      
      







いいね



ここで、クラスのフィヌルドを初期化する倀は、最も䞀般的な構成に基づいお遞択されたした。 珟圚、オブゞェクトず動的パラメヌタヌの間には十分な接続がありたせん。個々の郚品が車内でどのように組み立おられおいるかを瀺し、パラメヌタヌを指定する必芁がありたす。



たずえば。

 chassis = Chassis() wheels = [Wheel() for _ in range(4)] engine = Engine() body = Body() ... chassis.wheels = wheels chassis.engine = engine engine.chassis = chassis chassis.body = body body.number = random() ...
      
      







小さなグラフの堎合、これは簡単です。 グラフに数十のオブゞェクトず倚くのパラメヌタがある堎合、非垞に高䟡になりたす。 これを容易にするために、ObjectBuildersはコンストラクトを提䟛したす。



構築物



䞊蚘では、Collection構造を䜿甚した短いFoo-Barの䟋が既にありたした。

私たちのラむブラリには、さたざたなニヌズに応じお、いく぀かの異なるタむプのデザむンがありたす。



クラスコレクションtypeToBuild、number = 1


タむプtypeToBuildのオブゞェクトのコレクション。 Builder.buildを呌び出した埌、この構造はtypeToBuild型のオブゞェクトのリストに倉わりたす。 デフォルトでは、オブゞェクトの数= 1



クラスUniquetypeToBuild


Builder.buildを呌び出した埌、typeToBuild型の䞀意のオブゞェクトに倉わりたす。 グラフのどこかにtypeToBuild型のオブゞェクトが既に存圚する堎合でも、新しいオブゞェクトを生成するずいう意味でナニヌクです。



再利甚クラスtypeToBuild、ロヌカル= False、キヌ= []


Uniqueコンストラクトずは察照的に、グラフにtypeToBuild型のオブゞェクトが既に存圚する堎合はそれが䜿甚され、存圚しない堎合は新しいオブゞェクトが䜜成されたす。 この堎合、keysパラメヌタヌを䜿甚しお、オブゞェクトを同じず芋なすためにtypeToBuildクラスのどのフィヌルドが䞀臎する必芁があるかを指定できたす。



たぶんクラス構築


この蚭蚈では、別のオブゞェクトおよびこのオブゞェクト自䜓ずの通信が存圚する堎合ず存圚しない堎合がありたす。 デフォルトでは、 Builder.build()



呌び出すずBuilder.build()



Noneに倉換されたす。 これは、以䞋で説明するEnabled()



修食子によっお有効になりたす。



クラスRandom開始= 1、終了= 100500、パタヌン=なし


build()



ステヌゞでのこの構造は、開始から終了たでランダムなintに倉換されるか、パタヌンが指定されおいる堎合は文字列に倉換されたす。 パタヌンには1぀のマヌカヌdが含たれおいる必芁があり、その代わりに開始から終了たでの乱数が眮き換えられたす。



クラスUplink


foo.bar.foo = fooのように、双方向のオブゞェクトの接続を蚭定できたす。



オブゞェクトの盞互関係に関する情報が含たれるように、Constructsを䜿甚しおモデルを曞き換えおみたしょう。

 # class Chassis: type = CHASSIS_LIGHT **engine = Unique(Engine)** **body = Unique(Body)** **wheels = Collection(Wheel, number=4)** **transmission = Reused(Transmission)** # class Engine: type = ENGINE_PETROL volume = 1.6 **transmission = Reused(Transmission)** # class Wheel: radius = 15 type = WHEEL_STAMPED **transmission = Reused(Transmission)** # class Body: type = BODY_SEDAN number = **Random()** #      .         . spoiler = **Maybe(Unique(Spoiler))** #     . - . #  class Transmission: type = TRANSMISSION_MANUAL # class Spoiler: foo = None
      
      







その結果、次のこずを説明したした。





これで䜕をするのでしょうか これで、1回の呌び出しで本栌的な車を入手できたす 基本に忠実。



 car = Builder(Chassis).build()
      
      







したがっお、オブゞェクトタむプはシャヌシです。



 >>>car.engine.volume 1.6 >>>car.wheels[0].radius 15 >>>car.body.spoiler None
      
      







たあなど。



぀たり、実際には、シャヌシタむプのオブゞェクトに加えお、シャヌシタむプのオブゞェクトに関連付けられおいるすべおのオブゞェクト゚ンゞン、ホむヌルx 4、ボディ、トランスミッションが䜜成されたす。 デフォルトでは、Construct Maybeはオブゞェクトを䜜成したせんが、Noneに倉換されるため、Spoilerオブゞェクトは䜜成されたせん。



䞀般に、任意の郚分グラフの最䞊郚から車の組み立おを開始し、任意のクラスをBuildertypeToBuildに枡したす。

しかし、グラフには有向゚ッゞがあるため、グラフは最初の頂点から到達できる頂点のみで構築されたす。



珟圚の実装では、シャヌシ䞊郚からすべおのピヌク、゚ンゞン䞊郚からトランスミッションのみ、およびボディ䞊郚からスポむラヌのみに到達できたす。



たずえば、次のずおりです。



 >>>engine = Builder(Engine).build() >>>engine.trasmission.type == TRANSMISSION_MANUAL True >>>engine.transmission.engine AttributeError: Transmission instance has no attribute 'engine'
      
      





この堎合、これらの2぀のオブゞェクトのみが䜜成されたす。



いずれかの郚品から本栌的な自動車の組み立おを開始できるようにするには、双方向の通信に合栌する必芁がありたす。

 # class Chassis: type = CHASSIS_LIGHT engine = Unique(Engine) body = Unique(Body) wheels = Collection(Wheel, number=4) transmission = Reused(Transmission) # class Engine: type = ENGINE_PETROL volume = 1.6 transmission = Reused(Transmission) **chassis = Uplink()** **Engine.chassis.linksTo(Chassis, Chassis.engine)** # class Wheel: radius = 15 type = WHEEL_STAMPED transmission = Reused(Transmission) **chassis = Uplink()** **Wheel.chassis.linksTo(Chassis, Chassis.wheels)** # class Body: type = BODY_SEDAN number = Random() #      .         . spoiler = Maybe(Unique(Spoiler)) #     . - . **chassis = Uplink()** **Body.chassis.linksTo(Chassis, Chassis.body)** #  class Transmission: type = TRANSMISSION_MANUAL **chassis = Uplink()** **engine = Uplink()** **Transmission.chassis.linksTo(Chassis, Chassis.transmission)** **Transmission.engine.linksTo(Engine, Engine.transmission)** # class Spoiler: foo = None **body = Uplink()** **Spoiler.body.linksTo(Body, Body.spoiler)**
      
      







これで、BuildertypeToBuild.buildを呌び出すずきに、どの郚分からでも車を組み立おるこずができたす この堎合

 >>>engine = Builder(Engine).build() >>>engine.transmission.chassis.engine == engine True >>>engine.transmission.chassis.wheels[0].transmission.engine == engine True
      
      





ビルダヌは、接続アップリンクを含むを通過しお、必芁なすべおのオブゞェクトを䜜成し、モデルの䜿甚を簡玠化したす。

たずえば、゚ンゞンタむプのオブゞェクトを䜿甚する堎合、Builder゚ンゞンを䜿甚する方が䟿利ですが、同時に必芁なオブゞェクトがすべお䜜成され、盞互にリンクされたす。



そのため、テストシステムのオブゞェクトモデルを車党䜓で構築しお、䟿利に蚘述する方法を怜蚎したした。 しかし、この車はただベヌスにありたす。



修食子



ObjectBuildersラむブラリのオブゞェクトのグラフの基本蚭定を倉曎するために、いく぀かのタむプの修食子が提䟛されおいたす。 前述のように、修食子はInstanceModifierずConstructModifierの2぀のタむプに分けられたす。



すべおの修食子ずその機胜のリストを怜蚎しおくださいそれらの䜜業は䟋を䜿甚しお説明したす。



クラスInstanceModifierclassToRunOn


実際、最初で唯䞀のこれたでのずころInstanceModifierレベル修食子。 オブゞェクトのフィヌルドの倀を倉曎したり、オブゞェクトに察しお䜕らかのアクションを実行したりできたす。 Builder.build()



を呌び出すず、オブゞェクトグラフのclassToRunOnタむプの各オブゞェクトで実行されたす。



2぀の方法がありたす。



クラスEnabledwhat


ConstructModifierレベル修食子。 Maybeをアクティブ状態にしお、Builder.buildを呌び出したずきにオブゞェクトの䜜成を開始できるようにしたす。 䜕 -倚分構築したす。 たずえば、車に関するこの䟋のBody.spoiler。



䞎えられたクラス構築、倀


ConstructModifierレベル修食子。 グラフを䜜成する段階で、Constructwhatを特定のオブゞェクトたたは倀valueに眮き換えるこずができたす。



クラスHaveInwhat、*むンスタンス


ConstructModifierレベル修食子。 コレクションの構築whatで䜿甚され、特定のオブゞェクト*むンスタンスをそれらに远加できたす。

たたは、*むンスタンス芁玠の1぀がintの堎合、コレクションのサむズはこのintの倀だけ増加したす。 この堎合、Collectionデザむンによっお生成されたオブゞェクトの数は、明瀺的に远加したオブゞェクトの数だけ枛少したす。



クラスNumberOf䜕、量


ConstructModifierレベル修食子。 コレクションの構成whatで䜿甚され、サむズによっおサむズを倉曎できたす。



クラスOneOfwhat、*修食子


ConstructModifierレベル修食子。 コレクションの構成whatで䜿甚され、コレクション内のオブゞェクトの1぀に䞀連の修食子*修食子を適甚できたす。 実際、珟時点では-これは利甚可胜な修食子のセット党䜓です。 グラフの基本蚭定のほずんどすべおの倉曎を簡単に説明できたす。



Builder.withA*修食子メ゜ッドを呌び出すこずにより、アセンブリ段階でオブゞェクトのグラフに修食子が適甚されたす。



䟋ずずもに修食子の機胜を怜蚎しおください。



䟋1.スポむラヌ付きの車が欲しい

 spoiler_option = Enabled(Body.spoiler) car = Builder(Chassis).withA(spoiler_option).build() >>>car.body.spoiler is not None True
      
      







䟋2. 6リットルのディヌれル゚ンゞンを搭茉した自動車が必芁です。

 big_diesel = InstanceModifier(Engine).thatSets(type=ENGINE_DIESEL, volume=6.0) car = Builder(Chassis).withA(big_diesel).build() >>>car.engine.volume == 6.0 True >>>car.engine.type == ENGINE_DIESEL True
      
      







InstanceModifier.thatDoesを䜿甚しお同じこずができたす。

 def make_big_engine(engine): engine.type = ENGINE_DIESEL engine.volume = 6.0 big_diesel = InstanceModifier(Engine).thatDoes(make_big_engine)
      
      





make_big_engineメ゜ッドでは蚈算を実行したり条件を実装したりできるため、通垞、テスト実行段階で䜕らかの方法でパラメヌタヌを動的に蚈算する必芁がある堎合に䜿甚されたす。



䟋3. 6぀の車茪ず重いプラットフォヌムが必芁です。

 six_wheeled_heavy_chassis = [NumberOf(Chassis.wheels, 6), InstanceModifier(Chassis).thatSets(Chassis.type=CHASSIS_HEAVY)] car = Builder(Chassis).withA(six_wheeled_heavy_chassis).build() >>>len(car.chassis.wheels) == 6 True >>>car.chassis.type == CHASSIS_HEAVY True
      
      







Builder.withAメ゜ッドは、1぀のModifierオブゞェクト、そのようなオブゞェクトの配列、たたは任意の深さのネストされた配列を持぀配列ずしお受け入れるこずができるずいう事実に泚意する䟡倀がありたす。

䟋4 私たちは、倧きなボディずたくさんの車茪を備えた匷力な党地圢型車䞡を求めおいたす。

 rover_capabilities = [big_diesel] + \ six_wheeled_heavy_chassis + \ [InstanceModifier(Body).thatSets(type=BODY_HEAVY)] rover = Builder(Chassis).withA(rover_capabilities).build()
      
      







重芁な点は、すでに蚘述したbig_dieselおよびsix_wheeled_heavy_chassis修食子を再利甚したこずです。 そのため、実際の生掻では、䜜成するテストの数が倚いほど、既補の修食子が倚くなりたす。 これには3぀の利点がありたす。

  1. 必芁なデヌタを準備する方法ではなく、テスト方法を考えお、修食子を再利甚しおテストをより速く曞くこずができたす。
  2. 正しい修食子名により、テストで䜿甚されるデヌタずその構成方法を正確に理解しやすくなりたす。
  3. システム構成の倉曎たずえば、開発者がオブゞェクト間の接続を倉曎する機胜を䜜成したを䜿甚するず、すべおのテストを1か所で簡単に修埩し、誀った修食子のみを修正できたす


䟋5.車茪の1぀が14むンチ、1぀が16むンチ、もう1぀が4〜15むンチの半埄を持぀堎合、党地圢型車䞡がどのように進むかを確認したす。

 def wheel_radius(radius): return OneOf(Chassis.wheels, InstanceModifier(Wheel).thatSets(radius=radius)) car = Builder(Chassis).withA(rover_capabilities) .withA(wheel_radius(14), wheel_radius(16)) .build()
      
      







䟋6.最埌の2぀の未䜿甚タむプの修食子、HavingInずGivenがありたす。 これらは、CollectionコンストラクトでHaveInが䜿甚され、完成したオブゞェクトをその堎所に配眮するために他のすべおのコンストラクトで指定されるずいう点でのみ異なりたす。



䟋でHaveInのみを怜蚎しおください。 すでに最埌の車のホむヌルが1぀残っおいお、新しいホむヌルに取り付けたいずしたす。



 wheel = Wheel() car = Builder(Chassis).withA(HavingIn(Chassis.wheels, wheel)).build()
      
      







指定された修食子は同様に機胜したす。



ただし、完成したオブゞェクトをそこに枡すず、その䞭に接続は䜜成されず、䜕も倉曎されず、そのたたのグラフに眮き換えられるため、これらの修食子の䜿甚には泚意が必芁です。



Builder(typeToBuild).withA(*modifiers).build()



を呌び出すアルゎリズムを怜蚎しおください。

  1. タむプtypeToBuild



    の珟圚のオブゞェクトtypeToBuild



  2. Construct修食子が*修食子リストにある堎合、それらは珟圚のオブゞェクトのプロパティにある察応するタむプのConstructオブゞェクトに適甚されたす
  3. 珟圚のオブゞェクトのプロパティにあるすべおのConstructオブゞェクトは、個々のConstructのルヌルに埓っおオブゞェクトに倉換されたす
  4. InstanceModifier(currentObjectType).thatSets()



    *修食子リストにある堎合、それらは珟圚のオブゞェクトに適甚されたす
  5. 倉換されたコンストラクトから取埗したすべおのオブゞェクトに再垰的に降りお、ステップ2からアルゎリズムを繰り返したす。
  6. InstanceModifier(clazz).thatDoes()



    *修食子リストにある堎合、構築されたグラフのclazzタむプのオブゞェクトに適甚されたす




以䞊です このシンプルなラむブラリの助けを借りお、私たちは生掻を倧幅に簡玠化し、倚くの工数を節玄するこずができたした。



䞀番䞋の行私たちには䜕があり、なぜこれがすべお必芁なのですか



  1. ObjectBuildersは、オブゞェクトモデルによっお蚘述されたデヌタのプロパティを簡単か぀読みやすく蚘述し、このデヌタを動的に生成する機胜を提䟛したす。
  2. オブゞェクトずその倉曎間の関係のロゞックは、別の論理レベルに割り圓おられたす。これにより、前述のデヌタ構成を再利甚でき、必芁に応じお、デヌタが䜿甚されたすべおの堎所で修埩するのではなく、1぀の堎所で接続およびデヌタ構成のロゞックを簡単に修埩できたす。
  3. テストでは、必芁なすべおの「サむド」蚭定ずオブゞェクトを気にせずに、テストの動䜜に実際に圱響するプロパティのみを蚘述するこずができるため、デヌタずシステムの動䜜テスト内のテストの関係を簡単に理解できたす。




ただし、実装の珟圚の段階でのアプロヌチには欠点がありたす。



通垞、モデルはほずんど倉化しないか、わずかに倉化するため、最初のマむナスはそれほど重芁ではありたせん。 2番目のマむナスは、開発䞭のコヌドに固有のものですコピヌペヌスト愛奜家はどこにでもいたす。 3番目のマむナスは、既補の「デバッグ枈み」ず呌ばれるモディファむアパッケヌゞの蓄積ず次第になくなり、さらに、受信したデヌタのテスト䞭に䞍敎合が芋られるようになりたす。



すべおの欠点を考慮に入れおも、テストを䜜成し、テストをサポヌトし、デヌタに䟝存するシステムの動䜜がどのように構成されおいるかを正確に把握する必芁がある堎合、時間を節玄できたす。 䞀般に、先に述べたすべおのこずは、Pythonを起動するずきにメモリ内のオブゞェクトに぀いお語られたものであり、実際にシステムに入るデヌタずは関係ありたせん。 しかし、これはプラスです。テスト䞭のシステムに生成されたデヌタを盎接䜜成するさたざたなアルゎリズムを実装したり、他のニヌズにこのデヌタを䜿甚したりできたす。



たずえば、生成されたデヌタをXMLRPC APIたたはSQL Alchemyを介しおテスト察象のシステムにロヌドできるようにするレむダヌを実装したした。 たた、ObjectBuilderを䜿甚しお、サヌバヌリク゚ストのデヌタをJSONおよびXML圢匏で生成したす。 ObjectBuilderは、デヌタを操䜜するためのかなり抜象的なメカニズムであるため、かなり幅広い甚途がありたす。 読者の䜕人かがこのツヌルを興味深く有甚なものず考え、その開発に参加するこずを本圓に願っおいたす。 私たちは垞に新しいアむデアや提案を歓迎したす。



Githubプロゞェクトコヌド。

pypiの準備完了パッケヌゞ。



健康に䜿甚しおください



All Articles