9
votes

I just upgraded a bunch of projects to VS2015/C#6.

Now MSTest's Code Coverage analysis is reporting that some auto properties aren't covered by unit tests. This wasn't the case in Visual Studio 2013, and I suspect it may be something to do with the new autoproperty features in C#6.

Dealing with all the false-positives this generates rather defeats the purpose of the Code Coverage tool as it makes it practically impossible to identify actual code lacking test coverage. We don't want to write unit tests for all our DTOs, and I'd really rather not have to go through the project annotating every single auto-property with ExcludeFromCodeCoverage.

I've created a working MCVE at https://github.com/iaingalloway/VisualStudioCodeCoverageIssue


  • Open VisualStudio2013.sln in Visual Studio 2013 Premium or Ultimate.
  • Click Test -> Analyze Code Coverage -> All Tests.
  • Observe that the "Code Coverage Results" window reports 0 Blocks "Not Covered".

  • Open VisualStudio2015.sln in Visual Studio 2015 Enterprise.
  • Click Test -> Analyze Code Coverage -> All Tests.
  • Observe that the "Code Coverage Results" window reports 1 Block "Not Covered" (the getter for ExampleDto.Value)

Is it possible to configure the built-in Code Coverage tool in Visual Studio 2015 to ignore auto-properties like Visual Studio 2013 does?

3
This behaviour appears to be a bug in Visual Studio 2015. There are currently no workarounds other than using [ExcludeFromCodeCoverage]. You can monitor the progress of the ticket at:- connect.microsoft.com/VisualStudio/Feedback/Details/1742106Wayne Birch
The problem is that you have properties on a class that are not touched by any unit test code. They are unused code. The question that you should be asking is why do you have these properties? If there is a good answer for them, by all means use the [Exclude...] attribute, but immediately follow it with a // reason why each can be excluded.StingyJack

3 Answers

12
votes

As a workaround, you can add the following to your .runsettings file:-

<RunSettings>
  <DataCollectionRunSettings>
    <DataCollector ...>
      <Configuration>
        <CodeCoverage>
          <Functions>
            <Exclude>
              <Function>.*get_.*</Function>
              <Function>.*set_.*</Function>
            </Exclude>
          ...

It's not a great workaround, but as long as you aren't using any functions with "get_" or "set_" in the names it should get you the behaviour you need.

2
votes

I didn't like filtering all get/set methods, especially since I sometimes write get and set logic that needs to be tested. For me, for just basic coverage of relatively simple models, the following pair of xUnit tests has worked well:

    public class ModelsGetSetTest
    {
        [ClassData(typeof(ModelTestDataGenerator))]
        [Theory]
        public void GettersGetWithoutError<T>(T model)
        {
            var properties =
                typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            for (var i = 0; i < properties.Length; i++)
            {
                var prop = properties[i];
                if (prop.GetGetMethod(true) != null)
                    prop.GetValue(model);
            }
        }

        [ClassData(typeof(ModelTestDataGenerator))]
        [Theory]
        public void SettersSetWithoutError<T>(T model)
        {
            var properties =
                typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            for (var i = 0; i < properties.Length; i++)
            {
                var prop = properties[i];
                if (prop.GetSetMethod(true) != null)
                    prop.SetValue(model, null);
            }
        }

        public class ModelTestDataGenerator : IEnumerable<object[]>
        {
            private readonly List<object[]> _data = new List<object[]>();

            public ModelTestDataGenerator()
            {
                var assembly = typeof(Program).Assembly;
                var nsprefix = $"{typeof(Program).Namespace}.{nameof(Models)}";
                var modelTypes = assembly.GetTypes()
                    .Where(t => t.IsClass && !t.IsGenericType) // can instantiate without much hubbub
                    .Where(t => t.Namespace.StartsWith(nsprefix)) // is a model
                    .Where(t => t.GetConstructor(Type.EmptyTypes) != null) // has parameterless constructor
                    .ToList();
                foreach (var modelType in modelTypes) _data.Add(new[] {Activator.CreateInstance(modelType)});
            }

            public IEnumerator<object[]> GetEnumerator()
            {
                return _data.GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    }

Updated 2020-03-18: This version uses reflection to find your models under a particular namespace.

0
votes

I think [ExcludeFromCodeCoverage] is your only option. It is just a one-time thing you are going to have to do. I, personally, DO write unit tests on the property getter/setters, especially ones such as in WPF, where I want to be sure the property change notifications occur.