0 フォロワー · 14 投稿

ソフトウェアテストは、テスト対象のソフトウェア製品またはサービスの品質に関する情報を関係者に提供するために行われる調査です。

記事 Toshihiko Minamoto · 10月 16, 2025 2m read

私が先週リリースしたInterSystems Testing Managerの新しいバージョンでは、@Timothy Leavittの優れた
テストカバレッジツールが追加され、私は2025年度Developer Toolsコンテストに出品しました。

こちらは、IPMプロジェクトのユニットテストが、IPMリポジトリでソート順を上書きできると思われる機能をまだカバーしていないことを示すティザー的なスクリーンショットです。

88行目が開発者への警告として赤くハイライトされていることに注目してください。

VS Codeのエクスプローラービューには「バッテリーインジケーター」風のアイコンが表示されており、このクラスのメソッド内の実行可能な行のうち、テストでカバーされているのが76%のみであるため、黄色になっています。 インジケーターにカーソルを合わせると、さらに詳しい情報が表示され、メソッドのカバレッジ(9個中8個)が、エディター内のオプションのテストカバレッジツールバーに第2のインジケーターとして表示されます。

VS Codeでは、しきい値を設定できるほか、赤・黄・緑が区別しにくい場合には、色自体も構成できます。

いかがでしょうか? すでにInterSystemsの%UnitTestフレームワークをご利用中であれば、ぜひご自身でもお試しください。 ご意見・ご感想は大歓迎です。コンテストの投票への投票もぜひお願いします。投票は米国東部時間で8月3日(日)深夜まで受け付けています。

0
0 20
お知らせ Toshihiko Minamoto · 10月 14, 2025

%UnitTestフレームワークのユーザーは、InterSystems Testing Manager拡張機能の最新リリース(v2.0.0)を@Timothy Leavittの素晴らしいテストカバレッジツールと組み合わせることで、VS Code内でテストカバレッジ情報を取得できるようになりました。

上部にテストカバレッジのペインが表示されています。左側のテストエクスプローラーと併せて確認しやすいように、右側のセカンダリサイドバーに移動させました。

直近のテスト実行(テストカバレッジツール自体のユニットテストすべて)の結果、TestCoverage.Procedures内のBitValueメソッドはカバーされていましたが、BitCountメソッド(および他の6つのメソッド)はカバーされていないことに注目してください。 TestCoverageパッケージの全体で43.08%の実行可能行のみが、ユニットテストによってカバーされていました。

InterSystems Testing Managerに対するこの大幅なアップグレードを、現在開催中のDeveloper Toolsコンテスト に出品します。 もし良いと思っていただけたら、来週の投票でぜひ応援してください!

0
0 29
お知らせ Toshihiko Minamoto · 8月 13, 2024

%UnitTest framework を使用してユニットテストを構築したことがある場合、またはこれから構築しようとお考えの場合は、InterSystems Testing Manager をご覧ください。

VS Code を離れることなく、ユニットテストの閲覧、実行またはデバッグ、過去の実行結果の表示が可能になりました。

InterSystems Testing Manager は、ObjectScript 拡張機能がサポートするソースコード場所のパラダイムに対応しています。 ユニットテストクラスは、VS Code のローカルファイルシステム('client-side editing' パラダイム)またはサーバーネームスペース('server-side editing')のいずれかでマスターできます。 いずれの場合でも、実際のテストの実行は、サーバーネームスペースで発生します。

フィードバックをぜひお送りください。

0
0 76
記事 Toshihiko Minamoto · 2月 26, 2024 10m read

   

Hello, community!

IrisApiTester アプリを作成した後、それにもっと可能性があることに気付き、いくつか調整することで強力なコラボレーションツールになるのではないかと考えました。

そこで、以下の事について検討してみました。

  • API コレクションをチーム全体で共有できるか?
  • ユニットテストの実行に使用できるか?
  • 統合テストにはメリットがあるか?
  • CI/CD 継続的インテグレーションレイヤーを追加するとどうなるか?

可能な答えを考えた末、試してみることにしました。 作業を終えると、すべての回答が(ある程度)肯定であることがわかりました。 最終的には、この記事を書いて、この経験を皆さんと共有することに決めました。 知識の交換に役立ち、できればアプリケーションを一緒に改善していければと思います。

この記事は役に立つと思います。 新しいアプリケーションを検討するきっかけになるかもしれませんし、アジャイルな方法で実行できることを知らなかったテストを、このヒントでやっと実行できるようになることに気付くでしょう。

簡潔にするために、この記事を複数のセクションに分けています。 そのため、直接必要な箇所を読むことも、全文を読むこともできるように構成されています。

手順にしたがってテストを実行する場合は、すでに IrisApiTester Docker イメージをダウンロード済みで、インスタンスを実行していることが前提です。 そうでない場合は、必要なデータは以前に書いた記事に含まれています:

https://community.intersystems.com/post/iris-api-tester

 

ポイント 1 - チーム間で Postman コレクションを共有する:

最初に考えたのは、自分の Postman コレクションをテストと共に同僚全員と共有できるのか、ということです。その解決策は思った以上にかなり単純なものでした。

1 つまたは複数の API コレクションを含むリポジトリに Postman を接続できることがわかったのです。 同時に、Postman には、ターミナルなどに切り替えずに Postman からコレクションの更新、コミット、およびその他の操作の実行を行える Git クライアントも含まれていました。 みんなは知っていたことかもしれませんが、私は知りませんでした (^^;)

さぁ、準備はいいですか?! これから、API コレクションを含むリポジトリに Postman を接続する方法を説明します。

例として、GitHub にリポジトリを作成しました。 また、Bitbucket にも作成したので、 そちらのサービスの方が好みの場合は、それを使うこともできます。 私のリポジトリへのリンクです:

https://github.com/daniel-aguilar-garcia/postman-collection-test

必要であれば、フォークできます。 コレクションには、IrisApiTester のローカルインスタンス(localhost)にある Docker コンテナーを指す単純なテストがいくつか含まれています。

まず、リポジトリに Postman を接続します。

Postman を開き、「APIs」をクリックして API を作成します。

次に、コレクションの名前を選択して、GitHub アイコン(または Bitbucket)をクリックします。

認証を求められる場合があります。 求められたら、自分の GitHub ユーザーに接続することを許可してください。

許可したら、GitHub ユーザー、API を保存するリポジトリ、およびリポジトリのブランチを選択します。

(新規リポジトリでない場合は)接続が完了すると、リポジトリに含まれる API コレクションが表示されます。

API 内のすべてのコレクションはメニューにも表示されます。

コレクションを変更すると、Git セクションがすぐに更新されるため、 コミット、プル、プッシュなどの操作を実行できます。

Postman の有料ライセンスを使用している場合は、共有ワークスペースを作成するだけで開発チーム全員と接続することができますが、 無料のアカウントを使用している場合は、開発チームのすべてのコンピューターで上記のすべての手順を繰り返す必要があります。

上記の手順によって、チームメンバーとコレクションを共有し、最新状態に維持するという最初のポイントを完了しました。 唯一の欠点は、無料アカウントを使用している場合に生成できる API が 3 つまでということです。 その場合は、同じリポジトリにすべてのコレクションを配置すると、問題を介できます。

 

ポイント 2 - ユニットテスト:

次に解決する課題は、API テストだけでなく、ユニットテストの実行にもツールを使用する方法に関係しています。 この時点で、新しいエンドポイントをテストごとに作成せずに、一番速くテストを生成する方法は何かについて考えてみました。その答えが以下です。

ウェブアプリを /run パスに作成しました。

また、API のクラスも作成しました。 次のルートを作りました(リポジトリでは、Example パッケージの TestRoutes.cls クラスです)。

このウェブアプリとルートを使用すると、すべてのクラスにいわゆる「自動公開」メソッドを確立できます。 API にクラストとメソッド名を渡すだけでこれを達成できます。 以下の例を見てください。

localhost:52773/run/IrisNewman.Example.TestMethods/TestOK

この場合、以下のように指定しました。

/run -> ウェブアプリのパス

/IrisNewman.Example.TestMethods -> 実行するメソッドが配置されるクラス

/TestOK -> 実行するメソッド

TestOK メソッドでは、ユニットテストの単純なチェック操作が実行されます。

ClassMethod TestOK() As%Status
{
    Set testNumber = 1Set a = 1Set b = 2Set res = ..SumNumbers(a,b)

    Do..assert(res,3,$G(%methodname),testNumber)

    Quit$$$OK
}

 

このクラスでは、独自の assert メソッドを定義しているのが特徴です。 このメソッドによって、パラメーター 1 と 2 に渡される値が同等であることを確実にしています。 パラメーター 3 はメソッドの実際の名前で、4 はメソッド内のアサーションの数です。 これらの最後の 2 つのパラメーターは、アサーションでエラーが発生した場合に詳細なメッセージを返すために使用されます。 比較されたときに値が一致しない場合、一般のスローで例外をスローし、リクエストのヘッダーに 500 のレスポンスコードを送信します。 すると、Newman のレポートでそのテストがエラーにマークされます。

このアプローチでは、以下の操作のみが残っています。

  • Postman コレクションへの呼び出しをメソッドに含める。
  • 変更をコミットする。 

そうすれば、全員のリポジトリでコレクションを更新させることができるようになります。

 

ポイント 3 - 統合テスト:

前の手順と同じように、統合テストに使用されているウェブアプリを再利用することができます。 テストの例として、データベースにユーザーを追加しましょう。

localhost:52773/run/IrisNewman.Example.TestMethods/InsertPerson

ここでは、以下の項目について指定されています。

/run -> ウェブアプリのパス

/IrisNewman.Example.TestMethods -> 実行するメソッドが配置されるクラス

/InsertPerson -> 実行するメソッド

InsertPerson メソッドでは、すべての操作をトランザクション内で実行し、求める値が格納されたことをアサーションで確認しています。 最後に、変更を元に戻すロールバックが含まれています。

ClassMethod InsertPerson()
{

    Set testNumber = 1Set method=$G(%methodname)

    TSTARTSet person=##class(IrisNewman.Example.Entity.Person).%New()
    Set person.IDCard="11111111H"Set person.Name="Incognito Guy"Set person.Address="False Street, 123"Set person.City="Springfield"Set res = person.%Save()

    Do..assert(res,1)

    Set personReaded = ##class(IrisNewman.Example.Entity.Person).%OpenId(person.IDCard)

    Do..assert(personReaded.Name,"Incognito Guy",method,testNumber)

    TROLLBACKQuit$$$OK
}

 

更新や削除などの操作も同じようにテストできるため、必要な統合テストの実行が可能となります。

 

ポイント 4 - CI/CD の追加:

「これはこれでいいけれど、もっと自動化させたい。 同僚のテストを自動的に起動するにはどうすればいいのか」と思ってていませんか?

このために、アプリに新しいエンドポイントを作成しました(Postman ではなくブラウザで起動する必要があります)。

http://localhost:52773/pull_and_run_tests

