3

I wrote some code that looks like the following, to unit-test the implementation of a C# finalizer method.

In TeamCity version 2017.1.3, I use a build step with the NUnit runner type to run this test using NUnit.ConsoleRunner.3.7.0.

The test fails if I enable the JetBrains dotCover .NET Coverage tool.

The test passes if I disable .NET Coverage.

What can dotCover possibly be doing to influence test outcome?

using System;
using System.Threading;
using NUnit.Framework;
using Telerik.JustMock;

namespace MyNamespace
{
    public interface IWin32Api
    {
        void FreeResource();
    }

    public class Disposable
    {
        private readonly IWin32Api _win32;

        public Disposable(IWin32Api win32)
        {
            _win32 = win32;
        }

        ~Disposable()
        {
            _win32.FreeResource();
        }
    }

    [TestFixture]
    public class TestFixture
    {
        [Test]
        public void Test()
        {
            using (var signal = new ManualResetEvent(false))
            {
                var win32 = Mock.Create<IWin32Api>();

                Mock.Arrange(() => win32.FreeResource())
                    .DoInstead(() => { signal.Set(); });

                var subjectUnderTest = new Disposable(win32);

                subjectUnderTest = null;

                GC.Collect();

                if (!signal.WaitOne(TimeSpan.FromMinutes(1)))
                {
                    Assert.Fail("IWin32Api.FreeResource never called");
                }
            }
        }
    }
}
hwaien
  • 493
  • 1
  • 3
  • 14
  • I'm not sure about the specifics of JustMock. But did you notice that there is a x86 and x64 version of DotCover ? Maybe that's the issue. – Matthias Aug 28 '17 at 05:49
  • Good suggestion. Unfortunately I tried both x86 and x64 and the symptom is the same. – hwaien Aug 28 '17 at 17:55
  • One thing I noticed is that dotCover runs the [`nunit-agent.exe`](https://github.com/nunit/docs/wiki/NUnit-Agent) executable. When I turn off code coverage, only `nunit3-console.exe` is run; `nunit-agent.exe` is not run. Could `nunit-agent.exe` have something to do with finalizers not being run during garbage collection? – hwaien Aug 28 '17 at 20:36

1 Answers1

2

I still do not understand what's going on, but I was able to fix the problem and get the test to pass by using an immediately-invoked function expression.

The test method now looks like this:

[Test]
public void Test()
{
    using (var signal = new ManualResetEvent(false))
    {
        var win32 = Mock.Create<IWin32Api>();

        Mock.Arrange(() => win32.FreeResource())
            .DoInstead(() => { signal.Set(); });

        new Action(() => { new Disposable(win32); })();

        GC.Collect();

        GC.WaitForPendingFinalizers();

        if (!signal.WaitOne(TimeSpan.FromMinutes(1)))
        {
            Assert.Fail("IWin32Api.FreeResource never called");
        }
    }
}

The test fails if I replace new Action(() => { new Disposable(win32); })(); with new Disposable(win32); or with var d = new Disposable(win32); d = null;

hwaien
  • 493
  • 1
  • 3
  • 14
  • Thank you for this! I still wasted an hour because I didn't realize the problem was caused by dotCover, but at least I already had the solution ready! :) – Melvyn Oct 29 '20 at 17:05