前の資料
「モバイルDelphiアプリケーション用のDBMSの選択」では 、その名前が示すように、そのデータの保存とほとんどの処理を担当するアプリケーションサブシステムの開発の第1段階が示されました。 「ほとんど」についての明確化が理由で行われました。最終的には、ストアドプロシージャ(CP)を使用する機能のために指定された選択肢がInterbase DBMSに落ちたためです。それらを呼び出すタスク。
この特定のケースでのテストの必要性をよりよく理解するために、説明されたプロジェクトでは、かなり高い品質レベルが最初に設定され、特に主要なCPをチェックするセルフテストにより、手順で実装された機能の面でのメンテナンスが達成されたことに注意するアプリケーションの重要な機能を担当します-
推奨システム )。 このようなテストを編成する方法の1つ(
DUnitXとXMLに基づく)は、この記事の主題です。
何か良いものはありましたか?
あなた自身がすでに適用している場合、または自動テストの利点を説明している文献に精通している場合、この半水セクションは、特に理論を含まないためスキップできますが、この特定のプロジェクトにもたらされる利点の例を示します。
忘れられた中断のために常に空の結果のようなプリミティブで愚かな簡単に検出可能なエラー、つまり、最小限のテストデータに記述されたコードの義務的な手動テストですぐに現れるすべてのエラーを破棄すると、問題が解決された2つのカテゴリを区別できます:
- 基本的に同様のテストが記述されているため、最も明らかなものは、必要なアルゴリズムが何らかの方法で誤って実装されている場合の論理エラーの層です 。 自動テストでは、このような欠陥を最大12個特定することができました。これらのケースの手動検査は、特別な特別に選択されたテストデータが必要なため、ほとんど見つかりませんでした。
- 歩留まりはそれほど豊かではありませんが、許容できないエラーにつながるのは、 Interbase自体の欠陥です 。 テストのおかげで、 不安定なカーソルやHPへの接続などの微妙なニュアンスに不慣れなことが起こりました 。そのパラメーターはクエリテーブルから取得されます。
自動テストの楽しさを知った経験の浅い開発者は、どこでもどこでもそれを使用したいと思うかもしれません。しかし、世界平和は確かに素晴らしいですが、そのような願望の価格はいくらですか? 著者自身がテストに費やしました-テスト計画の作成、テストデータの選択、約3〜4週間(そして、以下で説明するように、起動のためのインフラストラクチャを作成する時間は考慮されません)。 したがって、それぞれのケースで利点とコストを個別に相関させることをお勧めします。
言葉から行為へ
自動テストが満たす必要のある技術要件を事前に定式化して、実際の部分に移りましょう
- クロスプラットフォーム :Windowsおよびモバイルデバイスで起動します。 デスクトップOSは非常に明白な理由で必要です。テストは何倍も高速に実行されるため、テストが開発され、その後モバイルOSでの最終実行が進行中です。
- DUnitXライブラリの使用-DUnitとは異なり、着実に開発されており、FireMonkeyには既製のグラフィカルインターフェイスがあります(サイズは中程度ですが)。すべてのプラットフォームで同じです。
- テストの実際のセマンティックコンテンツ(テストデータ、実行パラメーターを含むXPのリスト、期待される結果)からの起動インフラストラクチャ(DUnitX部分)の分離は、すべてXMLファイルに保存する必要があります 。 この要件によりタスクは多少複雑になりますが、利点もあります:テストの本質は整頓されたままであり、サービスDelphiコードのみがそれらを複雑にしません。 そのようなXMLの小さな断片は、言われたことをよりよく理解するのに役立ちます(それへの参照は、本文の後半にあります)。
<?xml version="1.0" encoding="utf-8"?> <> <__> <_> < ="SHOPPING_LIST"/> </_> </__> <__/> <> <>...</> ... <> <_> < ="SHOPPING_LIST"> <> <ID ="">1</ID> <NAME =""> </NAME> <ADD_DATE ="__">1.2.2015</ADD_DATE> </> </> < ="LIST_ITEM"> <> <ID ="">1</ID> <LIST_ID ="">1</LIST_ID> <GOODS_ID ="">107</GOODS_ID> <AMOUNT ="">1</AMOUNT> <ADD_DATE ="__">25.2.2015 15:12</ADD_DATE> </> ... </> </_> < ="RECOMMEND_GOODS_TO_EMPTY_LIST" _=""> <> <_> <TARGET_DATE ="__">16.9.2014</TARGET_DATE> </_> </> ... <> <_> <TARGET_DATE ="__">1.3.2015</TARGET_DATE> </_> <> <> <GOODS_ID ="">107</GOODS_ID> <RECOMMENDATION_ID ="">0</RECOMMENDATION_ID> <ACCURACY ="">0.75</ACCURACY> </> </> </> ... </> </> <>...</> ... </> </>
実際のファイルは、この例よりもはるかに大きく(3,000行から4,500行まで)、テキスト形式で編集するのは合理的ではないようです-テストで既にミスをする可能性が非常に高くなるため、XMLを操作するテーブルモードを備えた専用の
Altova XMLSpyエディターが使用されました。 このツールのおかげで、タグやネストなどに気を取られることなく、本質に完全に集中できます。
DUnitX拡張
選択したライブラリを使用すると、任意のクラスにテストを配置し、次のような特別な属性を使用して、それに対応するメソッドをマークできます。
[TestFixture] TTestSet = class public [SetupFixture] procedure Setup; [TearDownFixture] procedure Teardown; [Setup] procedure TestSetup; [TearDown] procedure TestTeardown; [Test] procedure Test1; [Test] [TestCase(' 1', '1,1')] [TestCase(' 2', '2,2')] procedure Test2(const IntegerParameter: Integer; const StringParameter: string); end;
ここで、
TTestSet
は2つのテストのテストセット(ライブラリに関してはフィクスチャ)であり、2番目のテストはこれらのオプションの両方で数回実行されます。 ただし、このような標準的な方法は、コンパイル段階でテストとパラメーター値の数が静的に設定され、テストセットのリスト(XMLファイルごとに1つ)と各テストのリストの両方を
動的に
形成する必要があるため、完全に適していませんそれら(「Test」タグによって対応するファイルから取得)。
出現した障害は、プラグインメカニズムにより簡単に克服できます。DUnitXを使用すると、セットを柔軟に作成し、必要に応じてセットを埋めることができます。 さらに、属性に基づく「ボックス化」メカニズムもプラグインとして実装されています(
DUnitX.FixtureProviderPlugin.pasを参照)。これは、十分にテストされており、作業する際に驚くことはないことを意味します。
モジュールの「インターフェース」部分
結果のクラスからの新しいプラグインでモジュールの検討を開始し、メソッドの実装は少し後にします。
unit Tests.XMLFixtureProviderPlugin; interface implementation uses DUnitX.TestFramework, DUnitX.Utils, DUnitX.Extensibility, ... Xml.XMLDoc, {$IFDEF MSWINDOWS} Xml.Win.msxmldom {$ELSE} Xml.omnixmldom {$ENDIF}; type TXMLFixtureProviderPlugin = class(TInterfacedObject, IPlugin) protected procedure GetPluginFeatures(const context: IPluginLoadContext); end; TXMLFixtureProvider = class(TInterfacedObject, IFixtureProvider) protected procedure GenerateTests(const Fixture: ITestFixture; const FileName: string); procedure Execute(const context: IFixtureProviderContext); end; TDBTests = class abstract ... public procedure Setup; virtual; procedure Teardown; virtual; procedure TestSetup; virtual; abstract; procedure TestTeardown; virtual; abstract; procedure Test(const TestIndex: Integer); virtual; abstract; end; TXMLBasedDBTests = class(TDBTests) private const TestsTag = ''; ... private FFileName: string; FXML: TXMLDocument;
最初の2つのクラス
TXMLFixtureProviderPlugin
および
TXMLFixtureProvider
、プラグインの既存のシステムに埋め込むために必要であり、それらのメソッドの実装によってのみ興味深いものです。 次の
TDBTests
もあまり関心がありません。これは
TDBTests
、DB固有のものをカプセル化する目的で階層に割り当てられるため、相続人
TXMLBasedDBTests
直接アクセスする必要があるためです。 彼の責任は彼の人生の段階に関連しています。
- テストの実行を担当するアプリケーションの起動直後に、DUnitXはテストスイートのリストを生成します。この時点で、指定されたクラスのオブジェクトは、「Tests」ノードの子ノードのインデックスを返すSpecifyTestIndexesメソッドを使用して作成されます(上記のXMLフラグメントを参照)。 それを実装するとき、少しの血でうまくいくことはできません-子孫ノードの数を見つけて、条件付きで、1からNまでのインデックスのシーケンスを返すだけです、最初に、いくつかのノードはコメントであり、テストを一時的に無効にすることもできます(削除する代わりにファイル)。
その結果、受信したインデックスごとに、テストがセットに追加されます(厳密にはテストケース)。 - 一般的な場合、この段階は存在しない場合がありますが、ユーザーが(ボタンを押すことで)テストを実行するコマンドを受け取った場合、この一連の呼び出しは次のようになります。
Setup
Test
が繰り返し実行され、最初の段階で受け取ったインデックスが渡されます。 各呼び出しの前にTestSetup
、完了後にTestTeardown
続きます。Teardown
メソッドの実装
クラスの目的を理解した後は、残りのコードのデモを行います。
TXMLFixtureProviderPlugin
procedure TXMLFixtureProviderPlugin.GetPluginFeatures(const context: IPluginLoadContext); begin context.RegisterFixtureProvider(TXMLFixtureProvider.Create); end;
TXMLFixtureProvider
procedure TXMLFixtureProvider.Execute(const context: IFixtureProviderContext); var XMLDirectory, XMLFile: string; begin {$IFDEF MSWINDOWS} XMLDirectory := ; {$ELSE} XMLDirectory := TPath.GetDocumentsPath; {$ENDIF} for XMLFile in TDirectory.GetFiles(XMLDirectory, '*.xml') do GenerateTests ( context.CreateFixture(TXMLBasedDBTests, TPath.GetFileNameWithoutExtension(XMLFile), ''), XMLFile ); end; procedure TXMLFixtureProvider.GenerateTests(const Fixture: ITestFixture; const FileName: string); procedure FillSetupAndTeardownMethods(const RTTIMethod: TRttiMethod); var Method: TMethod; TestMethod: TTestMethod; begin Method.Data := Fixture.FixtureInstance; Method.Code := RTTIMethod.CodeAddress; TestMethod := TTestMethod(Method); if RTTIMethod.HasAttributeOfType<SetupFixtureAttribute> then Fixture.SetSetupFixtureMethod(RTTIMethod.Name, TestMethod); if RTTIMethod.HasAttributeOfType<TearDownFixtureAttribute> then Fixture.SetTearDownFixtureMethod(RTTIMethod.Name, TestMethod, RTTIMethod.IsDestructor); if RTTIMethod.HasAttributeOfType<SetupAttribute> then Fixture.SetSetupTestMethod(RTTIMethod.Name, TestMethod); if RTTIMethod.HasAttributeOfType<TearDownAttribute> then Fixture.SetTearDownTestMethod(RTTIMethod.Name, TestMethod); end; var XMLTests: TXMLBasedDBTests; RTTIContext: TRttiContext; RTTIMethod: TRttiMethod; TestIndex: Integer; begin XMLTests := Fixture.FixtureInstance as TXMLBasedDBTests; XMLTests.FileName := FileName; RTTIContext := TRttiContext.Create; try for RTTIMethod in RTTIContext.GetType(Fixture.TestClass).GetMethods do begin FillSetupAndTeardownMethods(RTTIMethod); if RTTIMethod.HasAttributeOfType<TestAttribute> then for TestIndex in XMLTests.DetermineTestIndexes do Fixture.AddTestCase( RTTIMethod.Name, TestIndex.ToString, '', '', RTTIMethod, True, [TestIndex] ); end; finally RTTIContext.Free; end; end;
TDBTests
procedure TDBTests.Setup; begin
TXMLBasedDBTests
procedure TXMLBasedDBTests.AfterConstruction; begin inherited;
GUI
自動テストの要件には、起動を制御してその結果を表示できる
既製のフォームが記載されていました。これは
DUNitX.Loggers.MobileGUI.pasに関するもので、わずかな
外観上の改善が適用されました。 意図的に1つのテストに失敗した3つのプラットフォームでの実行結果を以下に示します。
- Windows(実行時間7秒)


- Android(実行時間35秒)


- iOS(実行時間28秒)

