Mic*_*ker 5 c# automated-tests dependency-injection asp.net-core
我们偶尔会遇到一些问题,即某人将某些DI添加到控制器中,但忘记将相关行添加到Startup.cs中以设置对象的范围.
这不会阻止应用程序启动,而是在匹配相关端点时抛出异常.
是否有任何方式以编程方式检查所有控制器是否有效并阻止应用程序启动否则?
或者有一种简单的方法可以编写一个全能自动化测试来检查每个控制器是否可以使用Startup.cs中的指定DI进行实例化?
总结自https://andrewlock.net/new-in-asp-net-core-3-service-provider-validation/,请参阅链接了解更多详细信息。
从 ASP.NET 3.0 开始,现在有一种方法可以验证构建时的控制器依赖关系:
启动.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddControllersAsServices(); // This part adds Controllers to DI
Run Code Online (Sandbox Code Playgroud)
程序.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
// Validate DI on build
options.ValidateOnBuild = true;
});
Run Code Online (Sandbox Code Playgroud)
笔记:
_service = provider.GetRequiredService<MyService>();[FromServices]方法中的参数(即它仅检查构造函数依赖项)services.AddSingleton(typeof(MyServiceWithGeneric<>)); services.AddSingleton<MyService>(provider =>
{
var nestedService = provider.GetRequiredService<MyNestedService>();
return new MyService(nestedService);
});
Run Code Online (Sandbox Code Playgroud)
你可以这样写:
[TestFixture]
[Category(TestCategory.Integration)]
public class ControllersResolutionTest
{
[Test]
public void VerifyControllers()
{
var builder = new WebHostBuilder()
.UseStartup<IntegrationTestsStartup>();
var testServer = new TestServer(builder);
var controllersAssembly = typeof(UsersController).Assembly;
var controllers = controllersAssembly.ExportedTypes.Where(x => typeof(ControllerBase).IsAssignableFrom(x));
var activator = testServer.Host.Services.GetService<IControllerActivator>();
var serviceProvider = testServer.Host.Services.GetService<IServiceProvider>();
var errors = new Dictionary<Type, Exception>();
foreach (var controllerType in controllers)
{
try
{
var actionContext = new ActionContext(
new DefaultHttpContext
{
RequestServices = serviceProvider
},
new RouteData(),
new ControllerActionDescriptor
{
ControllerTypeInfo = controllerType.GetTypeInfo()
});
activator.Create(new ControllerContext(actionContext));
}
catch (Exception e)
{
errors.Add(controllerType, e);
}
}
if (errors.Any())
{
Assert.Fail(
string.Join(
Environment.NewLine,
errors.Select(x => $"Failed to resolve controller {x.Key.Name} due to {x.Value.ToString()}")));
}
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码实际上经历了使用数据库配置设置 asp.net 核心应用程序的完整过程,以及你在启动时没有的东西,所以你可能想从中派生并删除/模拟一些东西。此代码还需要Microsoft.AspNetCore.TestHost nuget。
我更改了我提出的原始代码,因为它没有按预期工作。
改编@Rafal 对 xUnit的回答以避免管理异常迭代并跳过对 TestHost 的依赖:
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace redacted.WebApi.Test {
using Core;
public class VerifyDependencies {
[Theory]
[MemberData(nameof(Controllers))]
public void VerifyController(Type controllerType) {
var services = new WebHostBuilder().UseStartup<Startup>().Build().Services;
ControllerUtilities.Create(
controllerType,
services.GetService<IControllerActivator>(),
services.GetService<IServiceProvider>()
);
}
public static IEnumerable<object[]> Controllers() {
return ControllerUtilities.GetControllers<ApiController>().Select(c => new object[] { c });
}
}
public class ControllerUtilities {
public static IEnumerable<Type> GetControllers<TProject>() {
return typeof(TProject)
.Assembly.ExportedTypes
.Where(x => typeof(Controller).IsAssignableFrom(x));
}
public static Controller Create(Type controllerType, IControllerActivator activator, IServiceProvider serviceProvider) {
return activator.Create(new ControllerContext(new ActionContext(
new DefaultHttpContext {
RequestServices = serviceProvider
},
new RouteData(),
new ControllerActionDescriptor {
ControllerTypeInfo = controllerType.GetTypeInfo()
})
)) as Controller;
}
public static TController Create<TController>(IControllerActivator activator, IServiceProvider serviceProvider) where TController : Controller {
return Create(typeof(TController), activator, serviceProvider) as TController;
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
660 次 |
| 最近记录: |