このエンドポイントは、Git または Bitbucket リポジトリから最新バージョンのコレクションをダウンロードします。 また、それに対してテストを実行し、画面上にテスト結果のレポート(Newman)も表示するようになっています。

pull_and_run_test エンドポイントを実行するには、repository.cfg ファイルに、テストのコレクションを保存するリポジトリへのアクセスデータを構成する必要があります。

このとき、コミットするたびに自動的にすべてのテストを実行するワークフローをリポジトリに追加すればよくなるのではないかという考えがありました。

このための新しいエンドポイントを作ることがアイデアとして浮かびました。

http://localhost:52773/pull_run_and_send_google

これは、ApiTesting Docker イメージをダウンロードしてからリポジトリからコレクションをダウンロードし、テストを実行して Google Chat に結果の HTML ファイルの URL と共にメッセージを送信します。

これが機能するには、repository.cfg ファイルで、メッセージの受信先である Google Chat チャンネルのウェブフックの URL を構成する必要があります。

次の図に、リポジトリにコミット完了ごとのワークフロー実行の例を示します。

以下は、例として使用した GitHub ワークフローのコードです。

name:LaunchTestson:
  push:
    branches:
      -mainjobs:
  build:
    runs-on:ubuntu-latest
    steps:
    -name:Checkoutsourcecode
      uses:actions/checkout@v2
    -name:Clonetheirisapitesterrepository
      run:gitclonehttps://github.com/daniel-aguilar-garcia/irisapitester.git
    -name:RaiseDockerCompose
      run:docker-composeup-d
      working-directory:./irisapitester
    -name:WaitforDocker
      run:sleep30
    -name:SendreporttoGoogleChat
      id:send_report_google
      run:|

        $(curl -s http://localhost:52773/pull_run_and_send_google)

 

では、受信するメッセージの例を見てみましょう。

(この例では URL のみを送信していますが、 失敗したときにのみ送信するか、「executed Ok / KO」と URL をレポートに送信するようにプログラムすることもできます。)

注意: メッセージ送信の機能を使用する場合は、テスト結果の HTML を保存するネットワークストレージにマッピングすることをお勧めします。 Docker インスタンスはワークフロー中に生成されて破棄されるため、そこに保存しても消えてしまいます。 この問題を解決するには、Docker ファイルへのパスでネットワークボリュームを追加し、IrisNewman/Api.cls クラスの executionsTestPath パラメーターのテストストレージパスを変更します。

 

上記を行うと、自動化しようとしているすべてのテストの大部分が対応されたことになります。 このワークフローは手順の例として使用しているため、テストリポジトリに追加しましたが、 ソフトウェアのリポジトリに保存することもできます。 もう 1 つのオプションは、ソフトウェアのコードと実行されるコレクションを直接同じリポジトリに配置することです。 この場合は、コミットごとに、更新した Postman コレクションのテストが実行され、操作の結果が自動的に通知(この場合は Google Chat)されます。

この記事に興味を持っていただき、このアプリケーションまたは作業方法を日常業務で使っていただけたら幸いです。少なくとも、このアイデアがどこかで役立てられればと思います。

ご質問や不明な点は、コメントセクションにぜひ投稿してください。 ご質問を歓迎しています。

お読みいただきありがとうございました!!


 

0
0 141
記事 Toshihiko Minamoto · 2月 16, 2024 3m read

コミュニティの皆さん、こんにちは!

IrisApiTester の新しいバージョンを公開しました。以下のようないくつかの新機能が含まれています。

  • 外部リポジトリ: コレクションのファイルをウェブページに手動でドラッグする代わりに、リポジトリ(GitHub または Bitbucket)のコレクションを使用できる機能を追加しました。
  • プルして実行: リポジトリの変更を自動的にプルしてテストを実行する新しいエンドポイントを作成しました。
  • ユニットテスト / 統合テスト: ユニットテストと統合テストで IrisApiTester を使用する例を追加しました。
  • CI/CD: GitHub などのワークフローでコミットの完了ごとにコレクションのリポジトリの変更を自動的にプルし、テストを実行して Google Chat に結果を送信するための新しいエンドポイントを追加しました。

外部リポジトリ:

Postman のコレクションテストが格納される外部リポジトリを構成できるようになりました。

- プルして実行:
リポジトリの最新バージョンをプルし、テストを通過させてブラウザにレポートを表示します(ファイルをドラッグすなどの手動操作は不要です):
http://localhost:52773/pull_and_run_tests

-ユニットテスト / 統合テスト

ユニットテストへの新しいルート:
http://localhost:52773/run/<クラス名>/<メソッド名>

この新しいルート '/run' を使って、クラス名とメソッド名を URL に追加するだけで、あらゆるメソッドを公開できます。例:
http://localhost:52773/run/IrisNewman.Example.TestMethods/TestOK

統合テストにも使用できます。例を参照してください:
localhost:52773/run/IrisNewman.Example.TestMethods/InsertPerson

-CI/CD の便利なエンドポイント:

CI/CD への新しいルート:
このエンドポイントをワークフローに追加すると、コミットが完了するたびにテストの結果を自動的に Google Chat に送信できます。
http://localhost:52773/pull_run_and_send_google

IrisApiTester リポジトリの最新バージョンをダウンロードし、テストコレクションリポジトリの最新バージョンをダウンロードし、テストを実行して、テスト結果レポートの URL 付きのメッセージを Google Chat のウェブフックに送信します。

これらの新しい改善を使用するには、repository.cfg ファイルに必要なデータを入力する必要があります。

repository.cfg の例:

[repo]# プラットフォーム(現在サポートされているのは 'gitbub' と 'bitbucket' のみです)platform='github'# 公開リポジトリのリポジトリユーザーは空です。username=''# 公開リポジトリのリポジトリトークンは空ですapptoken=''# リポジトリの URL # Bitbucket の例: 'bitbucket.org/projectName/repoName.git'# GitHub の例:    'https://github.com/userName/repoName.git'repository='https://github.com/daniel-aguilar-garcia/postman-collection-test.git'# リポジトリ名repository_name='postman-collection-test'# リポジトリのコレクションのパスcollection_path='postman/collections/Test.json'# Google Chat のウェブフックwebhook_google='https://chat.googleapis.com/v1/spaces/AAAA2PmVItg/messages?key=XXXYYYzzzzz'

 

以下は、Postman Collection リポジトリの例です。

https://github.com/daniel-aguilar-garcia/postman-collection-test

このリポジトリには、GitHub ワークフローの CI/CD 機能の例も含まれます。ファイル: ../workflows/launch_test.yml

これらの新機能を楽しんでいただければ幸いです。

このアプリを気に入っていただけたなら、Python Contest で投票してください。

お読みいただきありがとうございました!

0
0 101
記事 Toshihiko Minamoto · 2月 7, 2024 2m read

コミュニティの皆さん、こんにちは!

私の IRIS Api Tester というアプリで使用する Postman コレクションのテストを作成する方法を説明します。

Newman とは?

Newman は、Postman コレクションを拡張可能な方法で自動的に実行できるコマンドラインツールです。 Newman でテストを作成することで、API エンドポイントの信頼性と正確性を確実にすることができます。 この記事では、Postman で Newman のテストを作成する方法と開始に役立つ実用的な例を紹介します。

Postman コレクションを作成したら:

テストスクリプトを書き始めることができます。

Postman のテストは、Postman スクリプトサンドボックスを使って JavaScript で記述します。 テストスクリプトを作成するには、リクエストを開いて「Test」タブに移動します。 ここで、API レスポンスを評価してその正確さを検証するカスタム JavaScript コードを作成できます。

例 1: レスポンスのステータスコードを確認する:

pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

 

例 2: レスポンスの特定のフィールドの有無を検証する:

pm.test("Response body contains name field", function () {
    pm.response.to.have.jsonBody('name');
});

 

例 3: レスポンス時間を確認する:

pm.test("Response time is less than 500ms", function () {
    pm.expect(pm.response.responseTime).to.be.below(500);
});

 

Postman でテストを作成し終えたら、コレクションをエクスポートして、こちらのチュートリアルの手順を実行できます。

または、YouTube でこちらの動画をご覧ください。

<iframe allowfullscreen="" frameborder="0" height="360" src="https://www.youtube.com/embed/6JJJ0a6dSmY" width="640"></iframe>

お読みいただきありがとうございました!!

0
0 537
記事 Toshihiko Minamoto · 1月 31, 2024 3m read

コミュニティの皆さん、こんにちは!!

Open Exchange に最新の「IRIS Api Tester」アプリケーションをアップロードしました。

InterSystems IRIS と Newman を使用した Docker プロジェクトで、素早く簡単に Postman コレクションをテストできます。

リポジトリをクローンするだけで、初期状態で使用できるようになっています: https://github.com/daniel-aguilar-garcia/irisapitester

docker-compose ファイルを実行します。

この URL をブラウザで開きます。

http://localhost:52773/csp/user/index.html

テストを Postman コレクションに追加します。

この例では、テストをコレクションの Test セクションに追加することでコレクションのすべてのエントリにグローバルテストを追加していますが、個別のテストを項目ごとに追加することもできます。

ここでは、リクエストのステータスコードが 200 になることをテストしています。ステータスコードが 200 でない場合には、レポートにエラーとして表示されます。

テストを追加したら、コレクションを JSON 形式でエクスポートします。

次に、JSON ファイルを IRIS Api Tester のホームページにドラッグし、「Run Test」ボタンを押します。

数秒後、レポートページにリダイレクトされます。

ここで、レポート内を移動して、すべてのテストを詳しく調べることができます。

ユーザーインターフェースを使わずにテストを起動したい場合は、このリクエストを使用できます。

テストを実行するための POST リクエスト:

localhost:52773/run_tests

Body の例:

{

"collection" : { "info": { "_postman_id": "79cfb5de-a2ab-4548-aa54-4a1712bf67a4", "name": "TestNewman", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "838575" }, "item": [ { "name": "test_ok", "event": [ { "listen": "test", "script": { "exec": [ "" ], "type": "text/javascript" } } ], "request": { "method": "GET", "header": [], "url": { "raw": "localhost:52773/test_ok", "host": [ "localhost" ], "port": "52773", "path": [ "test_ok" ] } }, "response": [] }, { "name": "test_ko", "request": { "method": "GET", "header": [], "url": { "raw": "localhost:52773/test_ko", "host": [ "localhost" ], "port": "52773", "path": [ "test_ko" ] } }, "response": [] } ], "event": [ { "listen": "prerequest", "script": { "type": "text/javascript", "exec": [ "" ] } }, { "listen": "test", "script": { "type": "text/javascript", "exec": [ "pm.test("Verificar código de cabecera", function () {", " pm.response.to.not.have.status(500);", "});" ] } } ] } }

次に、HTML レポートを取得します。

HTML レポートを取得するための GET リクエスト:

localhost:52773/show_report

必要であれば、エクスポートしたサンプルコレクションの JSON ファイルを残しているので、ぜひお試しください。

このアプリがお役に立てれば幸いです。

お読みいただきありがとうございました!!

以下は、デモ動画です ;-)

