1

I'm using ThinkTecture's resource based authorization in my WebApi.

I'm trying to test one of my controller that I needed to check the access inside the function. But now, I can't test the function anymore since, I can't mock an extension method and since it's a nuget method, I can't modify the class to inject another value.

My controller look like this:

public class AlbumController : ApiController
{
    public async Task<IHttpActionResult> Get(int id)
    {
        if (!(await Request.CheckAccessAsync(ChinookResources.AlbumActions.View, 
                                            ChinookResources.Album,
                                            id.ToString())))
        {
            return this.AccessDenied();
        }

        return Ok();
    }
}    

And the ResourceAuthorizationManager is setted into the startup like this:

app.UseResourceAuthorization(new ChinookAuthorization());    

Source code of the ThinkTecture project is here.

Thank you for your help

3 Answers3

1

You could always wrap this static call into some abstraction of yours:

public interface IAuthorizationService
{
    Task<bool> CheckAccessAsync(string view, string album, string id);
}

and then have some implementation that will delegate the call to the static extension method. But now since you will be working with the IAuthorizationService you can freely mock the CheckAccessAsync method in your unit tests.

As far as testing the implementation of this abstraction is concerned, you probably don't need it as it only acts as a bridge to the ThinkTecture's classes which should already be pretty well tested.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • And after mocking IAuthorizationService, where am I putting it ? I don't see any authorizationSerivce or any thing like that to set it to use the mocked checkAccessAsync. My authorizationManager is setted in the owin context in my api so I don't know how I'm supose to set it. Also, I'm not trying to test the implementation of it, I'm trying to unit test the controller. – Tristan Legault-B Jan 28 '15 at 16:18
  • You will inject into the constructor of your controller the `IAuthorizationService` interface. You will configure your DI framework to inject the proper implementation that will delegate the call to the ThinkTecture static method. And then in your controller action you will simply invoke the method on the abstraction: `if (!(await this.authService.CheckAccessAsync(ChinookResources.AlbumActions.View, ChinookResources.Album, id.ToString())))`. Now you can easily unit test your API controller in isolation as it depends only on `IAuthorizationService` - there are no longer static method calls. – Darin Dimitrov Jan 28 '15 at 21:21
1

I finally solved my problem. The real problem was that the CheckAccess method was an extension. (for my answer, every class will refer to the sample that can be find here)

To stop using the extension method, I added these methods into my chinookAuthorization

   public Task<bool> CheckAccessAsync(ClaimsPrincipal user, string action, params string[] resources)
    {
        var ctx = new ResourceAuthorizationContext(user ?? Principal.Anonymous, action, resources);

        return CheckAccessAsync(ctx);
    }

    public Task<bool> CheckAccessAsync(ClaimsPrincipal user, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
    {
        var authorizationContext = new ResourceAuthorizationContext(
            user ?? Principal.Anonymous,
            actions,
            resources);

        return CheckAccessAsync(authorizationContext);
    }    

Then I changed my controller to have an instance of the chinookAuthorization

public class AlbumController : ApiController
{
    protected readonly chinookAuthorization chinookAuth;

    public BaseApiController(chinookAuthorization chinookAuth)
    {
        if (chinookAuth == null)
            throw new ArgumentNullException("chinookAuth");

        this.chinookAuth = chinookAuth;
    }
    public async Task<IHttpActionResult> Get(int id)
    {
        if (!(await chinookAuth.CheckAccessAsync((ClaimsPrincipal)RequestContext.Principal, ChinookResources.AlbumActions.View, 
                                        ChinookResources.Album,
                                        id.ToString())))
        {
            return this.AccessDenied();
        }

        return Ok();
    }
}

And I'm still declaring my ChinookAuthorization into my owin startup, to keep using the same pattern for my attribute check access call.

So now, I just have to mock the chinookAuthorization, mock the response of the call to return true, and that's it!

1

The ResourceAuthorizationAttribute uses Reqest.CheckAccess so I don't think it is a good solution to abstract away the implementation and then injecting it into the controller since in theory, the ResourceAuthorizationAttribute and the created service could use different implementations of the CheckAccess method.

I took a simpler approach by creating a BaseController

public class BaseController : ApiController
{
        public virtual Task<bool> CheckAccessAsync(string action, params string[] resources)
        {
            return Request.CheckAccessAsync(action, resources);
        }
}

and making CheckAccessAsync virtual so I can mock it (by for example Moq).

then from my controller

public class AlbumController : BaseController
{
    public async Task<IHttpActionResult> Get(int id)
    {
        if (!(await CheckAccessAsync(ChinookResources.AlbumActions.View, 
                                            ChinookResources.Album,
                                            id.ToString())))
        {
            return this.AccessDenied();
        }

        return Ok();
    }
}

Unit testing the controller then is as easy as:

[TestClass]
public class TestClass
{
    Mock<AlbumController> mockedTarget
    AlbumController target

    [TestInitialize]
    public void Init()
    {
        mockedTarget = new Mock<AlbumController>();
        target = mockedTarget.Object;
    }

    [Test]
    public void Test()
    {
        mockedTarget.Setup(x => x.CheckAccessAsync(It.IsAny<string>(),
            It.IsAny<string[]>()))
            .Returns(Task.FromResult(true));

        var result = target.Get(1);

        // Assert
    }

}
olif
  • 3,221
  • 2
  • 25
  • 23