使用BuildManager.GetReferencedAssemblies()的ASP.NET Web API单元测试Autofac模块

时间:2019-03-08 12:26:00

标签: unit-testing asp.net-web-api nunit autofac

正在使用Autofac作为我的IoC容器的ASP.NET Web API 2中的项目。该项目托管在IIS上,在我的Autofac模块中,我使用以下方法扫描程序集:

var asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();

为什么?

https://docs.autofac.org/en/latest/register/scanning.html#iis-hosted-web-applications

但是现在我们正在使用NUnit进行单元测试,在设置过程中,我注册了使用此方法的模块。现在,在运行测试时,我收到以下异常:

System.InvalidOperationException: 'This method cannot be called during the application's pre-start initialization phase.'

我理解为什么会有这个异常,但是我不知道如何使我的代码在测试和部署环境中工作。

NUnit的设置方法:

[TestFixture]
public abstract class ApplicationTestBase
{
    [SetUp]
    public override void Init()
    {
        var builder = new ContainerBuilder();

        // If the class requires auto mapper mapping, initialize them
        // We do this in order not to init them for every test => optimalisation!
        if (GetType().GetCustomAttributes<RequiresAutoMapperMappingsAttribute>(false) != null)
        {
            builder.RegisterModule<AutoMapperModule>();
        }

        this.Container = builder.Build();
    }
}

我是否需要为我的单元测试创​​建一个新的模块,或者还有其他方法吗?

AutoMapperTest

[RequiresAutoMapperMappings]
[TestFixture]
public class AutoMapperTests : ApplicationTestBase
{
    [Test]
    public void Assert_Valid_Mappings()
    {
        Mapper.AssertConfigurationIsValid();
    }
}

更新

就像西里尔提到的那样:为什么在单元测试中需要Ioc?我去搜索了,实际上您不必在测试中使用Ioc。因此,我放弃了Ioc,并通过以下方式初始化了我的映射器配置:

        Mapper.Initialize(configuration => 
        {
            var asm = AppDomain.CurrentDomain.GetAssemblies()
                        .Where(a => a.FullName.StartsWith("ProjectWebService."));

            configuration.AddProfiles(asm);
        });

1 个答案:

答案 0 :(得分:0)

我建议将“如何加载程序集”逻辑与“执行程序集扫描和注册模块逻辑”分开。

现在我想您可以通过一种方法来完成所有这些工作。

public IContainer BuildContainer()
{
  var asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
  var builder = new ContainerBuilder();
  builder.RegisterAssemblyTypes(asm);
  var container = builder.Build();
}

不完全是,但是类似的东西-内联程序集的加载是直接使用的。

将其分开,以便可以交换该逻辑以进行测试。例如,考虑允许有选择地传递参数,以便您可以覆盖测试中的逻辑。

public IContainer BuildContainer(Func<IEnumerable<Assembly>> assemblyLoader = null)
{
  IEnumerable<Assembly> asm = null;
  if (assemblyLoader != null)
  {
    asm = assemblyLoader();
  }
  else
  {
    asm = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
  }

  var builder = new ContainerBuilder();
  builder.RegisterAssemblyTypes(asm);
  var container = builder.Build();
}

您的默认逻辑将按照您想要的方式工作,但是在测试中,您可以交换其他内容。

var container = BuildContainer(() => AppDomain.GetAssemblies());

有很多方法可以进行这种交换。从可以在某处设置的静态属性到可以在某处覆盖的虚拟方法,它可以是任何东西。关键是,通过分离程序集加载逻辑,可以使测试时行为正常运行,但仍使用您要遵循的注册行为。