struts-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Miguel Almeida <mig...@almeida.at>
Subject [Idea] Add ability for decorator pattern in Actions
Date Fri, 12 Jul 2013 09:42:14 GMT
I'd like to discuss with you an idea that came up a while ago while
trying to fulfil this need:

As a developer
I want to unit test annotations in my actions without having to run the
method's body
So that I can TDD more effectively and have quicker unit tests.

Namely, imagine an action: 

@SecuredRoles("admin")
public String adminStuff(){
...
}

It would be interesting to have a way to test the @SecuredRoles without
having to run the method's body.

Upon discussion within the TDD group, a nice solution was suggested (in
italic. Skip below for my comments regarding Struts) :

So I would test and implement each aspect in isolation of the other -
the authorization would be implemented as a decorator (a-la GoF
decorator pattern) adding authorization to the underlaying (decorated)
MVC app. BTW I got to this technique by TDDing the separate aspects
(this is a good example of where the tests drive the design).

The technique to getting there is quite simple:
1. Extract an interface from your main class with all the public methods

2. Implement a decorator which adds authorization rules to a decorated
underlaying object. The decorator can implement the authorization rules
using your annotations (see this is where the academic meets the
practical - you'll in fact be reusing the already existing interceptor
but adapting it to a test-driven architecture)

3. in your tests, test the decorator providing a mock underlaying
decorated object, asserting in each test that given a request with a
user that has certain roles the underlaying method should or should not
be called.

The code might end up like this (semi-pseudo code but you get the idea)


Tests:

test_admin_can_call_method_a()
{
// setup a fake request with admin role:
httpRequest = buildRequestWithRole("admin");

// setup a mock app decorated with an authorzation decorator:
MockApp app = new MockApp();
AuthorizationDecorator authorizer = new AuthorizationDecorator(app);

// act - try calling method A in the decorator:
authorizer.MethodA(httpRequest);

// assert - underlaying method a should have been called:
Assert(app.MethodA.WasCalled==true);
}

test_regularUser_cannot_call_method_a()
{
// setup a fake request with regular user role:
httpRequest = buildRequestWithRole("regular user");

// setup a mock app decorated with an authorzation decorator:
MockApp app = new MockApp();
AuthorizationDecorator authorizer = new AuthorizationDecorator(app);

// act - try calling method A in the decorator:
authorizer.MethodA(httpRequest);

// assert - underlaying method a should not have been called:
Assert(app.MethodA.WasCalled==false);
}



In the SUT:

interface IApp
{
void MethodA()
void MethodB()
...
}

// this is the real app implementing methodA, methodB etc
class RealApp : IApp
{
void MethodA()
void MethodB()
...
}

// this is responsible for authorization
class AuthorizingDecorator : IApp
{
private IApp _decorated;
public AuthorizationDecorator(IApp decorated)
{
_decorated = decorated;
}

// each method is implemented using annotations
// and calling the underlaying decorated object
@SecuredRoles("admin, manager")
public void MethodA()
{
_decorated.MethodA();
}



@SecuredRoles("regular user")
public void MethodB()
{
_decorated.MethodB();
}
}

Then in your final MVC application you'd wire up the decorator with the
RealApp object:

IApp app = new AuthorizingDecorator(new RealApp());



This design is incompatible with Struts2 - if you decorate
DecoratedAction with OriginalAction, all Struts-related things (e.g.
ParamsInterceptor populating Action's fields from request, and I assume
every other interceptor's behaviour as well) will no work on
DecoratedAction, because it doesn't extend OriginalAction but rather
delegates to OriginalAction. E.g, a "someRequestParam" would be
populated OriginalAction if it was in the request, but it won't populate
originalAction.someRequestParam in DecoratedAction.

It would be very interesting to support the Decorator Pattern in Struts
actions. What are your thoughts on it?


Miguel Almeida


Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message