ファイナライザコードをテストする方法(c#)

明らかでないタスクの1つは、Donnetクラスのファイナライザーに実装されたコードをテストすることです。

このノートでは、この問題を解決する1つの方法について説明します。





たとえば、コンストラクターで一意の一時ファイルを作成し、 Dipose()またはファイナライザーで削除する必要があるMyTemporaryFile クラス (一時ファイル)があります。



public class MyTemporaryFile : IDisposable { public string FileName { private set; get; } public MyTemporaryFile() { FileName = Path.GetTempFileName(); } public void Dispose() { Dispose(true); } ~MyTemporaryFile() { Dispose(false); } void Dispose(bool disposing) { if (disposing) { GC.SuppressFinalize(this); } DeleteFile(); } void DeleteFile() { if (FileName != null) { File.Delete(FileName); FileName = null; } } }
      
      







Disposeパターンの実装かなり標準的なもので、 Habré議論されました 。 この実装にはおそらく微妙な点があるので、「実際の」プログラムではこの点に留意してください。



しかし、なんらかの理由で、この問題に関する議論は見つかりませんでしたが、ファイナライザに実装されたコードをテストする方法を見つけました。



「非常に単純な」テスト実装が機能しないことは明らかです。



  [Test] public void TestMyTemporaryFile_without_Dispose() { var temporaryFile = new MyTemporaryFile(); string createdTemporaryFileName = temporaryFile.FileName; Assert.IsTrue(File.Exists(createdTemporaryFileName)); temporaryFile = null; Assert.IsFalse(File.Exists(createdTemporaryFileName)); }
      
      





実際、 temporaryFileに nullを設定してもファイナライザーは呼び出されません。



GC.WaitForPendingFinalizers()を呼び出すアドバイスがありました しかし、何らかの理由でこのテストは私を助けませんでした。



offtopic: 昔々、c#に関するいくつかの講義で、彼らはAppDomainについて話しました。 なぜこれが必要なのか本当に理解していませんでした。 さて、ほとんどの講師が「 一般的な 聞き手 」に対して「 一般的な 聞き手 」に伝える方法を知っています。 講師の言葉からDisposeパターンを理解できたことはありません。 面白いことは、私が彼を少し理解し始めた後、私は講師が何を考えていたのかほとんど推測し始めなかったことです。



したがって、AppDomainを使用すると、ファイナライザコードのテストを簡単に準備できることがわかります。

  [Test] public void TestTemporaryFile_without_Dispose() { const string DOMAIN_NAME = "testDomain"; const string FILENAME_KEY = "fileName"; string testRoot = Directory.GetCurrentDirectory(); AppDomainSetup info = new AppDomainSetup { ApplicationBase = testRoot }; AppDomain testDomain = AppDomain.CreateDomain(DOMAIN_NAME, null, info); testDomain.DoCallBack(delegate { MyTemporaryFile temporaryFile = new MyTemporaryFile(); Assert.IsTrue(File.Exists(temporaryFile.FileName)); AppDomain.CurrentDomain.SetData(FILENAME_KEY, temporaryFile.FileName); }); string createdTemporaryFileName = (string)testDomain.GetData(FILENAME_KEY); Assert.IsTrue(File.Exists(createdTemporaryFileName)); AppDomain.Unload(testDomain); //       ( ),   Assert.IsFalse(File.Exists(createdTemporaryFileName)); }
      
      







ご存じのとおり、 AppDomain.Unload(testDomain) ; コードをアンロードし、メモリをクリアします(ファイナライザが呼び出されることを含む)。

これは、ファイナライザを「強制」し、それに応じてコードをテストするのに役立ちます。





1.講師の一人がファイナライザーで、「 call Dispose、idiot 」という言葉で例外をスローするようアドバイスしました。 どこかに彼は正しいかもしれませんが、管理されていないリソースがある場合は、ファイナライザーも提供する必要があります。

2. MyTemporaryFileクラスの実装は非常に不完全であり、実稼働環境での使用は推奨されていません。

3.ほとんどの場合、このテストの実装にもあらゆる種類の微妙なポイントがありますが、長年の実践でこのテストの誤検出が記録されたことはありません。

4.ファイナライザを他の方法でテストする問題を解決する方法、またはこのアプローチの短所を読んでうれしいです。



ありがとう

イゴール。



All Articles