我刚开始使用Asp.net core
依赖注入,我的概念可能不准确。 This docs.asp.net post解释了如何向控制器注入上下文。在测试方面,我对注射很少有困惑。假设我们有以下场景:
public interface ITasksRepository
{
public void Create();
}
//This is fake implementation, using fake DbContext for testing purpose
public class TasksRepositoryFake : ITasksRepository
{
public void Create()
{
FakeDbContext.Add(sometask);
//logic;
}
}
//This is actual implementation, using actual DbContext
public class TasksRepository : ITasksRepository
{
public void Create()
{
DbContext.Add(someTask);
//logic;
}
}
现在为了在控制器中注入上下文,我们将其设计为:
public class TasksController : Controller
{
public ITasksRepository TaskItems { get; set; }
public TodoController(ITaskRepository taskItems)
{
TaskItems = taskItems;
}
//other logic
}
asp.net core
提供的内置功能是,我们可以在启动类中注册依赖注入,如下所示:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton<ITasksRepository, TasksRepositoryFake>();
}
根据这个逻辑,我的TaskRepositoryFake
将注入控制器。到目前为止,一切都很清楚。我对此的疑问/困惑如下:
问题:
答案 0 :(得分:3)
首先回答您的问题:是的,您可以通过编程方式注入依赖项。通过使用工厂,就像基于运行时值注入依赖项一样。 AddSingleton有一个重载,它需要一个实现,所以你的用例的基本例子如下:
public class Startup
{
public bool IsTesting { get; }
public Startup(IHostingEnvironment env)
{
IsTesting = env.EnvironmentName == "Testing";
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISomeRepository>(sp => IsTesting ? (ISomeRepository)new SomeRepository() : (ISomeRepository) new FakesomeRepository());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, ISomeRepository someRepository)
{
app.UseIISPlatformHandler();
app.Run(async (context) =>
{
await context.Response.WriteAsync($"Hello World from {nameof(someRepository)}!");
});
}
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
您的TasksRepository的相关代码行如下所示:
services.AddSingleton<ITaskRepository>(sp => isTesting?(ITasksRepository)new TasksRepositoryFake(): (ITasksRespository)new TasksRepository() );
更好的方法是把它放在工厂里(再次以我的例子):
services.AddSingleton<ISomeRepository>(sp => SomeRepositoryFactory.CreatSomeRepository(IsTesting));
我希望您看到这有助于您根据配置,基于环境或根据需要设置它。 我感兴趣的是我通过抽象工厂here基于运行时值写了更多关于DI的信息。
话虽如此,通过单元测试,我只会将我的假货注入正在测试的类中。单元测试仍然向您自己和您的同事证明代码仍然按预期执行。 通过集成测试,我将使用我所有的假设制作一个特殊的StartUp类,并将其提供给测试主机,因为ASP.NET Core允许您这样做。您可以在此处详细了解测试主机:https://docs.asp.net/en/latest/testing/integration-testing.html
希望这有帮助。
更新为界面添加了强制转换,因为三元条件无法说明。另外还添加了一些基本样本。
答案 1 :(得分:2)
您可以注入依赖项配置,或基于环境,或两者兼而有之。
选项1:基于环境
public IHostingEnvironment env{ get; set; }
public Startup(IHostingEnvironment env)
{
this.env = env;
}
public void ConfigureServices(IServiceCollection services)
{
if (env.IsDevelopment())
{
// register other fake dependencies
services.AddSingleton<ITasksRepository, TasksRepositoryFake>();
}
else
{
// register other real dependencies
services.AddSingleton<ITasksRepository, TasksRepository>();
}
}
选项2:基于配置
public IConfigurationRoot Configuration { get; set; }
public Startup()
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
var isFakeMode= Configuration["ServiceRegistrationMode"] == "Fake";
if (isFakeMode)
{
// register other fake dependencies
services.AddSingleton<ITasksRepository, TasksRepositoryFake>();
}
else
{
// register other real dependencies
services.AddSingleton<ITasksRepository, TasksRepository>();
}
}
选项3:基于环境+基于配置
public IConfigurationRoot Configuration { get; set; }
public IHostingEnvironment env{ get; set; }
public Startup(IHostingEnvironment env)
{
this.env = env;
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
var isFakeMode = Configuration["ServiceRegistrationMode"] == "Fake";
if (env.IsDevelopment() && isFakeMode)
{
// register other fake dependencies
services.AddSingleton<ITasksRepository, TasksRepositoryFake>();
}
else
{
// register other real dependencies
services.AddSingleton<ITasksRepository, TasksRepository>();
}
}