0

I've been working on implementing OpenCover in a local build of some of the various APIs I work on for the sake of getting a decent testing report structure built for my team. When attempting to run the coverage against my NUnit tests however I've discovered that some of my more important classes are omitted from the report generated and have the visited node for the respective object set to false.

I still receive the proper NUnit results and know that the coverage results I expect are there due to running against Visual Studio's built-in tool to validate also. It's just OpenCover that fails to report correctly.

The common thread in any of these objects going unreported as I've discovered appears to be that they're sealed. I'm guessing that OpenCover is skipping those classes and uses some type of reflection for it's reporting structure, but I haven't had a chance to check out the source to prove this since I'm on a pretty stringent, internal network.

Anybody run into anything similar or have any tips to overcome this issue? I, of course, can't just remove the sealed keyword from my classes since they need it.

Edit* Here's an example I wrote up of something similar:

using System.Text;
using NUnit.Framework;

namespace OpenCover.Sealed.Test
{
    using Helpers;

    [TestFixture]
    public class UtilityTest
    {
        [Test, Owner("Patrick Ramser")]
        public void ConcatTest()
        {
            Utility utility = Utility.CreateNewUtility();

            string concatMsg = utility.Concat
                ("1:{0} 2:{1} 3:{2}", "FIRST", "SECOND", "THIRD");

            Assert.AreEqual
                (concatMsg, "1:FIRST 2:SECOND 3:THIRD", "Wrong message returned!");
        }
    }
}

namespace OpenCover.Sealed.Helpers
{
    public sealed class Utility
    {
        internal Utility()
        {

        }

        public static Utility CreateNewUtility()
        {
            return new Utility();
        }

        public string Concat(string message, params string[] lstStrings)
        {
            StringBuilder builder = new StringBuilder(message);

            for (int i = 0; i < lstStrings.Length; i++)
            {
                builder.Replace("{" + i + "}", lstStrings[i]);
            }

            return builder.ToString();
        }
    }
}

When using both NUnit and the new test assembly I simply reference:

D:\exes\OpenCover\OpenCover.Console.exe
-target:"D:\TestRunners\nunit.console.exe"
-targetargs:"/nologo /domain=Single /xml=C:\NUnit\Artifacts\nunit-results.xml
D:\OpenCover.Sealed.Test\bin\Debug\OpenCover.Sealed.Test.dll"
-output:"C:\NUnit\Artifacts\coverage.xml"
-register
-filter:"+[OpenCover.Sealed*]*"
-returntargetcode

There are no spaces or formatting for the batch script I run, I just limited it between lines to make it more readable.

Could internals either pose any issue?

  • OpenCover does not have a known issue with sealed classes, are you able to provide a sample that replicates the issue. If you are using nunit what arguments are you using? Have you tried the `-noisolation` switch? Alternatively you may also want to try the `-mergebyhash` switch to merge coverage results from multiple locations. – Shaun Wilde Oct 22 '13 at 08:23
  • My NUnit arguments aren't anything special, just: "/nologo /domain=Single /xml=C:\NUnit\Artifacts\nunit-results.xml". The results set populates correctly also and has the right results which shows passing for my tests. I use the -returntargetcode, -register, and -filter attributes for OpenCover itself and they appear to work fine. Implementing no isolation and merge by hash didn't appear to affect anything that I noticed. I still believe it's something programmatic, but I can't tell what it is since OpenCover is just skipping the code. – Patrick Ramser Oct 22 '13 at 15:24
  • Hi Patrick, I have taken your sample code and I unfortunately cannot repeat your issue (100% coverage of the sealed classes) - I have placed my compiled project on [dropbox](https://dl.dropboxusercontent.com/u/16543630/OpenCover/Sealed.zip) for you to look at. I have used the same arguments but the application locations are different and I also used -register:user (but that shouldn't make a difference). Does the assembly undergo any modifications (obfuscation) that may affect opencover injecting its code based on what it has gathered from the PDB? – Shaun Wilde Oct 22 '13 at 18:45

1 Answers1

0

I believe I may have figured out my problem. The issue didn't end up being the sealed classes, rather that those classes weren't being used correctly with NUnit & the tested library.

I pulled all my resources to the same directory & changed NUnit to run in no app. domain (using the /domain=none argument) & that appeared to solve my problem. I'm guessing the fact that my custom NUnit code creates another app. domain & most of my test libraries live in disparate locations was causing some disconnect between the running code OpenCover needs to execute against.

The bad results I received (that I discovered now) were coming from my custom runner only being run through OpenCover which doesn't touch any of my sealed objects. I wasn't even using the test libraries the way it was set-up. Really sorry for the misunderstanding, Shawn! The coverage report works great & the results are now exactly what I needed.

  • I am interested in your setup as to why OpenCover didn't find the code in those other appdomains. If you have a chance to create a repeatable scenario or can provide more details so I can repeat it, I may be able to create a fix. – Shaun Wilde Oct 23 '13 at 20:57
  • I've eliminated the application domain from my custom NUnit code and now the only thing that uses one appears to be NUnit itself. So, it looks like if OpenCover, the NUnit Console Runner, and my test library are in separate directories while using NUnit with a single or multiple domains the coverage report will not generate. Only my NUnit Console Runner is considered by OpenCover in this case. – Patrick Ramser Nov 13 '13 at 22:43
  • Interesting, I'll see if I can replicate - have you tried the `/noshadow` switch as well in these scenarios? – Shaun Wilde Nov 14 '13 at 08:17