Home > OS >  ASP.NET Core WebApplicationFactory using InternalsVisibleTo does not work in xunit
ASP.NET Core WebApplicationFactory using InternalsVisibleTo does not work in xunit

Time:08-10

I originally posted a question on the asp.net docs and was referred here.

Moving from NET5 -> NET6 broke a lot of testing using the WebApplicationFactory since the public Startup.cs was removed. The alternative was moving to Program which worked until the new templates came about. In the new templates there is no public class program, so we needed to add a public partial class Program {}

I was looking at the docs and they offered a new solution using InternalsVisibleTo which is better imho since it does not change code just for testing purposes.

However that would lead to code like this

public class UnitTest1 : IClassFixture<WebApplicationFactory<Program>>
{
  public UnitTest1(WebApplicationFactory<Program> applicationFactory)
  {
    
  }
}

The problem however is that Program is internal, which triggers CS0051:

UnitTest1.cs(8, 12): [CS0051] Inconsistent accessibility: parameter type 'WebApplicationFactory' is less accessible than method 'UnitTest1.UnitTest1(WebApplicationFactory)')

The problem with that is that xunit requires its class / constructor to be public.

So did anyone manage to get this working without the public partial?

CodePudding user response:

If you just wanted to access the auto-generated internal Program class in your test project, [InternalsVisibleTo] would work.

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
    <_Parameter1>TestProject1</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

However, you're trying to use an internal type as a type parameter to a public type, and expect that public<internal> type to be publicly accessible.

Imagine a scenario with 3 assemblies: A, B, C.
A defines internal class InternalImpl and has [InternalsVisibleTo("B")] set,
B defines public type as follows:

public class Container
{
   public AnotherPublicType<InternalImpl> AnIllegalPublicProperty { get; }
   // ...
}

In the above example, AnIllegalPublicProperty is illegal, because though assembly B has access to type InternalImpl the type is still internal and wont be accessible to assembly C (or any other assembly for that matter).

Infact, though AnotherPublicType is a public class, C# would not allow AnotherPublicType<InternalImpl> to be more accessible than internal, because the highest-allowed accessibility is the lowest accessibility of the type (AnotherPublicType) and its type arguments (InternalImpl). Changing that property to the following would work:

public class Container
{
   internal AnotherPublicType<InternalImpl> AnIllegalPublicProperty { get; }
   // ...
}

For more information, check the spec §7.5.3 Accessibility domains:

The accessibility domain for a constructed type T<A1, ..., Ae> is the intersection of the accessibility domain of the unbound generic type T and the accessibility domains of the type arguments A1, ..., Ae.

To answer your question, either WebApplicationFactory must be made internal (along with IClassFixture and also UnitTest1) or Program must be made public.

I believe xunit requires tests to be public.
So, your only option is to add the following to the end of your Program.cs file:

#pragma warning disable CA1050 // Declare types in namespaces
public partial class Program { }
#pragma warning restore CA1050 // Declare types in namespaces
  • Related