<iframe allowfullscreen="" frameborder="0" height="360" src="https://www.youtube.com/embed/6JJJ0a6dSmY" width="640"></iframe>

0
0 100
記事 Toshihiko Minamoto · 9月 20, 2023 14m read

主流となっているソフトウェア開発手法では、必ずテスト専用の項目が用意されています。 これは、デリバリーの品質を持続的に達成する上では欠かせないアプローチです。

テストには、以下の 2 種類があります。

  1. ホワイトボックステスト: これらは、ソースコードとアプリケーションの機能の品質を調べるテストです。 この種のテストには、以下のようなテストがあります。
    1. 静的解析: 静的解析ソリューションは、ソースコードの解析に使用されます(テスト時に機能は実行されません)。命名パターン、インデント、宣言された未使用の変数、コンポーネント間のインデックスの結合など、定義された条件が解析ソリューション内で評価されます。 一般に、開発環境内では、品質に問題のあるソースコードの行が指摘されるため、開発者は問題の解決に取り組むことができます。
    2. ユニットテスト: テストクラス(テストユニット)が作成されます。機能トピック(顧客の維持、トランザクションの収集など)ごとに 1 つのテストクラス(テストユニット)が作成され、テストスイート(一連のテストユニット)にまとめられます。アプリケーションモジュールごと(登録モジュール、販売モジュールなど)、またはアプリケーション全体に 1 つのスイートがあります。 一般に、検出された結果を含む HTML レポートが発行されます。
  2. ブラックボックステスト: アプリケーションのソースコードにアクセスせずに、アプリケーションのバイナリーで実行されるテストです。 この種のテストには、以下のようなテストがあります。
    1. パフォーマンステスト: データの読み込みと実行スクリプトは主要な機能に対して定義され、アプリケーションと実行環境が期待されるトランザクション数とユーザー数をサポートするかどうかを評価します。
    2. 進入テスト(ペンテスト): アプリケーションの IP アドレスとポート、ソリューションをホストする製品(Web サーバー、アプリケーション、データベース)、およびアプリケーションのエンドポイント(API、Web サービス、ファイル交換ディレクトリ、およびその他の対話ポイント)には、脆弱性分析ソリューションが使用されます。 通常、検出された脆弱性を記載する PDF レポートが作成されます。
    3. 機能テスト: テストアナリストとテスターは、アプリケーションのビジュアルインターフェースから機能的な欠陥(エンドユーザーの観点でエラーのある機能)を特定することを目的とした複数のテストシナリオを作成して実行します。 現在では、これらのビジュアルインターフェースからのテストも自動化するソリューションがあります。

テスト規律の重要な目標は、テストカバレッジ(テストが対応する機能)のスイートスポットを見つけることです。 私が関わっているプロジェクトでは、少なくとも機能の 75% を達成することが目標とされています。 大きな秘密は、テストの焦点をアプリケーションのビジネスレイヤーに当てることにあります。このレイヤーに、エンドユーザーに提供されるビジネスルールと機能要件の実装が存在するためです。 様々なビジュアルインターフェースと統合ポイントには、かなわずビジネスレイヤーが必要となります。 このため、機能レイヤーのテストを自動化する場合には、テストのカバレッジに適切な割合が設定されます。

この記事では、IRIS アプリケーションのビジネスレイヤーのユニットテストを自動化する方法を説明し、エンドユーザーに提供される IRIS アプリケーションで適切なテストカバレッジの割合と品質を達成する方法を紹介します。

テストするアプリのダウンロード

https://openexchange.intersystems.com/package/global-mindmap に移動し、Github ボタンをクリックします。 以下の手順に従ってください。

  1. プロジェクトをクローンします。
$ git clone https://github.com/yurimarx/global-mindmap.git
  1. プロジェクトのルートフォルダ内で docker build を実行します。
$ docker-compose build
  1. Docker コンテナーを実行します。
$ docker-compose up -d

アプリの実際の動作を見る

テストを開始する

