4

I'm trying to use PostSharp to implement a security aspect in order to apply method level authorisation checks in my repository layer.

The concept is outlined here.

However these authorisation checks are getting in the way during unit testing, making them more into integration tests.

What would be the best way to isolate these as unit tests, essentially ignoring/mocking the security aspect so that I can just test the actual class behaviour without the need to initialise a bunch of security information?

Does AOP inherently conflict with unit testing?

Brett Postin
  • 11,215
  • 10
  • 60
  • 95
  • 1
    The [official PostSharp document that Gael linked to](http://doc.postsharp.net/postsharp-3.0/Default.aspx##PostSharp-3.0.chm/html/2ad6cf92-08eb-4537-a434-d88a3e493721.htm) actually answers your second question. The documentation states: "testing techniques for aspects differ from testing techniques for normal class libraries". In other words: yes, PostSharp conflicts with unit testing. Note that AOP by itself does **not** conflict with unit testing, but compile time weaving does. AOP techniques such as interception and decoration do not conflict with unit testing. – Steven Jun 12 '13 at 10:56
  • @Steven I agree. Now with a better understanding of PostSharp and AOP in general I can see it is the former's method of AOP that requires a different approach to unit testing. – Brett Postin Jun 12 '13 at 11:12
  • PostSharp requires a different approach of unit testing (since it does compile time weaving), but AOP by itself does not (since AOP does not dictate if aspects are added compile time or runtime). – Steven Jun 12 '13 at 11:23

3 Answers3

4

To answer your 2nd question first, no, AOP doesn’t inherently conflict with unit testing. Usually I’d say it’s best to unit test the methods and the aspects separately.

In your case, there are several options.

The easiest is simply to have the unit test setup method make sure the thread has the required permissions.

If you don’t want to do that, there are two ways you could separate things out for your unit test. The first is to extract all the code from the methods that you are applying security aspects to into separate methods like this:

[SecurityAspect]
void DoSomething()
{
    DoSomethingInternal();
}

void DoSomethingInternal()
{
    // this is the real code
}

Then you can run your unit tests against all the unsecured ‘internal’ methods – which tests the logic in them without worrying about security.

The second approach would be to inject a mock permissions tester into the aspect itself. To be able to do this, you would have to define a separate class and interface that does the actual logic of testing the security, something like this (assuming it’s a Thread that you pass in to verify the security):

public interface IPermissionsChecker
{
    bool HasPermissions(Thread thread);
}

This is your permissions checker for your live system:

public class RealPermissionsChecker : IPermissionsChecker
{

    public bool HasPermissions(Thread thread)
    {
        // do your real work here
    }
}

And this is the one you’ll use in your unit tests

public class MockPermissionsChecker : IPermissionsChecker
{
    public bool HasPermissions(Thread thread)
    {
        return true;
    }
}

Now you need to define your aspect something like this:

public class SecurityChecker : OnMethodBoundaryAspect
{

    IPermissionsChecker _checker;

    public override void OnEntry(MethodExecutionArgs args) 
    { 
        if (!_checker.HasPermissions(Thread.CurrentThread))
            throw new SecurityException("No permissions");
    }
}

The only remaining issue is the need to inject the correct permissions checker into the aspect.

The slightly hacky way I've done this before is to make _checker a static field, and provide a static method to initialize it:

public class SecurityChecker : OnMethodBoundaryAspect
{

    private static IPermissionsChecker _checker;

    public static void InjectChecker(IPermissionsChecker checker)
    {
        // best put some code here to make sure this is only called once,
        // as well as doing thread synchronization
        if (_checker == null)
            _checker = checker;
    }

The fact that InjectChecker is static means you can access it from your app startup (or unit test startup) code. I suspect unit test purists would frown on this - and you do have to make sure you do call it at app startup, but I think it is the simplest way to inject the checker into the aspect, circumventing the fact that the rest of your code can't directly access instances of the aspect.

The more complicated alternative is to override RunTimeInitialize() in your aspect - this method is called by PostSharp when the aspect is initialized. You'd probably do something like this:

    public override void RuntimeInitialize(MethodBase method)
    {
        base.RuntimeInitialize();
        this._checker =PermissionsCheckerProvider.Current.GetChecker();
    }

You'll see that requires you to define another class:

public class PermissionsCheckerProvider
{
    // make sure you set this at app startup, either to the mock or to the real checker
    public static PermissionsCheckerProvider Current { get; set;}

    public IPermissionsChecker GetChecker()
    {
    }
}

This approach guarantees that the method will attempt its initialization at the right time, but then you have the problem of making sure you have supplied an appropriate current provider before the aspect attempts to initialize. So I personally would probably go for the first approach to keep things simple.

There's some discussion about dependency injection and RuntimeInitialize here. https://codereview.stackexchange.com/questions/20341/inject-dependency-into-postsharp-aspect

Community
  • 1
  • 1
  • I like your second solution of being able to inject the permissions checker, I'll follow that up. In the meantime if you do get chance to fill in the remaining issue I'd be extremely grateful. – Brett Postin Jun 07 '13 at 17:23
  • OK I've now added more information about how to inject the permissions checker. Hope that helps – Simon Robinson Jun 07 '13 at 18:07
2

Two links that extensively answer your question:

Gael Fraiteur
  • 6,759
  • 2
  • 24
  • 31
0

If you are using Typemock in your unit tests you can use something like

MyAspect myAspectMock = Isolate.Fake.Instance<MyAspect>(Members.MustSpecifyReturnValues);
Isolate.Swap.AllInstances<MyAspect>().With(myAspectMock);

This allows you to control what tests the aspects are used on, and which ones are not, allowing you to test the method itself, and with the advices applied.

Presumably there would be a similar mechanism with other mocking frameworks

sweetfa
  • 5,457
  • 2
  • 48
  • 62