The quick answer is that you can reference any (accessible) class in your application in order to get a reference to the assembly. It needn't be the Program
or Startup
class, and it needn't be in the root namespace.
You'll obviously want to choose a class you expect to be persistent, and not subject to being renamed or removed in later versions. Historically, the Startup
class fit that criteria. With the ASP.NET Core 6 minimal hosting model, however, that's obviously not true anymore.
Given this, there are two approaches you can take here.
Option 1: Anchor your Assembly
reference off of an application class
The first option is to anchor off of any arbitrary, public
class from your application. For example, you could use one of your controllers. So long as it's compiled into the same assembly, the Assembly.GetTypes()
call will yield the same results. This might look like e.g.:
using Example.Web.Controllers;
var types = typeof(ExampleController).Assembly.GetTypes();
The main downside of this approach is that the class is completely arbitrary, and could potentially be moved or renamed in the future. Of course, if that did happen, you'll likely need to update your unit tests anyway, so it's not that big of a deal.
Option 2: Expose your Program
class to your test assembly
Another option is to anchor your Assembly
reference off of the class compiled from your Program.cs
file, which is very similar to your previous approach. This requires understanding a bit about how this file is processed by the compiler.
When you use the ASP.NET Core 6 minimal hosting model, you're actually taking advantage of C# 9's top-level statements. The compiler automatically places any top-level statements into a class named Program
, without a namespace.
Note: That happens to align with your use of Program.cs
, but that's completely incidental; you could rename Program.cs
to MyWebApplication.cs
, but the class will still be named Program
.
The problem is that this Program
class is marked as internal
, and thus not accessible to your unit test assembly.
You can work around that, however, by marking your ASP.NET Core assembly's internals as visible to your unit test assembly. This can be done by adding the following to e.g. your AssemblyInfo.cs
:
[assembly: InternalsVisibleTo("Example.Web.Tests")]
Or, as @kal pointed out in the comments, by setting the following in your csproj
file:
<ItemGroup>
<InternalsVisibleTo Include="Example.Web.Tests" />
</ItemGroup>
Once this is done, you can access your Program
class using:
var types = typeof(Program).Assembly.GetTypes();
I'm not a big fan of exposing the internals of my assembly in this way, but it's fairly common practice with unit tests, so I'm including it as an option in case you're already doing this.
Ultimately, this really isn't any different from the first option—you're still anchoring your Assembly
reference off of a different class—but it has the advantage of being anchored to a class we know will always be present, and not some arbitrary, application-specific class. That may also feel more intuitive when reading the code.
<ItemGroup><InternalsVisibleTo Include="MyTestProject" /></ItemGroup>
– Anthe