単体テストとメモリプロファイリングを組み合わせることはできますか?

メモリプロファイラは、「日常的に使用するユーティリティ」とはほとんど言えません。 ほとんどの場合、開発者はリリース前に製品のプロファイリングを検討します。 そのようなアプローチはうまくいくかもしれませんが、最後の瞬間に何らかのメモリの問題(メモリリークや大量のメモリトラフィックなど)が発見されるまで、すべての計画が破壊されます。 1つの解決策は定期的なプロファイリングかもしれませんが、そのような貴重な時間を費やすことを望む人はほとんどいません。 ただし、解決策があるようです。



単体テストが開発プロセスの不可欠な部分である場合、アプリケーションの機能を検証するために多数のテストを定期的に実行します。 ここで、特別な「メモリテスト」を作成できると想像してください。 たとえば、特定の種類のオブジェクトの存在をメモリで確認してリークを検出するテスト、またはメモリトラフィックを監視し、トラフィック(割り当てられたボリューム)が指定されたしきい値を超えると「ドロップ」するテスト。 これは、 dotMemory Unitフレームワークでできることです。 dotMemory UnitはNuGetパッケージとして配布されており、次のスクリプトを実行できます。



つまり、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 }); }
      
      





  1. ラムダは、 dotMemory



    静的クラスのCheck



    メソッドに渡されます。 このメソッドは、dotMemory UnitメニューのRun Unit Testsを使用してこのテストを実行する場合にのみ呼び出されます
  2. ラムダに渡されるmemory



    オブジェクトには、プログラム実行の現在の時点でメモリ内のすべてのオブジェクトのデータが含まれています。
  3. 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)); });
      
      





  1. トラフィックを分析する期間をマークするには、同じdotMemory.Check



    メソッドで作成された「チェックポイント」を使用します(ごdotMemory.Check



    、このメソッドは呼び出し時にメモリスナップショットを単に削除します)。
  2. 間隔の開始点を定義するチェックポイントは、 GetTrafficFrom



    メソッドに渡されます。

    たとえば、この行は、 IFoo



    インターフェイスを実装し、 memoryCheckPoint1



    memoryCheckPoint2



    間に作成されたオブジェクトの合計サイズが1000バイトを超えないことをmemoryCheckPoint2



    しています。

  3. 以前に作成されたチェックポイントのいずれかでデータを受信できます。 したがって、この行は、 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は非常に柔軟性があり、アプリケーションのメモリ使用量を完全に制御できます。 通常のテストを使用するときは、「メモリのテスト」を使用します。




All Articles