Search code examples
code-coveragegrpc-c#coverlet

How to exclude GRPC generated C# code from code coverage


We have a C# GRPC service. It references the "Grpc.AspNetCore" NuGet package, which in turn pulls in the "Grpc.Tools" package. From what I can gather, those tools convert our "proto" files in the project into auto-generated code.

The Tests project references the service project, and has NuGet packages Microsoft.NET.Test.Sdk, NUnit3TestAdapter, NUnit, coverlet.collector, Microsoft.CodeAnalysis and some others which I don't think are relevant. We're using dotnet test to run the tests, with the "XPlat Code Coverage" collector.

Someone previously raised a question to Coverlet about excluding the generated code from the code coverage, and they were just waiting for the Grpc.Tools to be changed to attribute things as having the GeneratedCode attribute. It looks like that has been done, because I can see that the generated code usually has that attribute.

So I added the following section to our ".runsettings" file, to exclude things with that attribute:

  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="XPlat Code Coverage">
        <Configuration>
          <ExcludeByAttribute>GeneratedCodeAttribute</ExcludeByAttribute>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>

And this has improved things. But not entirely, partly because that attribute isn't always in place. For example, it doesn't put it on this field:

    private static readonly pb::MessageParser<GetRedactedRequest> _parser = new pb::MessageParser<GetRedactedRequest>(()

or this one

    static readonly string __ServiceName = "MyService.v1.Redacted";

There are also places where the attribute is there, but coverlet still reports these as "uncovered" (the following is taken from the generated report, so some of these lines are truncated):


[global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
static readonly grpc::Marshaller<global::MyService.V1.GetRedactedRequest> __Marshaller_MyService_v1_GetR
[global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
static readonly grpc::Marshaller<global::MyService.V1.GetRedactedResponse> __Marshaller_MyService_v1_Get

[global::System.CodeDom.Compiler.GeneratedCode("grpc_csharp_plugin", null)]
static readonly grpc::Method<global::MyService.V1.GetRedactedRequest, global::MyService.V1.GetRedactedRe
    grpc::MethodType.Unary,
    __ServiceName,
    "GetRedacted",
    __Marshaller_MyService_v1_GetRedactedRequest,
    __Marshaller_MyService_v1_GetRedactedResponse);

There is another way we could exclude it, by putting the generated classes in a particular namespace, and then excluding everything from that namespace; but that would require us each time we add a new "proto" to ensure it's in a namespace that is excluded. I'm looking for a way where I can just exclude all of the generated GRPC code.

I'm not sure whether it's a bug on the Grpc Tools, or on Coverlet (or both); or perhaps we shouldn't be excluding that from coverage - perhaps we should be unit testing it (although that doesn't feel like a "unit" test). How can we exclude this generated GRPC code from our code coverage?


Solution

  • One way to exclude the files generated by Grpc.Tools is to exclude the generated files using coverlet <ExcludeByFile> and having a pattern that matches the cs files in the output directory specified in the Grpc.Tools (defaults to under the obj directory).