IOC容器Autofac的另类使用
最近在公司项目中使用了Autofac,本人需要解决的问题比较特别,就是需要在单元测试时候替换实际处理的类,通过模拟数据库操作完成单元测试。
本人是个懒惰的程序员,因为不想修改autofac的配置文件,又要结合fakes进行模拟,在网上又未发现类似处理方法,所以特把自己的做法共享出来,供大家参考和改进。
因为实际代码为公司项目,未能尽列代码请见谅,同时认为应该还有更好的改进方案,请有更好想法的朋友分享。
部分参考代码
------------------------------------------------
DAO管理器:
/// <summary> /// Dao 管理器 /// </summary> public class DaoBag { #region 防实例静态化 /// <summary> /// 获取实例 /// </summary> public static DaoBag Instance { get { return Nested.instance; } } /// <summary> /// 密封的内部嵌套辅助类,辅助创建单例对象 /// </summary> sealed class Nested { // 静态钩子 static Nested() { } /// <summary> /// 只读类型 /// </summary> internal static readonly DaoBag instance = new DaoBag(); } // 防止直接实例化 private DaoBag() { builder.RegisterModule(new ConfigurationSettingsReader("autofac")); } #endregion ContainerBuilder builder = new ContainerBuilder(); IContainer container; public T GetDao<T>() { // 延迟加载,第一次使用时候被调用 if (container == null) container = builder.Build(); return container.Resolve<T>(); } public void SetDao<T,I>(T t) where T : class { builder.RegisterInstance<T>(t).As<I>(); } }
Config配置(无论单元测试还是实际项目中,配置文件不变,这也是我非常需要偷懒的地方):
<configuration> <configSections> <section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/> </configSections> <autofac defaultAssembly="Model"> <components> <component type="Model.Help.HelpNewsRepository, Model" service="Model.Help.IHelpNewsRepository" /> <component type="Model.Pri.PriUserRepository, Model" service="Model.Pri.IPriUserRepository" /> </components> </autofac> </configuration>
最终完成后实现的效果如下
------------------------------------------------------
业务类中使用方法:
private IHelpNewsRepository hnr = DaoBag.Instance.GetDao<IHelpNewsRepository>();
单元测试中替换方法(注意使用了Fakes,单元测试中的替换将使配置失效,其实不是单元测试和实际项目使用相同配置,应该说是单元测试不用配置):
// 准备测试数据 var news_id = 1; var news_viewNum = 2; // 准备伪造数据处理类 StubIHelpNewsRepository stubhnr = new StubIHelpNewsRepository(); StubIPriUserRepository stubpur = new StubIPriUserRepository(); // 模拟底层数据处理方法 stubhnr.GetEntityInt64 = (x) => { return new HelpNews { Title = "新闻测试", Id = 1, ViewNum = 1 }; }; stubpur.GetEntityInt64 = (x) => { return new PriUser { Id = 1, Username = "admin" }; }; // 为IOC容器设置新的Dao (注意:必须设置完所有相关Dao映射,才可以进行其他步骤) DaoBag.Instance.SetDao<StubIHelpNewsRepository, IHelpNewsRepository>(stubhnr); DaoBag.Instance.SetDao<StubIPriUserRepository, IPriUserRepository>(stubpur); //测试第一个业务类 var newbus = NewsBus.Instance; var result = newbus.GetNews(news_id).AppendData as HelpNews; Assert.AreEqual(news_id, result.Id); Assert.AreEqual("新闻测试", result.Title); Assert.AreEqual(news_viewNum, result.ViewNum); //测试第二个业务类 var userbus = UserBus.Instance; var user = userbus.GetUser(1).AppendData as PriUser; Assert.AreEqual(user.Username, "admin");