7

I'm using a PostSharp method attribute to do authorisation and auditing on my WCF service. It's working properly but now I'm trying to get my unit tests working with the attribute and am struggling to find a way to mock and inject the properties on the attribute.

My attribute is as below.

[Serializable]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class AuthoriseAndAuditAttribute : OnMethodBoundaryAspect
{
    private static ILog logger = AppState.logger;

    private static Ninject.IKernel _kernel = MyKernel.Kernel;

    private UserRoleTypesEnum _requiredRole = UserRoleTypesEnum.None;

    [Inject]
    public IServiceAuthToken _serviceAuthToken { get; set; }

    [Inject]
    public UserSessionDataLayer _userSessionDataLayer { get; set; }

    public AuthoriseAndAuditAttribute(UserRoleTypesEnum role = UserRoleTypesEnum.None)
    {
        _requiredRole = role;
        _kernel.Inject(this);
    }

    public override void OnEntry(MethodExecutionArgs args)
    {
        // Get the user's session from cookie.
        UserSession userSession = GetUserSession();

        // Check that user is in the required role.
        bool isAuthorised = (_requiredRole == UserRoleTypesEnum.None || (userSession != null && userSession.Roles.Contains(_requiredRole)));

        if (!isAuthorised)
        {
            logger.Warn("Not authorised for " + args.Method.Name + ".");
            throw new UnauthorizedAccessException();
        }
        else if (userSession != null)
        {
            Thread.CurrentPrincipal = new MyPrincipal(userSession);
        }
    }

    private UserSession GetUserSession()
    {
        if (_serviceAuthToken != null)
        {
            string sessionID = _serviceAuthToken.GetSessionID();

             if (!sessionID.IsNullOrBlank())
             {
                 return _userSessionDataLayer.GetForSessionID(sessionID);
             }
         }

         return null;
     }
}

I have a singleton class setting up the Ninject kernel:

public class MyKernel
{
    public static StandardKernel Kernel { get; set; }

    static MyKernel()
    {
        Kernel = new StandardKernel();
        Kernel.Bind<IServiceAuthToken>().To<ServiceAuthToken>();
        Kernel.Bind<UserSessionDataLayer>().To<UserSessionDataLayer>();
    }
}

In my WCF service I use the PostSharp attribute as below:

[AuthoriseAndAudit(UserRoleTypesEnum.Operator)]
public JSONResult<bool> IsAliveAuthorised()
{
   return new JSONResult<bool>() { Success = true, Result = true };
}

And in my unit test I'm using RhinoMocks to try and mock the two DI properties in the attribute.

 [TestMethod]
 public void IsAliveAuthorisedIsAuthorisedTest()
 {
     var mockServiceAuthToken = MockRepository.GenerateStrictMock<ServiceAuthToken>();
     mockServiceAuthToken.Stub(x => x.GetSessionID()).Return("x");
     var mockUserSessionDataLayer = MockRepository.GenerateStrictMock<UserSessionDataLayer>();
     mockUserSessionDataLayer.Stub(x => x.GetForSessionID(Arg<string>.Is.Anything)).Return(new UserSession());

     MyKernel.Kernel.Bind<ServiceAuthToken>().ToConstant(mockServiceAuthToken);
     MyKernel.Kernel.Bind<UserSessionDataLayer>().ToConstant(mockUserSessionDataLayer);

     var service = new MyService();
     Assert.IsTrue(service.IsAliveAuthorised().Result);
}

The issue I have is the mock objects in the unit test are never ending up being set as the properties on the attribute. What am I doing wrong or conversely is there a better way to do unit testing on a PostSharp attribute? Also bearing in mind I really want to minimise the use of the Ninject DI to the bare minimum.

sipsorcery
  • 30,273
  • 24
  • 104
  • 155
  • So what is assigned to the properties? The actual objects, or are they null? – Matthew Groves Jan 19 '12 at 04:53
  • 1
    No they are not null. The _kernel.Inject(this) call is what sets the two property objects. What I'm trying to do with my unit test is change the objects that the ninject kernel uses to set them with and that's the bit that's not working. – sipsorcery Jan 19 '12 at 05:05
  • Right, so you are expecting the Mock objects to be there, but instead it's the "real" objects that are still showing up? – Matthew Groves Jan 19 '12 at 05:07
  • 1
    Yep that's it, although I was "hoping" rather than "expecting" :). – sipsorcery Jan 19 '12 at 05:36
  • Heh, okay. I was able to reproduce this too, but I don't know if this is a Ninject issue or a PostSharp one (or both!). I'll keep working on it... – Matthew Groves Jan 19 '12 at 05:39
  • 1
    Is Ninject a critical part of your project or just used for unit testing purposes? – Dror Helper Jan 19 '12 at 07:24
  • Have you looked at http://autofixture.codeplex.com ? – Ruben Bartelink Jan 19 '12 at 08:57
  • No I hadn't come across autofixture but I don't think it will help in this case. I can create my mock objects ok, it's getting them into an attribute class that's the problem. – sipsorcery Jan 19 '12 at 10:27
  • Based on this documentation here - http://doc.sharpcrafters.com/postsharp-2.1/Default.aspx##PostSharp-2.1.chm/html/7480ca54-61c0-46c5-9914-60a58c3033e8.htm - I don't think Aspect constructors are being called at runtime. However, I moved the _kernel.Inject(this) into RuntimeInitialize, and it still doesn't work. We (Postsharp MVPs) are working on it, though! We're all very interested in figuring out how to do this too :) – Matthew Groves Jan 19 '12 at 20:19
  • @sipwiz, have you tried my answer below? That should work. – Kai G Feb 08 '12 at 01:53

1 Answers1

1

Instead of using the [Inject] attribute on your properties, redefine them like this:

    public IServiceAuthToken _serviceAuthToken { get { return _kernel.Get<IServiceAuthToken>(); } }

    public UserSessionDataLayer _userSessionDataLayer { get { return _kernel.Get<UserSessionDataLayer>(); } }

Also, in your test method you need to re-bind (note also that you were using the concrete type ServiceAuthToken in the first bind instead of the interface IServiceAuthToken):

MyKernel.Kernel.Rebind<IServiceAuthToken>().ToConstant(mockServiceAuthToken);
MyKernel.Kernel.Rebind<UserSessionDataLayer>().ToConstant(mockUserSessionDataLayer);
Kai G
  • 3,371
  • 3
  • 26
  • 30
  • That's very close to what I ended up doing. The only difference being instead of calling the kernel.Get on each property I call kernel.Inject at the start of the PostSharp OnEntry method. And I also do tehe kernel.Rebind in my unit test initialisation. – sipsorcery Feb 08 '12 at 02:47