【问题标题】:How to link Autofac to UnitTesting如何将 Autofac 链接到 UnitTesting
【发布时间】:2016-01-05 10:42:22
【问题描述】:

在我的 Web API 中,我将 Autofac 链接为 IoC 容器,我这样做:

域级别

public class Autofac
{
    protected ContainerBuilder Builder { get; set; }

    public Autofac()
    {
        this.Builder = new ContainerBuilder();
    }

    public virtual IContainer Register()
    {
        // Register dependencies

        SetUpRegistration(this.Builder);

        // Build registration.
        var container = this.Builder.Build();

        // End
        return container;
    }   

    private static void SetUpRegistration(ContainerBuilder builder)
    {
        // === DATALAYER === //

        // MyRepository
        builder.RegisterType<MyRepository>()
            .As<IMyRepository>()
            .InstancePerLifetimeScope();

        // === DOMAIN === //

        // MyManager
        builder.RegisterType<MyManager>()
            .As<IMyManager>()
            .InstancePerLifetimeScope();
    }
}

网络 API

public class Autofac : Domain.IoC.Autofac
{
    public IContainer Register(HttpConfiguration config)
    {
        // Register your Web API controllers.
        base.Builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        // OPTIONAL: Register the Autofac filter provider.
        base.Builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);

        // Complete registration and get container instance.
        var container = base.Register();

        // Set the dependency resolver to be Autofac.
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        // Done.
        return container;
    }
}

如您所见,它继承自 Domain 的基类并设置 Web API 特定配置。

用法

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(WebApiConfig.Register);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    new IoC.Autofac().Register(GlobalConfiguration.Configuration);
}

如您所知,它位于 global.asax。

问题

这适用于 Web API,但我不知道我需要做什么才能在 UnitTest 项目上下文中注册所有这些。

我的想法是,我将在 Web API 级别创建与 Autofac 类类似的实现,但不是使用模拟(完全忽略来自 Domain 的基类)。

任何指针?

【问题讨论】:

  • 就我个人而言,我从来没有看到有必要(也不知道为什么它甚至可行)在单元测试中设置我的 IoC 容器。单元测试应该用于测试一段逻辑代码,该代码可以快速轻松地自行测试,而无需预先设置应用程序的其余部分。我认为您以错误的方式看待问题,应该单独测试每个部分而不是整体测试。
  • @StephenRoss:在理论上同意,但我需要测试依赖于IRepositories 的域管理器将访问数据库。我需要嘲笑那些,所以我实际上并没有全部删除......我认为这很难避免。
  • 在这种情况下,您应该只设置域管理器所需的依赖项(在使用模拟的单元测试中,正如您已经说过的)。请记住,您不是在测试域管理器是否与您的所有IRepositories 一起工作,而是在测试数据流是否会以IRepository 结束。然后对您的每个IRepositories 进行单独的测试,以确保它们按照应有的方式进行。我当然希望您的域管理员不依赖应用程序的其余部分。
  • 因为您已经使用界面分离了不同的存储库,这将使您可以轻松地测试您的域管理器。如果存储库本身继承自DBContext,那就有点困难了,另一方面,如果你将DBContext 作为类的依赖项,我通常将它包装在一个接口类中,这样你就可以简单地传递接口和类将简单地提供一个薄包装器,易于测试且相对简单。

标签: c# unit-testing dependency-injection inversion-of-control autofac


【解决方案1】:

就我个人而言,我从来没有看到直接在单元测试中设置我的 IoC 容器的必要性(而且我很难理解它的可行性或有用性)。 由于单元测试用于测试可以快速构建、轻松运行并且不需要太多(我不提倡)拆解的逻辑代码。它不应该要求您设置所有应用程序才能运行测试。

请记住,您的单元测试只是测试通过系统的数据流,即您的 DomainManager 实际上会在您期望的时候调用 IRepository。然后,您将为所有存储库设置单独的测试类,以确定它们是否会正确添加到数据库等中。

我不确定您如何使用 DBContext 类,但作为包装器的一个示例,这就是它的样子。

interface IDBSetWrapper
{
    object Add(object entity);
}

interface IDBContextWrapper
{
    ...
    IDBSet Set(Type entityType);
    ...
}

class DBContextWrapper : IDBContextWrapper
{
    private readonly DBContext context;

    public DBContextWrapper()
    {
        context = new DBContext();
    }

    ...

    public IDBSet Set(Type entityType)
    {
        var dbSet = context.Set(entityType);
        return new DBSetWrapper(dbSet);
    }

    ...
}

这并不多,但我希望它能说明我对薄包装器的意思。基本上,包装器是 DBContext 并将在类中包含它的一个实例,当您请求包装器执行任何操作时,将调用实际的 DBContext。 我已经展示了返回另一个对象(在本例中为 DBSet)时会发生什么,这也将被包装在一个带有接口的单独对象中。这样你就可以轻松地模拟这个类的返回。

您现在可以更好地将这个新包装器添加到您的 IoC 中,因为它提供了一个接口。

需要注意的一点是,您将无法也可能不希望测试包装类,我认为这没什么意义。但之前我见过同事对这类类进行集成测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    • 2016-06-16
    • 2012-10-23
    • 2019-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多