註冊物件
UnitTestContainerFactory.InitExternalRegistrarInstance(builder =>
{
builder.RegisterInstance(mockDealerApplyRepository.Object)
.As<IDealerApplyRepository>()
.SingleInstance();
這個是把IDealerApplyRepository介面註冊時使用 mockDealerApplyRepository 這個已經被new出來的物件實體,然後最後面的SingleInstance指的是每個實作都用同一個物件
builder.RegisterType<MockDealerApplyRepository>()
.As<IDealerApplyRepository>()
.InstancePerLifetimeScope();
這邊的IDealerApplyRepository介面註冊則是使用MockDealerApplyRepository類別,就是使用class宣告
});
另一種使用
如果同一介面需要兩個實作註冊,可以使用
builder
.RegisterType<CreateDealerTask>()
.Keyed<IDealerTask>(DealerTaskActionEnum.Create)
.InstancePerLifetimeScope();
builder
.RegisterType<UpdateDealerTask>()
.Keyed<IDealerTask>(DealerTaskActionEnum.Update)
.InstancePerLifetimeScope();
一樣都是IDealerTask介面,不過CreateDealerTask、UpdateDealerTask都可以用
但是這樣在使用時必須宣告清楚
IDealerTask task = this.ResolveKeyed<IDealerTask>(DealerTaskActionEnum.Create);
/// <summary>
/// Senao.Admin 單元測試 DI 容器工廠
/// </summary>
public class UnitTestContainerFactory : UnitTestContainerFactoryBase<UnitTestContainerFactory>
{
/// <summary>
/// 建構子
/// </summary>
protected UnitTestContainerFactory()
{
}
/// <summary>
/// 建構子,支援額外類型註冊
/// </summary>
/// <param name="externalTypeRegister">除了底層共同註冊之外,不同的 TestClass 額外註冊的類別委派</param>
protected UnitTestContainerFactory(Action<ContainerBuilder> externalTypeRegister) : base(externalTypeRegister)
{
}
/// <summary>
/// 於子類實作之註冊類型之設定
/// </summary>
/// <param name="builder">
/// Used to build an <see cref="T:Autofac.IContainer"/> from component registrations.
/// </param>
protected override void RegisterTypes(ContainerBuilder builder)
{
// 此處載入該 solution 引用之 Helper、Repository & Service 專案
IEnumerable<Assembly> assemblies = Assembly.GetExecutingAssembly()
.GetReferencedAssemblies()
.Select(Assembly.Load)
.Concat(new[]
{
Assembly.Load("Clam.Common.Helper"),
Assembly.Load("Clam.Common.Repository"),
Assembly.Load("Clam.Common.Service"),
Assembly.Load("Clam.Common.UnitTest"), Assembly.Load("Clam.Dealer.Channel.Helper"),
Assembly.Load("Clam.Dealer.Channel.Repository"),
Assembly.Load("Clam.Dealer.Channel.Service")
});
// 取得 ITypeRegistrar
IOrderedEnumerable<ITypeRegistrar> registrars = assemblies
.SelectMany(p => p.ExportedTypes.Where(s => s.IsAssignableTo<ITypeRegistrar>() && !s.IsInterface))
.Select(p => (ITypeRegistrar)Activator.CreateInstance(p))
.OrderBy(p => p.Order);
// 對 Helper、Repository & Service 進行分別註冊
foreach (ITypeRegistrar registrar in registrars)
{
registrar.RegisterTypes(builder);
}
builder.RegisterCallback(p =>
{
foreach (IComponentRegistration item in p.Registrations)
{
item.LogActualRegistration();
}
});
// 以下進行共用 Mock 物件註冊
builder
.RegisterType<ConfigHelper>()
.As<IConfigHelper>()
.InstancePerLifetimeScope()
.LogExpectRegistration();
builder
.RegisterType<MockExecutionContext>()
.As<IExecutionContext>()
.InstancePerLifetimeScope()
.LogExpectRegistration();
builder
.RegisterType<MockUserContext>()
.As<IUserContext<UserInfo>>()
.InstancePerLifetimeScope()
.LogExpectRegistration();
builder
.RegisterType<MockUserContext>()
.As<IUserContext<IIdentity<string>>>()
.InstancePerLifetimeScope()
.LogExpectRegistration();
builder
.RegisterType<MockEmailService>()
.As<IEmailService>()
.InstancePerLifetimeScope()
.LogExpectRegistration();
builder
.RegisterType<NullLoggerFactory>()
.As<ILoggerFactory>()
.SingleInstance()
.LogExpectRegistration();
builder
.RegisterGeneric(typeof(Logger<>))
.As(typeof(ILogger<>))
.SingleInstance();
builder
.RegisterType<MockTaskProvider>()
.As<ITaskProvider>()
.InstancePerLifetimeScope()
.LogExpectRegistration();
}
}
/// <summary>
/// 單元測試專用之相依性注入容器抽象工廠
/// </summary>
/// <typeparam name="TContainerFactory">具體容器工廠</typeparam>
public abstract class UnitTestContainerFactoryBase<TContainerFactory>
where TContainerFactory : UnitTestContainerFactoryBase<TContainerFactory>
{
/// <summary>
/// 單一實例,延遲載入
/// </summary>
private static Lazy<TContainerFactory> Instance;
/// <summary>
/// 除了底層共同註冊之外,不同的 TestClass 額外註冊的類別委派
/// </summary>
private readonly Action<ContainerBuilder> ExternalTypeRegister;
/// <summary>
/// DI 容器實例
/// </summary>
private IContainer ContainerInstance;
/// <summary>
/// 建構子
/// </summary>
protected UnitTestContainerFactoryBase()
{
this.ExternalTypeRegister = null;
}
/// <summary>
/// 建構子,支援額外類型註冊
/// </summary>
/// <param name="externalTypeRegister">除了底層共同註冊之外,不同的 TestClass 額外註冊的類別委派</param>
protected UnitTestContainerFactoryBase(Action<ContainerBuilder> externalTypeRegister)
{
this.ExternalTypeRegister = externalTypeRegister;
}
/// <summary>
/// 取得 Container
/// </summary>
/// <returns>IContainer</returns>
public IContainer Container => this.ContainerInstance ?? (this.ContainerInstance = this.BuildContainer());
/// <summary>
/// 建立具備額外類型註冊的 UnitTestContainerBuiler 之單一實例。
/// <para>在真正呼叫 <see cref="GetInstance"/> 之前,允許重新設定。</para>
/// </summary>
/// <param name="externalTypeRegister">除了底層共同註冊之外,不同的 TestClass 額外註冊的類別委派</param>
public static void InitExternalRegistrarInstance(Action<ContainerBuilder> externalTypeRegister)
{
if (Instance != null && Instance.IsValueCreated)
{
return;
}
// 使用 Reflection 的方式維持繼承下的單例模式
Instance =
new Lazy<TContainerFactory>(() => (TContainerFactory)Activator.CreateInstance(
typeof(TContainerFactory),
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance,
null,
new object[] { externalTypeRegister },
CultureInfo.InvariantCulture));
}
/// <summary>
/// 取得 UnitTestContainerBuiler 的單一實例,
/// <para> 若測試類別未呼叫 <see cref="InitExternalRegistrarInstance"/> </para>
/// 則會取得無額外註冊類型的原始 UnitTestContainerBuiler
/// </summary>
/// <returns>UnitTestContainerBuiler</returns>
public static TContainerFactory GetInstance()
{
if (Instance != null)
{
return Instance.Value;
}
Instance = new Lazy<TContainerFactory>(() => (TContainerFactory)Activator.CreateInstance(
typeof(TContainerFactory),
true));
return Instance.Value;
}
/// <summary>
/// 重設 ContainerBuiler
/// </summary>
public static void RestContainerBuilder()
{
if (Instance == null || !Instance.IsValueCreated)
{
return;
}
Instance.Value.Container.Dispose();
Instance = null;
}
/// <summary>
/// 於子類實作之註冊類型之設定
/// </summary>
/// <param name="builder">Used to build an <see cref="T:Autofac.IContainer" /> from component registrations.</param>
protected abstract void RegisterTypes(ContainerBuilder builder);
/// <summary>
/// 建構容器
/// </summary>
/// <returns>IContainer</returns>
private IContainer BuildContainer()
{
ContainerBuilder builder = new ContainerBuilder();
this.RegisterTypes(builder);
// 註冊不同 test class 所定義之額外類別
this.ExternalTypeRegister?.Invoke(builder);
IContainer container = builder.Build();
return container;
}
}