0
votes

I am using PostSharp MethodInteceptionAspect in my ASP.NET MVC project. It works fine but I want to test my aspects in unit tests. When I add an aspect to any method in unit test it compiles ok but it fails at runtime with this message:

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

The compilation applies the postsharp aspect (I have checked the compiled MSIL) but in runtime I can't create instances of classes that has intercepted methods.

This happens only when running the tests. Intercepting in ASP.NET MVC project is fine.

Example of the unit test that uses PostSharp aspect:

[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
    }
}

BTW: I am using VS Unit Testing Framework.


EDIT:

I found out that when I remove a private property in the aspect it works.

The aspect looked like this:

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
    }
}

When i remove the IDatabaseSessionProvider it works fine as expected.

Can anyone explain why this is necessary? I don't undestand why it works in the web project but not in the unit test project.

1
As it's a database session scope attribute are you missing a config setting in your unit test project? For example a database connection string?Kevin Holditch
No, when I remove the aspect and create a database session in the method itself it works. It fails this way even if I don't call the method just because the class contains an aspect.Martin Volek

1 Answers

1
votes

It seems like that RuntimeInitialize is called before the IoC container is initialized. The easiest solution could be to lazy initialize databaseSessionProvider. In addition to that databaseSessionProvider field should be marked as NonSerializable.

EDIT: Example fixed according to Martin's objection

[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>();
    }
}