テストするビジネスクラス

 
テストするビジネスクラスターゲット
Classdc.globalmindmap.GlobalMindMapService
  <div>
    <span style="color: #ffffff;">{</span>
  </div>   
  
  <div>
    <span style="color: #80bd66;">///</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">mindmap ノードを格納 </span>
  </div>
  
  <div>
    <span style="color: #85a6ff;">ClassMethod</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">StoreMindmapNode</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%DynamicObject</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%Status</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Try</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>   
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"topic"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">topic</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"style"</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"fontSize"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">fontSize</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"style"</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"color"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">color</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"style"</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"background"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">background</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"parent"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">parent</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"tags"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">tags</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%ToJSON</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"icons"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">icons</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%ToJSON</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"hyperLink"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">hyperLink</span>
  </div>   
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span>
  </div>   
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">Catch</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">write</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error name: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Name</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">            </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error code: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Code</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">            </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error location: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Location</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">            </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Additional data: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Data</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;"></span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>   
  
  <div>
    <span style="color: #85a6ff;">ClassMethod</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">GetMindmap</span><span style="color: #ffffff;">(</span><span style="color: #85a6ff;">Output</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%DynamicArray</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%Status</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Try</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span>
  </div>   
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Key</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">$ORDER</span><span style="color: #ffffff;">(</span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #d4b57c;">""</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Row</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;"></span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">While</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">'=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">""</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Push</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Key</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">hyperLink</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">,</span><span style="color: #d4b57c;">"hyperLink"</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">icons</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">,</span><span style="color: #d4b57c;">"icons"</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">parent</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">,</span><span style="color: #d4b57c;">"parent"</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">background</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">,</span><span style="color: #d4b57c;">"style"</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"background"</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">color</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">,</span><span style="color: #d4b57c;">"style"</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"color"</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">fontSize</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">,</span><span style="color: #d4b57c;">"style"</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"fontSize"</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">tags</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">,</span><span style="color: #d4b57c;">"tags"</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Nodes</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Get</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Row</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">topic</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">,</span><span style="color: #d4b57c;">"topic"</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Row</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Row</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">+</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Key</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">$ORDER</span><span style="color: #ffffff;">(</span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">Catch</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Write</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error name: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Name</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error code: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Code</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error location: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Location</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Additional data: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Data</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;"></span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>   
  
  <div>
    <span style="color: #80bd66;">///</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">mindmap ノードを削除する</span>
  </div>
  
  <div>
    <span style="color: #85a6ff;">ClassMethod</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">DeleteMindmapNode</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">id</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%String</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%Status</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Try</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Kill</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">id</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span>
  </div>   
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">Catch</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">write</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error name: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Name</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error code: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Code</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error location: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Location</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Additional data: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Data</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;"></span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">  </span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>   
  
  <div>
    <span style="color: #80bd66;">///</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">Has Content(コンテンツがあるかどうか): 1 - ある、0 - ない</span>
  </div>
  
  <div>
    <span style="color: #85a6ff;">ClassMethod</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">HasContent</span><span style="color: #ffffff;">(</span><span style="color: #85a6ff;">Output</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Result</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%String</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%Status</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Try</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">key</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">$ORDER</span><span style="color: #ffffff;">(</span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #d4b57c;">""</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">If</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">key</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">""</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">            </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Result</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"0"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">}</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">Else</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">            </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ff75f4;">Result</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"1"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">}</span>
  </div>   
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">Catch</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Write</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error name: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Name</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error code: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Code</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Error location: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Location</span><span style="color: #ffffff;">,</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">          </span><span style="color: #ffffff;">!</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"Additional data: "</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">?</span><span style="color: #d4b57c;">20</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">err</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Data</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">!</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">      </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;"></span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>   
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>
</div>

ビジネスクラスには、テストする機能が 3 つあります。

  1. StoreMindmapNode: mindmap ノードのデータをグローバルの形態でデータベースに記録するために使用される機能。
  2. GetMindmap: グローバルに格納された mindmap のすべてのノード、つまり mindmap 全体を返すために使用される機能。
  3. DeleteMindmapNode: データベースから mindmap ノードを削除するために使用される機能(ノードを格納するグローバルノードを削除します)。

テストスイート(テストケース一式)を作成する

src ディレクトリのルートに、テストスイートの名前でディレクトリを作成します。この例では、名前を UnitTests としました。

テストスイートテストケースを作成する

%UnitTest.TestCase を継承するテストクラスを作成する必要があります。 クラス名は、テストするクラス名 + Test の形式にすることをお勧めします。 この例では、クラス名を GlobalMindMapServiceTest としています。 クラスの内容を見てみましょう。

 
テストケースクラス
ClassUnitTests.GlobalMindMapServiceTestExtends%UnitTest.TestCase
  <div>
    <span style="color: #ffffff;">{</span>
  </div>   
  
  <div>
    <span style="color: #85a6ff;">Method</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">TestStoreMindmapNode</span><span style="color: #ffffff;">()</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"TestId"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">topic</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"TestTopic"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">parent</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">""</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">tags</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">icons</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">background</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"black"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">fontSize</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"arial"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">color</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"white"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">hyperLink</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"intersystems.com"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">globalmindmap</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">GlobalMindMapService</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">StoreMindmapNode</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Key</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">$$$</span><span style="color: #dcdcaa;">AssertEquals</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">globalmindmap</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">GlobalMindMapService</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">DeleteMindmapNode</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>   
  
  <div>
    <span style="color: #85a6ff;">Method</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">TestDeleteMindmapNode</span><span style="color: #ffffff;">()</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%Status</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"TestDelete"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">topic</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"TestDelete"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">parent</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">""</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">tags</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">icons</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">background</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"black"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">fontSize</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"arial"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">color</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"white"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">hyperLink</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"intersystems.com"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">globalmindmap</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">GlobalMindMapService</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">StoreMindmapNode</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">globalmindmap</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">GlobalMindMapService</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">DeleteMindmapNode</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Key</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">$ORDER</span><span style="color: #ffffff;">(</span><span style="color: #64c9ff;">^mindmap</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">$$$</span><span style="color: #dcdcaa;">AssertEquals</span><span style="color: #ffffff;">(</span><span style="color: #d4b57c;">""</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Key</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>   
  
  <div>
    <span style="color: #85a6ff;">Method</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">TestGetMindmap</span><span style="color: #ffffff;">()</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">{</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"TestGet"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">topic</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"TestGet"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">parent</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">""</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">tags</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">icons</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span><span style="color: #ffffff;">}</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">background</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"black"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">fontSize</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"arial"</span><span style="color: #d4d4d4;"> </span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">color</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"white"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">style</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">hyperLink</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"intersystems.com"</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">globalmindmap</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">GlobalMindMapService</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">StoreMindmapNode</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">globalmindmap</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">GlobalMindMapService</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">GetMindmap</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">.</span><span style="color: #ade2ff;">Result</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Count</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Result</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Size</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">$$$</span><span style="color: #dcdcaa;">AssertEquals</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">Count</span><span style="color: #ffffff;">,</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Do</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">globalmindmap</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">GlobalMindMapService</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">DeleteMindmapNode</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">data</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">id</span><span style="color: #ffffff;">)</span>
  </div>
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>   
  
  <div>
    <span style="color: #ffffff;">}</span>
  </div>
</div>

テストされる各機能について、「Test + テストされるメソッド名」の命名規則に従ったメソッドがあることに注意してください。 それぞれのテストメソッドが、テストの実行に必要なものすべてを行うことが重要です。 例えばテストの削除の部分では、まずレコードが作成されてから、それを使用して削除し、削除が機能しているかどうかがテストされます。

各メソッドについては、$$$Assert... への呼び出しが少なくとも 1 つ必要です。 このマクロによって、テスト条件そのものの実行と記録が行われます。 この例では、すべてのメソッドに $$$AssertEquals が使用されています。 この場合、テスト条件がビジネスメソッドの実行結果に等しい場合、テストは成功としてマークされ、そうでない場合は不成功とされます。 Assert にはいくつかのオプションがあります。詳細は、https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=TUNT_AssertX をご覧ください。

OnBeforeAllTests を使用してテストに必要なデータと条件を作成し、OnAfterAllTests を使用してデータと環境を元の状態にクリーニングすることも可能です。

テストを実行する

スイートとテストケースの準備ができたら、テストを実行しましょう。 これには、ターミナルでクラスが存在するネームスペースにアクセスする必要があります。例としてこの記事の手順をご覧ください。

  1. ターミナルのネームスペース IRISAPP で、ビジネスクラスとテストクラスを含むネームスペースに移動します。
USER>zn "IRISAPP"
  1. テストスイートのフォルダが存在するフォルダをポイントする必要があります(この例では src 内の UnitTests フォルダです)。
IRISAPP>Set ^UnitTestRoot="/opt/irisbuild/src"
  1. ユニットテストを実行します。
IRISAPP>do ##class(%UnitTest.Manager).RunTest("UnitTests")
  1. http://localhost:52773/csp/sys/%25UnitTest.Portal.Indices.cls?Index=1&$NAMESPACE=IRISAPP で結果を確認します。

結果を分析する

前の手順から得るレポートは、以下のレイアウトで返されます。

何が合格(緑)し、何に失敗(赤)したかを知ることができます。 赤をクリックすると、エラーが発生した場所を確認できます。

修正しましょう。

サンプルアプリケーションを気に入った方は、グローバルコンテストで投票してください。

0
0 154
記事 Toshihiko Minamoto · 5月 17, 2022 9m read

はじめに

前の記事では、ObjectScript Package Manager を使用してユニットテストを実行するためのパターンについて説明しました。 この記事では、さらに一歩踏み込み、GitHub Actions を使用してテストの実行とレポート作成を行います。 私の Open Exchange プロジェクトの 1 つである AppS.REST に CI を実行するのが、やる気の出るユースケースでしょう(この導入編の記事は、こちらにあります)。 この記事のスニペットが使用されている完全な実装は、GitHub でご覧ください。ObjectScript Package Manager を使って他のプロジェクトで CI を実行するためのテンプレートとして簡単に利用できます。

紹介する実装の機能は以下のとおりです。

  • ObjectScript パッケージの構築とテスト
  • codecov.io によるテストカバレッジ測定のレポート(TestCoverage パッケージを使用)
  • テスト結果に関するレポートのビルドアーティファクトとしてのアップロード

ビルド環境

GitHub Actions に関する完全なドキュメントはこちらにあります。この記事の目的に準じ、この例で紹介される側面だけを詳しく確認します。

GitHub Actions のワークフローは、構成可能な一連のイベントによってトリガーされ、順番に、または並行して実行できる多数のジョブで構成されています。 それぞれのジョブには一連のステップがあります。このサンプルアクションのステップは、少し後の方で詳しく説明します。 これらのステップは、GitHub で提供されているアクションへの参照で構成されているか、単にシェルコマンドである場合があります。 この例での最初のボイラープレートのスニペットは、以下のようになります。

# Continuous integration workflow
name: CI

# Controls when the action will run. Triggers the workflow on push or pull request
# events in all branches
on: [push, pull_request]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    
    env:
      # Environment variables usable throughout the "build" job, e.g. in OS-level commands
      package: apps.rest
      container_image: intersystemsdc/iris-community:2019.4.0.383.0-zpm
      # More of these will be discussed later...
    
    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # These will be shown later...

この例では、多数の環境変数が使用されています。 この例を ObjectScript Package Manager を使用して他のパッケージに適用する場合、ほとんどの変数を変更する必要はありませんが、一部には変更が必要です。

    env:
      # ** FOR GENERAL USE, LIKELY NEED TO CHANGE: **
      package: apps.rest
      container_image: intersystemsdc/iris-community:2019.4.0.383.0-zpm
      
      # ** FOR GENERAL USE, MAY NEED TO CHANGE: **
      build_flags: -dev -verbose # Load in -dev mode to get unit test code preloaded
      test_package: UnitTest
      
      # ** FOR GENERAL USE, SHOULD NOT NEED TO CHANGE: **
      instance: iris
      # Note: test_reports value is duplicated in test_flags environment variable
      test_reports: test-reports
      test_flags: >-
       -verbose -DUnitTest.ManagerClass=TestCoverage.Manager -DUnitTest.JUnitOutput=/test-reports/junit.xml
       -DUnitTest.FailuresAreFatal=1 -DUnitTest.Manager=TestCoverage.Manager
       -DUnitTest.UserParam.CoverageReportClass=TestCoverage.Report.Cobertura.ReportGenerator
       -DUnitTest.UserParam.CoverageReportFile=/source/coverage.xml

これを独自のパッケージに適応させるには、独自のパッケージ名と優先するコンテナイメージをドロップしてください(zpm を含める必要があります。https://hub.docker.com/r/intersystemsdc/iris-community をご覧ください)。 また、ユニットテストパッケージが独自のパッケージの規則に一致するように変更することもお勧めします(ユニットテストを実行する前に、読み込みとコンパイルを行ってして依存関係の読み込み/コンパイルを処理する必要がある場合。私自身、このパッケージではユニットテストに特有の奇妙な問題に遭遇したためですが、他のケースでは関連性がないかもしれません)。

インスタンス名と test_reports ディレクトリは、他の使用では変更する必要はありませんし、test_flags には有効なデフォルトセットが備わっています。これらは、ユニットテストが失敗すると、ビルドを失敗としてフラグする機能をサポートしており、jUnit 形式のテスト結果とコードカバレッジレポートのエクスポートも処理できます。

ビルドのステップ

GitHub リポジトリの確認

この例では、テストされているリポジトリと、Forgery のフォーク(ユニットテストで必要であるため)の2 つのリポジトリを確認する必要があります。

    # Checks out this repository under $GITHUB_WORKSPACE, so your job can access it
    - uses: actions/checkout@v2
    
    # Also need to check out timleavitt/forgery until the official version installable via ZPM
    - uses: actions/checkout@v2
      with:
        repository: timleavitt/forgery
        path: forgery

$GITHUB_WORKSPACE は非常に重要な環境変数で、このすべてが実行するルートディレクトリを表します。 権限の観点では、そのディレクトリ内ではほぼあらゆる操作を実行できますが、他の場所では問題に遭遇するかもしれません。

InterSystems IRIS コンテナの実行

最終的にテスト結果レポートを配置するディレクトリをセットアップしたら、ビルドの InterSystems IRIS Community Edition(+ZPM)コンテナを実行します。

    - name: Run Container
      run: |
        # Create test_reports directory to share test results before running container
        mkdir $test_reports
        chmod 777 $test_reports
        # Run InterSystems IRIS instance
        docker pull $container_image
        docker run -d -h $instance --name $instance -v $GITHUB_WORKSPACE:/source -v $GITHUB_WORKSPACE/$test_reports:/$test_reports --init $container_image
        echo halt > wait
        # Wait for instance to be ready
        until docker exec --interactive $instance iris session $instance &lt; wait; do sleep 1; done

コンテナに共有されている GitHub ワークスペース(コードを読み込めるようにするため。ここにはテストカバレッジ情報もレポートします)と jUnit テスト結果を配置する別のディレクトリの 2 つのボリュームがあります。

「docker run」が終了しても、インスタンスが完全に開始され、コマンドを処理する準備が整っているわけではありません。 インスタンスの準備が整うまで、iris セッションを通じて「halt」コマンドを実行し続けます。これは失敗となりますが、(最終的に)成功になるまで、1 秒に 1 回ずつ試行し続けます。成功になれば、インスタンスの準備は完了です。

テスト関連ライブラリのインストール

このユースケースでは、他に TestCoverageForgery という 2 つのライブラリを使用してテストします。 TestCoverage は、Community Package Manager を介して直接インストールできますが、Forgery については、(現時点では)zpm「load」を介して読み込む必要があります。いずれのアプローチも有効です。

    - name: Install TestCoverage
      run: |
        echo "zpm \"install testcoverage\":1:1" > install-testcoverage
        docker exec --interactive $instance iris session $instance -B &lt; install-testcoverage
        # Workaround for permissions issues in TestCoverage (creating directory for source export)
        chmod 777 $GITHUB_WORKSPACE
    
    - name: Install Forgery
      run: |
        echo "zpm \"load /source/forgery\":1:1" > load-forgery
        docker exec --interactive $instance iris session $instance -B &lt; load-forgery

一般的には、ファイルにコマンドを記述して空、IRIS セッションで実行します。 ZPM コマンドに追加されている「:1:1」は、エラーが発生したらエラーコードとともにプロセスを終了し、エラーが発生しない場合は最後に停止することを示しています。つまり、エラーが発生した場合は、失敗したビルドステップとして報告されるため、ファイルの最後に「halt」コマンドを追加する必要はありません。

パッケージの構築とテスト

最後に、パッケージのテストを実際に構築して実行しましょう。 これはいたって単純です。始めの方で定義した $build_flags/$test_flags 環境変数が使用されていることに注目してください。

    # Runs a set of commands using the runners shell
    - name: Build and Test
      run: |
        # Run build
        echo "zpm \"load /source $build_flags\":1:1" > build
        # Test package is compiled first as a workaround for some dependency issues.
        echo "do \$System.OBJ.CompilePackage(\"$test_package\",\"ckd\") " > test
        # Run tests
        echo "zpm \"$package test -only $test_flags\":1:1" >> test
        docker exec --interactive $instance iris session $instance -B &lt; build && docker exec --interactive $instance iris session $instance -B &lt; test && bash &lt;(curl -s https://codecov.io/bash)

これは、見たことのあるパターンに則っています。ファイルにコマンドを書き出してから、そのファイルを iris セッションの入力として使用しています。

最後の行の最後の部分は、コードカバレッジの結果を codecov.io にアップロードしています。 とても簡単です!

ユニットテスト結果のアップロード

ユニットテストが失敗したとしましょう。 ビルドログに戻って、どこで間違ってしまったのかを見つけ出すのは本当に面倒な作業ですが、有用なコンテキストが得られるかもしれません。 作業を楽にするために、jUnit 形式の結果をアップロードできるだけでなく、サードパーティのプログラムを実行して、見栄えの良い HTML レポートに変換することも可能です。

    # Generate and Upload HTML xUnit report
    - name: XUnit Viewer
      id: xunit-viewer
      uses: AutoModality/action-xunit-viewer@v1
      if: always()
      with:
        # With -DUnitTest.FailuresAreFatal=1 a failed unit test will fail the build before this point.
        # This action would otherwise misinterpret our xUnit style output and fail the build even if
        # all tests passed.
        fail: false
    - name: Attach the report
      uses: actions/upload-artifact@v1
      if: always()
      with:
        name: ${{ steps.xunit-viewer.outputs.report-name }}
        path: ${{ steps.xunit-viewer.outputs.report-dir }}

このほとんどは、https://github.com/AutoModality/action-xunit-viewer の Readme から拝借したものです。

最終結果

このワークフローの結果は、以下でご覧いただけます。

intersystems/apps-rest での CI ジョブのログ(ビルドアーティファクトを含む): https://github.com/intersystems/apps-rest/actions?query=workflow%3ACI
テストカバレッジレポート: https://codecov.io/gh/intersystems/apps-rest

ご質問がございましたら、お知らせください!

0
0 198
記事 Toshihiko Minamoto · 7月 15, 2021 16m read

InterSystems および Intel は先日、InterSystems IRIS を「Cascade Late」としても知られる第 2 世代 Intel® Xeon® スケーラブルプロセッサおよび Intel® Optane™ DC パーシステントメモリ(DCPMM)と組み合わせて一連のベンチマークを実施しました。 さまざまなワークロード設定とサーバー構成で、Intel の最新のサーバーテクノロジーを使用した InterSystems IRIS のパフォーマンスとスケーラビリティ機能を実証するのがこのベンチマークの目的です。 このレポートには、さまざまなベンチマークの結果とともに、Intel DCPMM と InterSystems IRIS のユースケースが 3 つ示されています。

概要

パフォーマンスとスケーリングを実証するために 2 種類のワークロードが使用されています。読み取り集中型のワークロードと書き込み集中型のワークロードです。 このように分けて実証するのは、読み取り集中型ワークロードにおけるデータベースキャッシュ効率の増加と、書き込み集中型ワークロードにおけるトランザクションジャーナルの書き込みスループットの増加のそれぞれに特化したユースケースで、Intel DCPMM の影響を示すためです。 両方のユースケースシナリオにおいて、InterSystems IRIS のスループット、スケーラビリティ、およびパフォーマンスの大幅なゲインが達成されています。

  • 読み取り集中型ワークロードでは、4 ソケットサーバーと、合計約 1.2 TB のデータを持つデータセットを使用する大量の長期実行分析クエリが使用されました。 DCPMM を「Memory Mode」で使用した場合のベンチマーク比較では、メモリの少ない前世代の Intel E7v4 シリーズプロセッサと比べた場合、経過実行時間が大幅に短縮され、およそ 6 倍高速になりました。 E7v4 と、DCPMM を使った最新のサーバーを同じメモリサイズで比較した場合は、20% の改善が見られました。 これは、DCPMM による InterSystems IRIS データベースキャッシュ機能の向上と最新の Intel プロセッサアーキテクチャによるものです。

  • 書き込み集中型ワークロードでは、2 ソケットサーバーと InterSystems HL7 メッセージングのベンチマークが使用されました。多数のインバウンドインターフェースで構成されており、各メッセージには複数の変換が伴い、インバウンドメッセージごとに 4 つのアウトバウンドメッセージが使用されています。 高スループットを維持する上で重要なコンポーネントの 1 つは、IRIS for Health のメッセージ耐久性保証で、その操作においては、トランザクションのジャーナル書き込みのパフォーマンスが重要となります。 「APP DIRECT」モードで DCPMM を使用して、DAX XFS でトランザクションのジャーナル用の XFS ファイルシステムを提供した場合、このベンチマークのメッセージスループットには 60% の向上が示されました。

テスト結果と構成を要約すると、DCPMM は適切に設定された InterSystems IRIS とワークロードで使用された場合にスループットを大幅に向上させることができます。 高レベルのメリットとしては、読み取り集中型ワークロードではデータベースのキャッシュ効率の向上とディスク I/O ブロック読み取りの抑制、書き込み集中型ワークロードではジャーナルの書き込みスループットの向上を得られます。

さらに、古いハードウェアを更新し、パフォーマンスとスケーリングの改善を検討しているユーザーにとって、DCPMM を備えた Cascade Lake に基づくサーバーは優れた更新パスとなります。 InterSystems のテクノロジーアーキテクトと相談しながら、既存のワークロードに推奨される構成についてのアドバイスを得ることができます。


読み取り集中型ワークロードベンチマーク

読み取り集中型ワークロードでは、512 GiB と 2 TiB のデータベースキャッシュサイズの E7v4(Broadwell)と Intel® Optane™ DC パーシステントメモリ(DCPMM)を使用した 1 TB と 2 TB のデータベースキャッシュサイズの最新の第 2 世代 Intel® Xeon® スケーラブルプロセッサ(Cascade Lake)と比較する分析クエリベンチマークを使用しました。

より大規模なキャッシュの影響とパフォーマンスの向上を示すために、さまざまなグローバルバッファサイズで複数のワークロードを実行しました。 構成を反復するごとに、COLD と WARM で実行しています。 COLD は、データベースキャッシュにデータが事前に入力されていない場合で、 WARM は、データベースキャッシュがすでにアクティブになっており、ディスクからの物理的な読み取りを減らすために、データが入力済みである(または少なくと可能な限り入力されている)ことを示します。

ハードウェア構成

古い 4 ソケット E7v4(Broadwell)ホストを DCPMM を使った 4 ソケット Cascade Lake サーバーと比較しました。 この比較が選択されたのは、InterSystems IRIS を使ってハードウェアの更新を検討している既存のお客様がパフォーマンスの向上を得られることを実証するためです。 バージョン間のソフトウェアの最適化が要因とならないように、すべてのテストには同じバージョンの InterSystems IRIS が使用されました。

ディスクパフォーマンスが比較の要因とならないように、すべてのサーバーには同一のストレージアレイにある同一のストレージが使用されています。 ワーキングセットは 1.2 TB のデータベースです。 図 1 にはこのハードウェア構成と、それぞれの 4 ソケット構成の比較が示されています。

図 1: ハードウェア構成

サーバー #1 の構成サーバー #2 の構成
プロセッサ: 4 x E7-8890 v4 @ 2.5Ghzプロセッサ: 4 x Platinum 8280L @ 2.6Ghz
メモリ: 2TiB DRAMメモリ: 3TiB DCPMM + 768GiB DRAM
ストレージ: 16Gbps FC all-flash SAN @ 2TiBストレージ: 16Gbps FC all-flash SAN @ TiB
 DCPMM: Memory Mode のみ

ベンチマークの結果と結論

512 GiB を 1 TiB か 2 TiB のいずれかの DCPMM バッファプールサイズと比較した場合、経過実行時間に大幅な短縮が見られます(約 6 分の 1)。 さらに、2 TiB E7v4 DRAM と 2 TiB Cascade Lake DCPMM の構成を比較した場合には約 20% の改善も見られました。 バッファプールのサイズが同じであるとした場合、この 20% の増加は、ほぼ新しいプロセッサのアーキテクチャとプロセッサのコア数の増加によるものだと考えられますが、 それでも、テストされた 4 ソケット Cascade Lake にインストールされていたが 24 x 128 GiB DCPMM のみであることを考慮すると深い意義があります。DCPMM は 12 TiB までスケーリングすることが可能であり、これは E7v4 が同じ 4 ソケットサーバーのフットプリントでサポートできるメモリの約 4 倍のメモリです。

以下の図 2 に示されるグラフは、この比較の結果を示しています。 両方のグラフの y 軸は経過時間(値が小さくなるほど良)で、さまざまな構成で得た結果が比較されています。

図 2: 各種構成の経過時間の比較


書き込み集中型ワークロードベンチマーク

このベンチマークのワークロードは、すべての T4 タイプのワークロードを使用した HL7v2 メッセージングワークロードです。

  • T4 ワークロードは、ルーティングエンジンを使って、個別に変更されたメッセージを 4 つの各アウトバウントインターフェースにルーティングしました。 平均して、インバウンドメッセージの 4 つのセグメントが変換ごとに変更されました(4 つの変換で 1 件につき 4 件)。 各インバウンドメッセージでは、4 つのデータ変換の実行により 4 つのメッセージがアウトバウンドに送信され、5 つの HL7 メッセージオブジェクトがデータベースに作成されました。

各システムは 128 個のインバウンドビジネスサービスと各インバウンドインターフェースに送信される 4800 件のメッセージ(インバウンドメッセージ合計 614,400 件、アウトバウンドメッセージ合計 2,457,600 件)で構成されています。

このベンチマークワークロードのスループットの単位は「1秒あたりのメッセージ数」です。 トランザクションジャーナルのスループットとレイテンシは高スループットを維持する上で重要なコンポーネントであるため、ベンチマーク実行中のジャーナルの書き込みにも関心があります(記録されています)。 IRIS for Health のメッセージ耐久性保証のパフォーマンスに直接影響を与えるため、その操作において、トランザクションジャーナルの書き込みパフォーマンスが重要となります。 ジャーナルのスループットが低下すると、アプリケーションプロセスによってジャーナルバッファの可用性が阻止されてしまいます。

ハードウェア構成

書き込み集中型ワークロードでは、2 ソケットサーバーを使用することにしました。 192 GB の DRAM と 1.5 TiB の DCPMM しかないため、この構成は前述の 4 ソケット構成よりも小さくなります。 DCPMM を使用した Cascade Lake のワークロードを以前の初代 Intel® Xeon® スケーラブルプロセッサ(Skylake)サーバーに比較しました。 両サーバーには 750GiB Intel® Optane™ SSD DC P4800X ドライブがローカル接続されています。

図 3 にはこのハードウェア構成と、それぞれの 2 ソケット構成の比較が示されています。

図 3: 書き込み集中型ワークロードのハードウェア構成

サーバー #1 の構成サーバー #2 の構成
プロセッサ: 2 x Gold 6152 @ 2.1Ghzプロセッサ: 2 x Gold 6252 @ 2.1Ghz
メモリ: 192GiB DRAMメモリ: 1.5TiB DCPMM + 192GiB DRAM
ストレージ: 2 x 750GiB P4800X Optane SSDストレージ: 2 x 750GiB P4800X Optane SSD
 DCPMM: Memory Mode & App Direct モード

ベンチマークの結果と結論

テスト 1: このテストでは、図 3 のサーバー #1 構成に示される Skylake サーバーにおいて前述の**T4 ワークロード**を実行しました。 Skylake サーバーは、2010 ジャーナル書き込み/秒のジャーナルファイル書き込み速度で約 3355 件のインバウンドメッセージの持続的なスループットを示しました。

テスト 2: このテストでは、図 3 のサーバー #2 構成に示される Cascade Lake サーバーにおいて、DCPMM の Memory Mode を指定して同じワークロードを実行しました。 これは、2400 ジャーナル書き込み/秒のジャーナルファイル書き込み速度で約 4684 件のインバウンドメッセージの持続的なスループットという大幅な向上を示しました。 これは、テスト 1 に比較すると 39% の増加です。

テスト 3: このテストでは、図 3 のサーバー #2 構成に示される Cascade Lake サーバーにおいて同じワークロードを実行しましたが、今度は DCPMM を App Direct Mode に指定し、DCPMM による何らかの実行を構成せずに実行しました。 このテストの目的は、DRAM のみを使用した Cascade Lake のパフォーマンスとスループットを DCPMM と DRAM を使用した Cascade Lake に比較して測定することです。 DCPMM が使用されていない場合でもスループットは(比較的小さいとは言え)向上したという、特に驚くことでもない結果が出ました。 これは、2540 ジャーナル書き込み/秒のジャーナルファイル書き込み速度で約 4845 件のインバウンドメッセージの持続的なスループットという向上を示しました。 DCPMM は DRAM に比べてより高いレイテンシがあり、更新が大量に流入すればパフォーマンスが低下するため、予想された動作と言えます。 別の見方をすると、まったく同じサーバーで DCPMM を Memory Mode で使用する場合、書き込みの取り込みワークロードに 5% 未満の低下があることになります。 また、Skylake を Cascade Lake(DRAM のみ)に比較した場合、テスト 1 の Skylake サーバーに比べて 44% の増加が得られています。

テスト 4: このテストでは、図 3 のサーバー #2 構成に示される Cascade Lake サーバーにおいて同じワークロードを実行しましたが、今度は DCPMM を App Direct Mode に指定し、App Direct Mode をジャーナルファイルシステム用にマウントされた DAX XFS として使用して実行しました。 これは 2630/秒のジャーナルファイル書き込み速度で 1 秒あたり 5399 件のインバウンドメッセージというさらに高いスループットを示しました。 この種のワークロードでは App Direct Mode の DCPMM がより適した DCPMM の使用方法であることが示されています。 これらの結果を最初の Skylake 構成と比較すると、テスト 1 の Skylake サーバーに比べ、スループットが 60% 増加しています。


InterSystems IRIS の推奨される Intel DCPMM ユースケース

Intel® Optane™ DC パーシステントメモリを使用することで InterSystems IRIS にメリットが与えられるユースケースと構成にはいくつかあります。

Memory Mode

このモードは、単一の InterSystems IRIS デプロイや大規模な InterSystems IRIS シャードクラスタでの膨大なデータベースキャッシュに最適です。後者の環境ではより多く(またはすべて)のデータベースをメモリにキャッシュできます。 DCPMM と DRAM の比率は最大 8:1 にすることをお勧めします。「ホットメモリ」を L4 キャッシュレイヤーとして機能する DRAM に保持する際に重要です。 これは、リソース占有やその他のメモリキャッシュラインなど、共有内部 IRIS メモリ構造において特に重要となります。

App Direct Mode(DAX XFS)– ジャーナルディスクデバイス

このモードは、DCPMM をトランザクションジャーナルファイルのディスクデバイスとして使用する場合に最適です。 DCPMM は Linux にマウントされた XFS ファイルシステムとしてオペレーティングシステムに表示されます。 DAX XFS を使用するメリットは、これによって PCIe バスのオーバーヘッドとファイルシステムからのダイレクトメモリアクセスが緩和されることにあります。 HL7v2 ベンチマークの結果に示されるように、書き込みレイテンシによって HL7 メッセージングのスループットは大幅に向上します。 また、ストレージには従来のディスクデバイスと同様に、再起動や電源サイクル時における永続性と耐久性が備わっています。

App Direct Mode(DAX XFS)– ジャーナル + 書き込みイメージジャーナル(WIJ)ディスクデバイス

このユースケースでは、App Direct モードの用法がトランザクションジャーナルと書き込みイメージジャーナル(WIJ)の両方に拡張されます。 両ファイルは書き込み集中型であるため、超低レイテンシと永続性のメリットを確実に得られます。

Dual Mode: Memory + App Direct Modes

DCPMM を Dual Mode で使用すると DCPMM のメリットが拡大し、トランザクションジャーナルや書き込みイメージジャーナルデバイスで大規模なデータベースキャッシュと超低レイテンシを実現できるようになります。 このユースケースでは、DCPMM は OS にマウントされた XFS ファイルシステムとオペレーティングシステムの RAM として表示されます。 これは、DCPMM の一定の割合を DAX XFS に割り当て、残りを Memory Mode に割り当てることで可能です。 前述のように、インストールされている DRAM はプロセッサの L4 のようなキャッシュとして機能します。

「疑似」Dual Mode

疑似 Dual Mode 寄りにユースケースモデルを拡張するために、OLTPタイプのワークロード用と分析または大規模なクエリーニーズ用に高速のインバウンドトランザクションと更新が伴うトランザクションと分析の並行ワークロード(HTAP ワークロードとしても知られています)タイプのワークロードがあり、さらに InterSystems IRIS 共有クラスタ内ではそれぞれの InterSystems IRIS ノードタイプが DCPMM のさまざまなモードで稼働しています。

この例では、グローバルバッファの大規模データベースキャッシュと、トランザクションワークロード用に DAX XFS として Dual Mode または App Direct のいずれかで実行するデータノードのメリットを得られるように、DCPMM Memory Mode で実行する大規模なクエリ/分析ワークロードを処理する InterSystems IRIS 計算ノードが追加されています。


まとめ

インフラストラクチャの選択に関して言えば、InterSystems IRIS には多数のオプションが提供されています。 インフラストラクチャの要件はアプリケーション、ワークロードプロファイル、およびビジネスニーズによって決まり、これらのテクノロジーとインフラストラクチャの選択が、ビジネスにおけるアプリケーションの成功、採用、および重要性を左右します。 第 2 世代 Intel® Xeon® スケーラブルプロセッサと Intel® Optane™ DC パーシステントメモリを使用した InterSystems IRIS は、ビジネスに大きな影響を与える InterSystems IRIS ベースアプリケーションに画期的なスケーリング性能とスループット性能を与えることができます。

InterSystems IRIS と Intel DCPMM 対応サーバーには、次のようなメリットがあります。

  • Memory Mode の DCPMM を使用した InterSystems IRIS または InterSystems IRIS for Health データベースキャッシュにマルチテラバイトのデータベースが完全に収まるようにメモリ容量を増加します。 ストレージ(ディスク)からの読み取りと比較した場合、サイズが増加するにつれてシステムメモリを利用する InterSystems IRIS の実証されたメモリキャッシュ機能によって、コードを変更することなくクエリへの応答パフォーマンスを 6 倍向上させることができます。
  • 同一のプロセッサを使って、利用可能な最速の NVMe ドライブから、App Direct モードによって DAX XFS ファイルシステムとして DCPMM を利用するようにトランザクションジャーナルディスクを変更するだけで、HL7 変換など、InterSystems IRIS と InterSystems IRIS for Health に基づく高速データ相互運用性スループットアプリケーションのパフォーマンスを最大 60% 増のスループットに改善します。 メモリ速度のデータ転送とデータの永続性を活用することで、InterSystems IRIS と InterSystems IRIS for Health に大きなメリットが与えられます。
  • 読み取り集中型ワークロードか書き込み集中型ワークロードか、またはその両方のワークロードかに関係なく、Mixed Mode の DCPMM を使った 1 つのリソースコンポーネントの為だけにサーバー全体を過剰に割り当てることなく、必要に応じて計算リソースを拡大します。

お客様の InterSystems IRIS ベースのアプリケーションに最適なハードウェア構成についてのご相談は、InterSystems テクノロジーアーキテクトにお問い合わせください。

0
0 211
記事 Toshihiko Minamoto · 4月 28, 2021 5m read

数年ほど前、Caché Foundationsの講座(現「Developing Using InterSystems Objects and SQL」)において、%UnitTestフレームワークの基礎を講義していたことがあります。 その時、ある受講者から、ユニットテストを実行している間に、パフォーマンス統計を収集できるかどうかを尋ねられました。 それから数週間後、この質問に答えるために、%UnitTestの例にコードを追加したのですが、 ようやく、このコミュニティでも共有することにしました。

Processクラスの%SYSTEMには、プロセスについて収集可能なメトリクスが(所要時間以外に)いくつか提供されています。

  • 所要時間
  • 実行された行
  • グローバル参照
  • システムCPU時間
  • ユーザーCPU時間
  • ディスク読み取り時間
上記の統計を収集する任意のユニットテストを有効にするには、%UnitTest.TestCaseのサブクラスを作成してプロパティを追加します。
 
Class Performance.TestCase Extends %UnitTest.TestCase
{
Property Duration As %Time;
Property Lines As %Integer;
Property Globals As %Integer;
Property SystemCPUTime As %Integer;
Property UserCPUTime As %Integer;
Property DiskReadTime As %Integer;
}
 
作成する特定のユニットテストは、%UnitTest.TestCaseではなく、新しいサブクラスから継承されている必要があります。
 
サブクラスではOnBeforeOneTest()を使って、各ユニットテストの統計データを初期化します。 DiskReadTime以外、現在値でプロパティを初期化します。
 
/// パフォーマンス統計を初期化します
<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">Method OnBeforeOneTest(testname As %String) As %Status</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">{</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    // 現在の値で初期化します</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..Duration = $zh</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..Lines = $system.Process.LinesExecuted()</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..Globals = $system.Process.GlobalReferences()</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><span style="">    set ..SystemCPUTime = $piece(CPUTime, ",", 1)</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..UserCPUTime = $piece(CPUTime, ",", 2)</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    // ディスク時間を0にリセットし、カウントを開始します</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    do $system.Process.ResetDiskReadTiming()</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    do $system.Process.EnableDiskReadTiming()</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    return $$$OK</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">}</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>
 
<div>
  <span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none">OnAfterOneTest() を使って、各ユニットテストの統計データを確定します。 DiskReadTime以外、現在の値から初期値を減算します。</span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
   
</div>

<div>
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">/// パフォーマンス統計を確定します </font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">/// ここに、分析用にカウンターを別のテーブルに保存するためのコードを追加できます。</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">Method OnAfterOneTest(testname As %String) As %Status</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">{</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><span style="">    set ..Duration = $zh - ..Duration</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..Lines = $system.Process.LinesExecuted() - ..Lines</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..Globals = $system.Process.GlobalReferences() - ..Globals</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <div>
      <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set CPUTime = $system.Process.GetCPUTime()</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
    </div>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..SystemCPUTime = $piece(CPUTime, ",", 1) - ..SystemCPUTime</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..UserCPUTime = $piece(CPUTime, ",", 2) - ..UserCPUTime</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    // ディスク読み取り時間を取得し、カウントを停止します</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set ..DiskReadTime = $system.Process.DiskReadMilliseconds()</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    do $system.Process.DisableDiskReadTiming()</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    // ユニットテストログにメッセージを追加します</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set msg = "Performance: " _</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span> 
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">              "Duration: " _           ..Duration _</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">              ", Lines: " _            ..Lines _</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">              ", Globals: " _          ..Globals _</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">              ", System CPU Time: " _ (..SystemCPUTime / 1000) _</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">              ", User CPU Time: " _   (..UserCPUTime / 1000) _</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">              ", Disk Read Time: " _  (..DiskReadTime / 1000)</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    do $$$LogMessage(msg)</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    return $$$OK</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
  
  <div>
    <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">}</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
  </div>
</div>
 
ちょっとした技がもう1つあります。 統計を収集するかしないかを指定して、ユニットテストを実行したほうが良いかもしれません。 したがって、ユニットテストを呼び出すコードに引数(%Boolean 1または0)を追加し、何らかの方法で渡す必要があります。 テストを実際に実行するメソッド(RunTest() またはほかのRun*() メソッドの1つ)は、第3引数に配列を取り、参照形式で渡します。 次は、そのサンプルコードです。
 
    // logging引数(1または0)を保持する配列を作成し、それを参照で渡します
<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    set p("logging") = logging</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>

<div>
  <span style="font-size:12px"><span style="caret-color:#000000"><span style="color:#000000"><span style=""><span style="font-style:normal"><span style="font-variant-caps:normal"><span style="font-weight:normal"><span style="letter-spacing:normal"><span style="orphans:auto"><span style="text-transform:none"><span style=""><span style="widows:auto"><span style="word-spacing:0px"><span style="-webkit-text-size-adjust:auto"><span style="text-decoration:none"><font><font face="Consolas">    do ##class(%UnitTest.Manager).RunTest(test, qualifiers, .p)</font></font></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
</div>
 
配列に渡す値は、OnBeforeOneTest() と OnAfterOneTest() でアクセスできます。 これを両方のメソッドの最初の行に追加します。
 
   if (..Manager.UserFields.GetAt("logging") = 0) { return $$$OK }
 
以上です! 皆さんのご質問、コメント、その他のアイデアをぜひお聞かせください。
0
0 174
記事 Mihoko Iijima · 12月 15, 2020 1m read

これはInterSystems FAQ サイトの記事です。

デフォルトでは、セキュリティ脆弱性対応の観点でウェブサービス用テストページの実行を許可していません。

テスト目的等で利用する場合は、テストページへのアクセスを有効にする必要があり、以下グローバル変数のセットを %SYS ネームスペースで実行する必要があります。

set ^SYS("Security","CSP","AllowClass",0,"%SOAP.WebServiceInvoke")=1
set ^SYS("Security","CSP","AllowClass",0,"%SOAP.WebServiceInfo")=1

 

詳細については、以下のドキュメントもご参照下さい。

カタログおよびテスト・ページについて【IRIS】

カタログおよびテスト・ページについて 

0
0 163
記事 Mihoko Iijima · 12月 3, 2020 3m read

これまでさまざまなストレージ技術とそのパフォーマンス特性の例を紹介してきましたが、今回は新しい HPE Cloudline 3150 Gen10(AMD プロセッサベースのシングルソケットサーバーで 3.2TB の Samsung PM1725a NVMe ドライブを 2 台搭載)など、内部コモディティベースのサーバーストレージの活用が増加傾向にあることを確認しました。  

ハードウェア構成と LinuxLVM のセットアップ

テストは次の構成で行われました。

  • 2 x Samsung 3.2TB PM1725a NVMe ドライブ(サーバー内蔵)
  • 1 x HPE Cloudline 3150 Gen10 サーバー
  • Red Hat Enterprise Linux 7.5 64 ビット版

このテストでは、その他のストレージデバイスや HBA は使用していません。両方の NVMe ディスク装置の IO 能力を最大化するため、16MB の PE サイズを使用して単一の LVM PE ストライプを作成しました。 LVM PE ストライピングのセットアップの詳細は、コミュニティ内の別の記事にあります。

InterSystems IRIS のインストールとセットアップ

InterSystems IRIS は、単一のボリュームグループと単一の論理ボリュームにインストールされます。 今回は複数のボリュームグループと論理ボリュームを使わず、非常に単純なセットアップを行いたいと思いました。 この例では、単一の論理ボリュームとファイルシステムを作成しました(この例では、/data がファイルシステムのマウントポイントになっています)。データベースインスタンスからのランダム読み取りのワークロードを使用し、徐々にジョブ数が増加する IO ワークロードを生成しています。 1 TBのデータベースが IO ワークロードのターゲットです。 このテストで使用された RANREAD ツールの詳細は、こちらを参照してください。

テスト結果

わずか 10 個のジョブから開始した最初の時点では、ストレージのスループットは 100 マイクロ秒(µs)と非常に低い遅延で 1 秒間に 10 万個の 8 KB データベースブロックを読み取ることができました。 ジョブの数が増えても、サーバーが実際に CPU の能力を使い果たしてストレージをさらに激しく駆動させるまでは遅延はずっと低いままでした。 観察された中で最も大きな遅延は 300µs に過ぎず、1秒間に 850 K 以上の 8KB データベースブロックの読み取りを維持していました。  

図 1 は 2 台の Samsung NVMe ディスク装置のみを使用してテストした構成で、パフォーマンスが予測可能でスループットが維持されたことを示しています。 最大値でも遅延は非常に低く、実際には NVMe ディスク装置のスループットが最大化する前にテストサーバーが実際に CPU を使い果たしたことが分かります。

図 1: 予測可能で非常に低い遅延と非常に高いスループットを維持

まとめ

Samsung PM1725a NVMe ディスク装置は非常に低い遅延と高スループット能力を示し、非常に高性能なトランザクションシステムをサポートしています。 そのため、ストレージの待機時間とスループットが必要なインジェッション・ワークロードに最適です。 これらの Samsung NVMe ディスク装置はこのような素晴らしいパフォーマンス特性を備えながら非常に魅力的な価格で提供されており、InterSystems のデータベースミラーリングと組み合わせると同じレベルのアプリケーションの可用性を実現できます。

0
0 301
記事 Toshihiko Minamoto · 10月 5, 2020 15m read

CachéとCosFakerを使ったテスト駆動開発の簡単な紹介

読了****目安時間: 6分
 

皆さん、こんにちは。

私がTDDに初めて出会ったのは約9年前のことです。すぐに夢中になってしまいました。
最近は非常に人気が出てきているようですが、残念ながら多くの企業ではあまり使われていないようです。 また、主に初心者の方ではありますが、一体それがなんであるのか、どのように使うのかといったことさえも知らない開発者もたくさんいます。

概要

この記事は、%UnitTestでTDDを使用する方法を紹介することを目標としています。 ワークフローを示し、私の最初のプロジェクトであったcosFakerの使用方法を説明します。これはCachéを使って作成したものであり、最近になってOpenExchangeにアップロードしたものです。

では、ベルトを締めて出発しましょう。

TDDとは?

テスト駆動開発(TDD)は、自動テストが失敗した場合に、開発者に新しいコードの書き方のみを示すプログラミング実践として定義できます。
このメリットに関する記事、講義、講演などは数多く存在しますが、どれもが正しい内容です。
コードはテスト済みで生成されること、過度なエンジニアリングを避けるために定義された要件に、システムが実際に適合していることを確認できること、継続的にフィードバックを得ることが挙げられます。

では、TDDを使用しない理由は何でしょうか。 TDDにはどのような問題があるのでしょうか。 答えは単純です。そう、コストです! とにかくコストがかかります!
TDDではより多くの行のコードを記述する必要があるため、その処理には時間がかかります。 しかし、TDDを使用すると、製品を作成するための最終コストは現時点で発生し、後で追加コストをかける必要がありません。
常にテストを実行すれば、早期にエラーを検出できるため、修正にかかるコストが削減されるのです。
というわけで、私からのアドバイスは、ただ実行に移しましょう!

セットアップ

InterSystemsは、%UnitTestの使用方法に関するドキュメントとチュートリアルを用意しています。こちらからお読みください。

開発にはvscodeを使用します。 この方法で、テスト用に別のフォルダを作成し、 UnitTestRootにプロジェクトコードパスを追加して、テストを実行する際に、テストサブフォルダの名前を渡します。 そして必ず、修飾子loadudlを渡します。

Set ^UnitTestRoot = "~/code"

  Do ##class(%UnitTest.Manager).RunTest("myPack","/loadudl")

 

手順

おそらく、「レッド ➡グリーン➡リファクタリング」という有名なTDDサイクルを耳にしたことがあるでしょう。 失敗するテストを書き、合格する単純なプロダクションコードを書き、そしてそのプロダクションコードをリファクタリングするというサイクルです。
では、実際に手を動かして、計算を行うクラスとそれをテストする別のクラスを作成することにしましょう。 後者のクラスは、%UnitTest.TestCaseを拡張します。
では、整数の2乗を返すClassMethodを作成しましょう。



Class Production.Math

{


ClassMethod Square(pValue As %Integer) As %Integer

{

}


}

 

そして、2を渡すとどうなるかテストします。 4を返すはずです。

Class TDD.Math Extends %UnitTest.TestCase

{


Method TestSquare()

{

    Do $$$AssertEquals(##class(Production.Math).Square(2), 4)

}


}

 

次のコードを実行します。

Do ##class(%UnitTest.Manager).RunTest("TDD","/loadudl")

テストは失敗します。

レッド! 次のステップは、これをグリーンにすることです。
グリーンにするために、Squareメソッドの実行結果として4を返すようにしましょう。

Class Production.Math

{


ClassMethod Square(pValue As %Integer) As %Integer

{

  Quit 4

}


}

そしてテストを再実行します。

1つのシナリオでしか機能しないのですから、このソリューションでは、おそらくあまり嬉しくないのではないでしょうか。 わかりました! では次のステップに進みましょう。 別のシナリオを作成することにします。今度は負の数を渡してみます。

Class TDD.Math Extends %UnitTest.TestCase

{


Method TestSquare()

{

    Do $$$AssertEquals(##class(Production.Math).Square(2), 4)

}


Method TestSquareNegativeNumber()

{

    Do $$$AssertEquals(##class(Production.Math).Square(-3), 9)

}


}

テストを実行します。

また失敗してしまうので、プロダクションコードをリファクタリングしましょう。

Class Production.Math

{


ClassMethod Square(pValue As %Integer) As %Integer

{

  Quit pValue * pValue

}


}

そして、テストを再実行します。

これですべてがうまく機能するようになりました... これが凝縮版のTDDサイクルです。

なぜこの手順に従う必要があるのか、と思っていることでしょう。 なぜテストを失敗させる必要があるのか、と。
私はプロダクションコードを記述したチームで作業したことがありますが、テストを作成したのはその後でした。 それでも、この小さな一歩に従う方を好むのには、次の理由があります。
ボブおじさん(Robert C. Martin)は、コードを書いた後でテストを書くことは「TDD」ではなく「時間の無駄」だと呼んだのです。
もう少し述べれば、テストが失敗し、次に合格することから、テストをテストしていることになります。
このテストはコードに変わりなく、誤りが含まれることもあります。 そして、それをテストする方法こそ、失敗して合格する必要のある場合に失敗と合格を保証することになります。 つまり、「テストをテストした」ということになります。

cosFaker

適切なテストを作成するには、最初にテストデータを生成する必要があります。 これを行う方法の1つが、データのダンプを生成して、テストに使うという方法です。
別の方法には、cosFakerを使用して、必要なときに偽のデータを簡単に作り出す手があります。https://openexchange.intersystems.com/package/CosFaker

xml ファイルをダウンロードするだけです。その後、管理ポータル -> システムエクスプローラ -> Classes -> Importに移動します。 インポートする xml ファイルを選択するか、そのファイルを Studio にドラッグします。
また、ターミナルを使用してインポートすることもできます。

Do $system.OBJ.Load("yourpath/cosFaker.vX.X.X.xml","ck")

 

ローカリゼーション

cosFakerは、デフォルトのCSPアプリケーションフォルダにロケールファイルを追加します。 現時点では、英語とブラジルポルトガル語(私の母国語)の2言語しかありません。
データの言語は、Cachéの構成に応じて選択されます。
cosFakerのローカリゼーションは進行中のプロセスです。ご協力いただける方は、ぜひ、自身のロケール向けにローカライズされるプロバイダを作成してプルリクエストを提出してください。
cosFakerを使用すれば、ランダムな語、段落、電話番号、名前、住所、メール、価格、製品名、日付、16進色コードなどを生成することができます。

すべてのメソッドは、クラスでサブジェクトごとにグループ化されます。つまり、Latitudeを生成するには、AddressクラスのLatitudeメソッドを呼び出します。

 Write ##class(cosFaker.Address).Latitude()

-37.6806

また、テスト用のJsonを生成することもできます。

Write ##class(cosFaker.JSON).GetDataJSONFromJSON("{ip:'ipv4',created_at:'date.backward 40',login:'username', text: 'words 3'}")

{
    "created_at":"2019-03-08",
    "ip":"95.226.124.187",
    "login":"john46",
    "text":"temporibus fugit deserunt"
}

 

以下は、cosFakerクラスとメソッドの全リストです。

  • cosFaker.Address
    • StreetSuffix
    • StreetPrefix
    • PostCode
    • StreetName
    • Latitude
      • 出力: -54.7274
    • Longitude
      • 出力: -43.9504
    • Capital( Location = “” )
    • State( FullName = 0 )
    • City( State = “” )
    • Country( Abrev = 0 )
    • SecondaryAddress
    • BuildingNumber
  • cosFaker.App
    • FunctionName( Group= “”, Separator = “” )
    • AppAction( Group= “” )
    • AppType
  • cosFaker.Coffee
    • BlendName
      • 出力: Cascara Cake
    • Variety
      • 出力: Mundo Novo
    • Notes
      • 出力: crisp, slick, nutella, potato defect!, red apple
    • Origin
      • 出力: Rulindo, Rwanda
  • cosFaker.Color
    • Hexadecimal
      • 出力: #A50BD7
    • RGB
      • 出力: 189,180,195
    • Name
  • cosFaker.Commerce
    • ProductName
    • Product
    • PromotionCode
    • Color
    • Department
    • Price( Min = 0, Max = 1000, Dec = 2, Symbol = “” )
      • 出力: 556.88
    • CNPJ( Pretty = 1 )
      • CNPJはブラジルの法人用税務登記番号です
      • 出力: 44.383.315/0001-30
  • cosFaker.Company
    • Name
    • Profession
    • Industry
  • cosFaker.Dates
    • Forward( Days = 365, Format = 3 )
    • Backward( Days = 365, Format = 3 )
  • cosFaker.DragonBall
    • Character
      • 出力: Gogeta
  • cosFaker.File
    • Extension
      • 出力: txt
    • MimeType
      • 出力: application/font-woff
    • Filename( Dir = “”, Name = “”, Ext = “”, DirectorySeparator = “/” )
      • 出力: repellat.architecto.aut/aliquid.gif
  • cosFaker.Finance
    • Amount( Min = 0, Max = 10000, Dec = 2, Separator= “,”, Symbol = “” )
      • 出力: 3949,18
    • CreditCard( Type = “” )
      • 出力: 3476-581511-6349
    • BitcoinAddress( Min = 24, Max = 34 )
      • 出力: 1WoR6fYvsE8gNXkBkeXvNqGECPUZ
  • cosFaker.Game
    • MortalKombat
      • 出力: Raiden
    • StreetFighter
      • 出力: Akuma
    • Card( Abrev = 0 )
      • 出力: 5 of Diamonds
  • cosFaker.Internet
    • UserName( FirstName = “”, LastName = “” )
    • Email( FirstName = “”, LastName = “”, Provider = “” )
    • Protocol
      • 出力: http
    • DomainWord
    • DomainName
    • Url
    • Avatar( Size = “” )
    • Slug( Words = “”, Glue = “” )
    • IPV4
      • 出力: 226.7.213.228
    • IPV6
      • 出力: 0532:0b70:35f6:00fd:041f:5655:74c8:83fe
    • MAC
      • 出力: 73:B0:82:D0:BC:70
  • cosFaker.JSON
    • GetDataOBJFromJSON( Json = “” //  JSON template string to create data )
      • パラメーターの例: "{dates:'5 date'}"
      • 出力: {"dates":["2019-02-19","2019-12-21","2018-07-02","2017-05-25","2016-08-14"]}
  • cosFaker.Job
    • Title
    • Field
    • Skills
  • cosFaker.Lorem
    • Word
    • Words( Num = “” )
    • Sentence( WordCount = “”, Min = 3, Max = 10 )
      • 出力: Sapiente et accusamus reiciendis iure qui est.
    • Sentences( SentenceCount = “”, Separator = “” )
    • Paragraph( SentenceCount = “” )
    • Paragraphs( ParagraphCount = “”, Separator = “” )
    • Lines( LineCount = “” )
    • Text( Times = 1 )
    • Hipster( ParagraphCount = “”, Separator = “” )
  • cosFaker.Name
    • FirstName( Gender = “” )
    • LastName
    • FullName( Gender = “” )
    • Suffix
  • cosFaker.Person
    • cpf( Pretty = 1 )
      • CPFはブラジルの社会保障番号です
      • 出力: 469.655.208-09
  • cosFaker.Phone
    • PhoneNumber( Area = 1 )
      • 出力: (36) 9560-9757
    • CellPhone( Area = 1 )
      • 出力: (77) 94497-9538
    • AreaCode
      • 出力: 17
  • cosFaker.Pokemon
    • Pokemon( EvolvesFrom = “” )
      • 出力: Kingdra
  • cosFaker.StarWars
    • Characters
      • 出力: Darth Vader
    • Droids
      • 出力: C-3PO
    • Planets
      • 出力: Takodana
    • Quotes
      • 出力: Only at the end do you realize the power of the Dark Side.
    • Species
      • 出力: Hutt
    • Vehicles
      • 出力: ATT Battle Tank
    • WookieWords
      • 出力: nng
    • WookieSentence( SentenceCount = “” )
      • 出力: ruh ga ru hnn-rowr mumwa ru ru mumwa.
  • cosFaker.UFC
    • Category
      • 出力: Middleweight
    • Fighter( Category = “”, Country = “”, WithISOCountry = 0 )
      • 出力: Dmitry Poberezhets
    • Featherweight( Country = “” )
      • 出力: Yair Rodriguez
    • Middleweight( Country = “” )
      • 出力: Elias Theodorou
    • Welterweight( Country = “” )
      • 出力: Charlie Ward
    • Lightweight( Country = “” )
      • 出力: Tae Hyun Bang
    • Bantamweight( Country = “” )
      • 出力: Alejandro Pérez
    • Flyweight( Country = “” )
      • 出力: Ben Nguyen
    • Heavyweight( Country = “” )
      • 出力: Francis Ngannou
    • LightHeavyweight( Country = “” )
      • 出力: Paul Craig
    • Nickname( Fighter = “” )
      • 出力: Abacus

ユーザー名を返すメソッドでユーザーのクラスを作成してみましょう。ユーザー名はFirstNameとLastNameを連結したものになります。

Class Production.User Extends %RegisteredObject

{


Property FirstName As %String;


Property LastName As %String;


Method Username() As %String

{

}


}

 

Class TDD.User Extends %UnitTest.TestCase

{


Method TestUsername()

{

  Set firstName = ##class(cosFaker.Name).FirstName(),

    lastName = ##class(cosFaker.Name).LastName(),

    user = ##class(Production.User).%New(),

    user.FirstName = firstName,

    user.LastName = lastName


  Do $$$AssertEquals(user.Username(), firstName _ "." _ lastName)

}


}

リファクタリング:

Class Production.User Extends %RegisteredObject

{


Property FirstName As %String;


Property LastName As %String;


Method Username() As %String

{

  Quit ..FirstName _ "." _ ..LastName

}


}

アカウントの有効期限を追加して、検証することにします。

Class Production.User Extends %RegisteredObject

{


Property FirstName As %String;


Property LastName As %String;


Property AccountExpires As %Date;


Method Username() As %String

{

  Quit ..FirstName _ "." _ ..LastName

}


Method Expired() As %Boolean

{

}


}




Class TDD.User Extends %UnitTest.TestCase

{


Method TestUsername()

{

  Set firstName = ##class(cosFaker.Name).FirstName(),

    lastName = ##class(cosFaker.Name).LastName(),

    user = ##class(Production.User).%New(),

    user.FirstName = firstName,

    user.LastName = lastName

    Do $$$AssertEquals(user.Username(), firstName _ "." _ lastName)

}


Method TestWhenIsNotExpired() As %Status

{

  Set user = ##class(Production.User).%New(),

    user.AccountExpires = ##class(cosFaker.Dates).Forward(40)

  Do $$$AssertNotTrue(user.Expired())

}


}

リファクタリング:

Method Expired() As %Boolean

{

  Quit ($system.SQL.DATEDIFF("dd", ..AccountExpires, +$Horolog) > 0)

}

では、アカウントの有効期限が切れた場合をテストしてみましょう。

Method TestWhenIsExpired() As %Status

{

  Set user = ##class(Production.User).%New(),

    user.AccountExpires = ##class(cosFaker.Dates).Backward(40)

  Do $$$AssertTrue(user.Expired())

}

すべてがグリーンです。

これらはあまり大したことのない例かもしれませんが、このようにすることで、コードだけでなくクラスの設計も単純にすることができます。
 

まとめ

この記事では、テスト駆動開発と%UnitTestクラスの使用方法について少しだけ学習しました。
また、cosFakerとテスト用の偽のデータの生成方法についても説明しました。

テストとTDDについては、これらの実践をレガシーコードで使用する方法、統合テスト、受け入れテスト駆動開発など、ほかにも学習することがたくさんあります。
詳細については、次の2冊が私のイチオシです。
 

『Test Driven Development Teste e design no mundo real com Ruby』Mauricio Aniche著 - これについては英語版が出版されているかわかりません。 Java、C#、Ruby、およびPHP版があります。 あまりの素晴らしさに感銘を受けた一冊です。

そしてもちろん、Kent Beckの『Test Driven Development by Example』。

コメントやご質問はお気軽にどうぞ。
これで、おしまいです。

0
0 392