ASP.NET 5 单元测试中使用依赖注入

发表于:2016-2-04 10:25

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:不详    来源:51testing软件测试网采编

  AdImageServiceTest 中的代码:
  namespace CNBlogs.Ad.Application.Tests
  {
  public class AdTextServiceTest : BaseTest
  {
  private IAdTextService _adTextService;
  public AdTextServiceTest(ITestOutputHelper output)
  : base(output)
  {
  Bootstrapper.Startup.ConfigureMapper();
  IUnitOfWork unitOfWork = new UnitOfWork(dbContext);
  IMemcached memcached = new EnyimMemcached(null);
  _adTextService = new AdTextService(new AdTextRepository(dbContext),
  new AdTextCreativeRepository(dbContext),
  new UserService(memcached),
  unitOfWork,
  memcached);
  }
  [Fact]
  public async Task GetByAdTextsTest()
  {
  var adTexts = await _adTextService.GetAdTexts();
  Assert.NotNull(adTexts);
  adTexts.ForEach(x => Console.WriteLine($"{x.Title}({x.Link})"));
  }
  }
  }
  AdImageServiceTest 构造函数中的代码,是不是看起来很别扭呢?我当时这样写,也是没有办法的,因为依赖注入的配置是写在 Startup 中,比如下面代码:
  namespace CNBlogs.Ad.WebApi
  {
  public class Startup
  {
  public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
  {
  // Set up configuration sources.
  var builder = new ConfigurationBuilder()
  .AddJsonFile("appsettings.json")
  .AddEnvironmentVariables();
  Configuration = builder.Build();
  }
  public IConfigurationRoot Configuration { get; set; }
  // This method gets called by the runtime. Use this method to add services to the container.
  public void ConfigureServices(IServiceCollection services)
  {
  // Add framework services.
  services.AddMvc();
  services.AddSingleton<IUnitOfWork, UnitOfWork>();
  services.AddScoped<IDbContext, EFDbContext>();
  services.AddSingleton<IAdTextRepository, AdTextRepository>();
  services.AddSingleton<IAdTextService, AdTextService>();
  }
  }
  }
  这样设计导致的结果是,针对类库项目的单元测试,就没办法使用依赖注入获取对象了,我后来想使用针对 WebApi 单元测试的方式,来对类库进行单元测试,比如用 TestServer 来启动,但类库中没有办法获取所注入的对象,构成函数注入会报错,[FromServices] 属性注入是 MVC Controller 中的东西,并不支持,所以针对类库的单元测试,和 WebApi 的单元测试并不是一样。
  IServiceCollection 的程序包是 Microsoft.Extensions.DependencyInjection.Abstractions,我原来以为它和 ASP.NET 5 Web 应用程序相关,其实它们也没啥关系,你可以脱离 ASP.NET 5 Web 应用程序,独立使用它,比如在类库的单元测试中,但如果这样设计使用,我们首先需要做一个工作,把 Startup.cs 中的 ConfigureServices 配置,独立出来,比如放在 CNBlogs.Ad.Bootstrapper 中,这样 Web 应用程序和单元测试项目,都可以使用它,减少代码的重复,比如我们可以进行下面设计:
  namespace CNBlogs.Ad.Bootstrapper
  {
  public static class Startup
  {
  public static void Configure(this IServiceCollection services, string connectionString)
  {
  services.AddEntityFramework()
  .AddSqlServer()
  .AddDbContext<EFDbContext>(options => options.UseSqlServer(connectionString));
  services.AddEnyimMemcached();
  ConfigureMapper();
  services.AddSingleton<IUnitOfWork, UnitOfWork>();
  services.AddScoped<IDbContext, EFDbContext>();
  services.AddSingleton<IAdTextRepository, AdTextRepository>();
  services.AddSingleton<IAdTextService, AdTextService>();
  }
  public static void ConfigureMapper()
  {
  Mapper.CreateMap<CNBlogs.Ad.Domain.Entities.AdText, AdTextDTO>();
  }
  }
  }
  ASP.NET 5 WebApi 项目中的 Startup.cs 配置会非常简单,只需要下面代码:
  public void ConfigureServices(IServiceCollection services)
  {
  services.Configure(Configuration["data:ConnectionString"]);//add using CNBlogs.Ad.Bootstrapper;
  }
  好了,做好上面工作后,单元测试中使用依赖注入就非常简单了,为了减少重复代码,我们可以先抽离出一个 BaseTest:
  namespace CNBlogs.Ad.BaseTests
  {
  public class BaseTest
  {
  protected readonly ITestOutputHelper output;
  protected IServiceProvider provider;
  public BaseTest(ITestOutputHelper output)
  {
  var connectionString = "";
  var services = new ServiceCollection();
  this.output = output;
  services.Configure(connectionString);////add using CNBlogs.Ad.Bootstrapper;
  provider = services.BuildServiceProvider();
  }
  }
  }
  可以看到,我们并没有使用 TestServer,而是手动创建一个 ServiceCollection,它有点类似于我们之前写依赖注入的 Unity Container,不过它们有很大不同,ServiceCollection 的功能更加强大,从 Bootstrapper.Startup 中可以看到,它可以注入 EF、MVC、Memcache 等等服务对象,BuildServiceProvider 的作用就是获取注入的服务对象,我们下面会用到:
  namespace CNBlogs.Ad.Repository.Tests
  {
  public class AdTextServiceTest : BaseTest
  {
  private IAdTextService _adTextService;
  public AdTextServiceTest(ITestOutputHelper output)
  : base(output)
  {
  _adTextService = provider.GetService<IAdTextService>();
  }
  [Fact]
  public async Task GetByAdTextsTest()
  {
  var adTexts = await _adTextService.GetAdTexts();
  Assert.NotNull(adTexts);
  adTexts.ForEach(x => Console.WriteLine($"{x.Title}({x.Link})"));
  }
  }
  }
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号