In large .NET Core projects, manually writing unit tests for every method can be time-consuming. But what if you could automatically invoke all public methods, validate their outputs and ensure they don't throw exceptions even for classes that use dependency injection?
In this guide, we’ll show how to use:
Reflection
to discover and invoke public methodsxUnit
as the testing frameworkFluentAssertions
for expressive validationsMicrosoft.Extensions.DependencyInjection
to resolve dependencies
🧱 Prerequisites
Before starting, make sure your test project includes the following NuGet packages:
dotnet add package xunit
dotnet add package FluentAssertions
dotnet add package Microsoft.Extensions.DependencyInjection
🧪 The Core Idea
We will:
Scan all classes in the assembly
Instantiate them via DI if possible
Call their public methods with default parameters
Assert that each method does not throw and (optionally) returns non-null
🛠️ Sample Test Code
Here’s the full implementation:
using System;
using System.Linq;
using System.Reflection;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
public class MethodApprovals
{
private readonly IServiceProvider _serviceProvider;
public MethodApprovals()
{
var services = new ServiceCollection();
ConfigureServices(services);
_serviceProvider = services.BuildServiceProvider();
}
private void ConfigureServices(IServiceCollection services)
{
// Register real or fake dependencies
services.AddTransient<DependencyA>();
services.AddTransient<DependencyB>();
services.AddTransient<DependentClass>();
}
[Fact]
public void ApproveAllMethodsInAssembly()
{
var assembly = Assembly.GetExecutingAssembly();
foreach (var type in assembly.GetTypes())
{
if (!type.IsClass || type.IsAbstract || type.Namespace == null)
continue;
ApproveMethods(type);
}
}
private void ApproveMethods(Type type)
{
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
.Where(m => !m.IsSpecialName); // Ignore property accessors
foreach (var method in methods)
{
try
{
object instance = method.IsStatic
? null
: _serviceProvider.GetService(type) ?? Activator.CreateInstance(type);
var parameters = method.GetParameters()
.Select(p => GetDefault(p.ParameterType))
.ToArray();
var result = method.Invoke(instance, parameters);
result.Should().NotBeNull($"{type.FullName}.{method.Name} should return non-null");
}
catch (Exception ex)
{
ex.Should().BeNull($"{type.FullName}.{method.Name} should not throw an exception but it did: {ex}");
}
}
}
private object GetDefault(Type type)
{
return type.IsValueType ? Activator.CreateInstance(type) : null;
}
}
📦 Example Classes Under Test
Let’s say your application has this simple service class:
public class DependencyA { }
public class DependencyB { }
public class DependentClass
{
private readonly DependencyA _a;
private readonly DependencyB _b;
public DependentClass(DependencyA a, DependencyB b)
{
_a = a;
_b = b;
}
public string SayHello() => "Hello from DependentClass!";
public int Add(int x = 1, int y = 2) => x + y;
}
This will be picked up and tested automatically, including DI instantiation.
⚙️ When and Why to Use This
✅ Great for smoke testing: You can quickly check that your services aren’t crashing with default values.
✅ Works well for DTOs and services with light logic: Especially if they don’t need real data inputs.
⚠️ Not a replacement for proper unit tests: It doesn't validate business logic correctness just that methods execute cleanly with default inputs.
🧠 Tips for Real-World Use
Replace
GetDefault(...)
with AutoFixture if you want smart input generation.Add filters to skip methods like
Dispose()
or those with required complex parameters.Log or collect results instead of asserting, if you want a diagnostic report.
🧾 Conclusion
This technique is a powerful way to bulk-test public APIs in your .NET projects with minimal effort. By combining reflection, FluentAssertions, xUnit and dependency injection, you can get immediate feedback on method stability and surface hidden exceptions early.
Use it as a safety net alongside your unit tests and you’ll catch a surprising number of regressions or missed registrations!