在单元测试中使用 PostSharp

Using PostSharp in unit tests

我在 ASP.NET MVC 项目中使用 PostSharp MethodInteceptionAspect。它工作正常,但我想在单元测试中测试我的方面。当我在单元测试中向任何方法添加一个方面时,它编译正常但在运行时失败并显示以下消息:

Unable to create instance of class PNAF.Tests.Auth.DatabaseSessionScopeAspectTests. Error: System.TypeInitializationException: The type initializer for 'PostSharp.ImplementationDetails_e613b708.<>z__a_1' threw an exception. ---> System.NullReferenceException: Object reference not set to an instance of an object..
    at PNAF.Core.IoC.ServiceLocator.Resolve() in ServiceLocator.cs: line 17
   at PNAF.Modules.Auth.Aspects.DatabaseSessionScopeAttribute.RuntimeInitialize(MethodBase method) in DatabaseSessionScopeAttribute.cs: line 24
   at PostSharp.ImplementationDetails_e613b708.<>z__a_1..cctor() in :line 0
 --- End of inner exception stack trace ---
    at PostSharp.ImplementationDetails_e613b708.<>z__a_1.Initialize()
   at PNAF.Tests.Auth.DatabaseSessionScopeAspectTests..cctor() in :line 0

编译应用了 postsharp 方面(我已经检查了编译的 MSIL)但是在运行时我无法创建具有拦截方法的 类 实例。

这仅在 运行 测试时发生。在 ASP.NET MVC 项目中拦截没问题。

使用 PostSharp 方面的单元测试示例:

[TestClass]
public class DatabaseSessionScopeAspectTests : TestBase
{
    [TestMethod]
    public void DataSessionScopeTest()
    {
        var data = GetData();
        Assert.IsNotNull(data);
    }

    [DatabaseSessionScope] // this is PostSharp MethodInterceptionAspect
    private IList<User> GetData()
    {
        // some code
    }
}

顺便说一句:我正在使用 VS 单元测试框架。


编辑:

我发现当我在方面删除私有 属性 时,它会起作用。

外观是这样的:

public class DatabaseSessionScopeAttribute : MethodInterceptionAspect
{
    private IDatabaseSessionProvider databaseSessionProvider;

    public override void RuntimeInitialize(MethodBase method)
    {
        base.RuntimeInitialize(method);
        databaseSessionProvider = ServiceLocator.Resolve<IDatabaseSessionProvider>();
    }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        // some code
    }
}

当我删除 IDatabaseSessionProvider 时,它按预期正常工作。

谁能解释为什么这是必要的?我不明白为什么它在 web 项目中有效,但在单元测试项目中无效。

似乎在初始化 IoC 容器之前调用了 RuntimeInitialize。最简单的解决方案可能是延迟初始化 databaseSessionProvider。除了该 databaseSessionProvider 字段应标记为 NonSerializable.

编辑:根据 Martin 的反对修正的示例

[Serializable]
public class DatabaseSessionScopeAttribute : MethodInterceptionAspect
{
    [NonSerialized]
    private Lazy<IDatabaseSessionProvider> databaseSessionProvider;

    public override void RuntimeInitialize(MethodBase method)
    {
        base.RuntimeInitialize(method);
        databaseSessionProvider = 
            new Lazy<IDatabaseSessionProvider>(SomeSessionProviderFactoryMethod);
    }

    private static IDatabaseSessionProvider SomeSessionProviderFactoryMethod()
    {
        return ServiceLocator.Resolve<IDatabaseSessionProvider>();
    }
}