switch que objeto hacer estructuras else ejemplo destructors como clases c# .net unit-testing idisposable finalizer

que - if else c#



¿Cómo puedo probar un finalizador de unidad? (4)

Tengo la siguiente clase, que es un decorador para un objeto IDisposable (omití las cosas que agrega) que implementa IDisposable usando un patrón común:

public class DisposableDecorator : IDisposable { private readonly IDisposable _innerDisposable; public DisposableDecorator(IDisposable innerDisposable) { _innerDisposable = innerDisposable; } #region IDisposable Members public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion ~DisposableDecorator() { Dispose(false); } protected virtual void Dispose(bool disposing) { if (disposing) _innerDisposable.Dispose(); } }

Puedo probar fácilmente que innerDisposable se elimina cuando se llama Dispose() :

[Test] public void Dispose__DisposesInnerDisposable() { var mockInnerDisposable = new Mock<IDisposable>(); new DisposableDecorator(mockInnerDisposable.Object).Dispose(); mockInnerDisposable.Verify(x => x.Dispose()); }

Pero, ¿cómo escribo una prueba para asegurarme de que innerDisposable no se innerDisposable por el finalizador? Quiero escribir algo como esto pero falla, probablemente porque el hilo GC no ha llamado al finalizador:

[Test] public void Finalizer__DoesNotDisposeInnerDisposable() { var mockInnerDisposable = new Mock<IDisposable>(); new DisposableDecorator(mockInnerDisposable.Object); GC.Collect(); mockInnerDisposable.Verify(x => x.Dispose(), Times.Never()); }


Al escribir pruebas unitarias, siempre debe intentar probar el comportamiento visible externo, no los detalles de la implementación. Se podría argumentar que suprimir la finalización es de hecho fuera del comportamiento visible, pero por otro lado, probablemente no hay manera de que puedas (ni deberías) burlarte del coleccionista de garabage.

Lo que intenta asegurarse en su caso es que se sigue una "mejor práctica" o una práctica de codificación. Se debe hacer cumplir a través de una herramienta que se hace para este propósito, como FxCop .


No es fácil probar la finalización, pero puede ser más fácil probar si un objeto está sujeto a la recolección de basura.

Esto se puede hacer con unas referencias débiles.

En una prueba, es importante que las variables locales se queden fuera del alcance antes de llamar a GC.Collect (). La forma más fácil de asegurarse es el alcance de una función.

class Stuff { ~Stuff() { } } WeakReference CreateWithWeakReference<T>(Func<T> factory) { return new WeakReference(factory()); } [Test] public void TestEverythingOutOfScopeIsReleased() { var tracked = new List<WeakReference>(); var referer = new List<Stuff>(); tracked.Add(CreateWithWeakReference(() => { var stuff = new Stuff(); referer.Add(stuff); return stuff; })); // Run some code that is expected to release the references referer.Clear(); GC.Collect(); Assert.IsFalse(tracked.Any(o => o.IsAlive), "All objects should have been released"); } [Test] public void TestLocalVariableIsStillInScope() { var tracked = new List<WeakReference>(); var referer = new List<Stuff>(); for (var i = 0; i < 10; i++) { var stuff = new Stuff(); tracked.Add(CreateWithWeakReference(() => { referer.Add(stuff); return stuff; })); } // Run some code that is expected to release the references referer.Clear(); GC.Collect(); // Following holds because of the stuff variable is still on stack! Assert.IsTrue(tracked.Count(o => o.IsAlive) == 1, "Should still have a reference to the last one from the for loop"); }



Yo uso Appdomain (ver muestra abajo). Class TemporaryFile crea un archivo temporal en el constructor y lo elimina en Dispose o in finalizer ~ TemporaryFile ().

Desafortunadamente, GC.WaitForPendingFinalizers () ; No me ayuda a probar el finalizador.

[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 { TemporaryFile temporaryFile = new TemporaryFile(); 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)); }