単体テストが開発プロセスの不可欠な部分である場合、アプリケーションの機能を検証するために多数のテストを定期的に実行します。 ここで、特別な「メモリテスト」を作成できると想像してください。 たとえば、特定の種類のオブジェクトの存在をメモリで確認してリークを検出するテスト、またはメモリトラフィックを監視し、トラフィック(割り当てられたボリューム)が指定されたしきい値を超えると「ドロップ」するテスト。 これは、 dotMemory Unitフレームワークでできることです。 dotMemory UnitはNuGetパッケージとして配布されており、次のスクリプトを実行できます。
- 特定のタイプのオブジェクトの存在についてメモリをチェックします。
- メモリトラフィックを確認します。
- メモリのスナップショット(以降、スナップショットと呼ぶ)の比較。
- スナップショットをディスクに保存して、後でdotMemory(JetBrainsメモリプロファイラー)で分析します。
つまり、dotMemory Unitは、メモリプロファイラーの機能を使用してユニットテストフレームワークの機能を拡張します。
どのように機能しますか?
- dotMemoryユニットは、テストプロジェクトにインストールされたNuGetパッケージとして配布されます。
PM> Install-Package JetBrains.DotMemoryUnit
- dotMemory Unitには、ReSharperに含まれるランナーユニットテストが必要です。 したがって、dotMemoryユニットテストを実行するには、ReSharper 9.1またはdotCover 3.1をマシンにインストールする必要があります。
- dotMemory Unitパッケージをインストールした後、dotMemory Unit項目の下に追加のRun Unit TestsがReSharperメニューに表示されます。 このモードでは、ランナーテストはdotMemory Unit呼び出しを残りのコードとともに実行します。 このテストを通常どおり実行する場合(dotMemory Unitサポートなし)、dotMemory Unitフレームワークへのすべての呼び出しは無視されます。
- dotMemoryユニットは、MSTestやNUnitなど、ReSharperがサポートするすべてのユニットテストフレームワークと互換性があります。
- JetBrains TeamCityなどのCIシステムと統合するための個別の「ランチャー」は、次のリリースの1つで計画されています。
- dotMemory Unitは完全に無料です。
例1:特定のオブジェクトのメモリを確認する
簡単なものから始めましょう。 最も有用なシナリオの1つは、特定のタイプのオブジェクトのメモリをチェックしてリークを識別することです。
[Test] public void TestMethod1() { ... // - // , 0 Foo dotMemory.Check(memory => //1, 2 { Assert.That(memory.GetObjects(where => where.Type.Is<Foo>()).ObjectsCount, Is.EqualTo(0)); //3 }); }
- ラムダは、
dotMemory
静的クラスのCheck
メソッドに渡されます。 このメソッドは、dotMemory UnitメニューのRun Unit Testsを使用してこのテストを実行する場合にのみ呼び出されます 。 - ラムダに渡される
memory
オブジェクトには、プログラム実行の現在の時点でメモリ内のすべてのオブジェクトのデータが含まれています。 -
GetObjects
メソッドは、次のラムダで渡された条件に一致するオブジェクトのセットを返します。 たとえば、次のコード行は、メモリからFoo
型のオブジェクトのみを選択します。Assert
式は、メモリ内にFoo
型のオブジェクトが0
個あることを前提としています。
dotMemoryユニットはAssert
特定の構文を使用する義務を負わないことに注意してください。 テストの対象となるフレームワークの構文を使用するだけです。 たとえば、上記の例の行(NUnit用に記述)は、MSTest用に書き直すことができます。
Assert.AreEqual(0, memory.GetObjects(where => where.Type.Is<Foo>()).ObjectsCount);
dotMemory Unitを使用すると、ほとんどすべての条件でオブジェクトを選択し、オブジェクトの数だけデータを受信し、それらを
Assert
式で使用できます。 たとえば、ラージオブジェクトヒープにオブジェクトが含まれていないことを確認できます。
Assert.That(memory.GetObjects(where => where.Generation.Is(Generation.Loh)).ObjectsCount, Is.EqualTo(0));
例2:メモリトラフィックの確認
メモリトラフィック(割り当てられたデータボリューム)を確認するためのテストは、さらに簡単に見えます。 必要なのは、
AssertTraffic
属性を使用してテストを「マーク」すること
AssertTraffic
です。 次の例では、
TestMethod1
によって割り当てられたメモリが1000バイトを超えないと仮定します。
[AssertTraffic(AllocatedMemoryAmount = 1000)] [Test] public void TestMethod1() { ... // - }
例3:メモリトラフィックをチェックするための複雑なスクリプト
トラフィックに関するより詳細な情報(たとえば、特定のタイプのオブジェクトの割り当てに関するデータ)が必要な場合は、例1に示すようなアプローチを使用
dotMemory.Check
ます
dotMemory.Check
メソッドに渡される
dotMemory.Check
を使用すると、さまざまな条件に従ってデータをフィルター
dotMemory.Check
ます。
var memoryCheckPoint1 = dotMemory.Check(); // 1 foo.Bar(); var memoryCheckPoint2 = dotMemory.Check(memory => { // 2 Assert.That(memory.GetTrafficFrom(memoryCheckPoint1).Where(obj => obj.Interface.Is<IFoo>()).AllocatedMemory.SizeInBytes, Is.LessThan(1000)); }); bar.Foo(); dotMemory.Check(memory => { // 3 Assert.That(memory.GetTrafficFrom(memoryCheckPoint2).Where(obj => obj.Type.Is<Bar>()).AllocatedMemory.ObjectsCount, Is.LessThan(10)); });
- トラフィックを分析する期間をマークするには、同じ
dotMemory.Check
メソッドで作成された「チェックポイント」を使用します(ごdotMemory.Check
、このメソッドは呼び出し時にメモリスナップショットを単に削除します)。 - 間隔の開始点を定義するチェックポイントは、
GetTrafficFrom
メソッドに渡されます。
たとえば、この行は、IFoo
インターフェイスを実装し、memoryCheckPoint1
とmemoryCheckPoint2
間に作成されたオブジェクトの合計サイズが1000バイトを超えないことをmemoryCheckPoint2
しています。
- 以前に作成されたチェックポイントのいずれかでデータを受信できます。 したがって、この行は、
dotMemory.Check
とmemoryCheckPoint2
現在の呼び出し間のトラフィックデータを要求します。
例4:スナップショットの比較
「アダルト」dotMemoryプロファイラーと同様に、チェックポイントを使用して、トラフィックを分析するだけでなく、それらを相互に比較することもできます。 以下の例では、
memoryCheckPoint1
と
memoryCheckPoint1
の2番目の呼び出しの間の間隔で、
MyApp
名前空間内のオブジェクトがガベージコレクションを生き残っていないと仮定し
MyApp
。
var memoryCheckPoint1 = dotMemory.Check(); foo.Bar(); dotMemory.Check(memory => { Assert.That(memory.GetDifference(memoryCheckPoint1) .GetSurvivedObjects().GetObjects(where => where.Namespace.Like("MyApp")).ObjectsCount, Is.EqualTo(0)); });
おわりに
dotMemory Unitは非常に柔軟性があり、アプリケーションのメモリ使用量を完全に制御できます。 通常のテストを使用するときは、「メモリのテスト」を使用します。
- メモリリークを発見したら、コードのこの部分をカバーするテストを作成します。
- dotMemory Unitを使用して統合テストを記述し、新しい機能によってメモリの問題が発生しないことを確認します。