Extending ATG components in a testable fashion
Sometimes there is a need to enhance an existing form handler. For example, let's say we have a following form handler supplied by ATG:
class NativeFormHandler extends FormHandler { public boolean handleSomething(request, response) { ... }; }
Component properties file for /atg/native/NativeFormHandler will be something like this:
$class = atg.native.NativeFormHandler $scope = request ...dependencies...
Then, we want to perform some actions on each handler invocation, in the beginning of the method - for example, cancelling handling if some condition fails.
The first instinct would be to extend the NativeFormHandler like this:
class CustomFormHandler extends NativeFormHandler { public void handleSomething(request, response) { if (conditionOk) { return super.handleSomething(request, response); } else { addFormException(...); return checkFormRedirect(request, response); } } }
And then to override $class in component properties (by overriding /atg/native/NativeFormHandler.properties):
$class = com.company.CustomFormHandler
And this is WRONG. By extending the NativeFormHandler we basically strip us out of ability to test the CustomFormHandler with unit tests. Native form handlers' handleXXX methods are usually not something you would easily call from JUnit environment. In the best case you would have to do a tremendous amount of NPE-driven mocking, and in the worst the NativeFormHandler would do a network, filesystem or database activity that you cannot outmock.
Also, all of your tests for CustomFormHandler.handleSomething would unavoidably call NativeFormHandler.handleSomething - and you will end up testing much more code than you have written.
This is why the right way to extend form handlers and other native components is not via inheritance, but via composition.
class CustomFormHandler extends FormHandler { private NativeFormHandler formHandler; public void handleSomething(request, response) { if (conditionOk) { return getFormHandler().handleSomething(request, response); } else { addFormException(...); return checkFormRedirect(request, response); } } public void setFormHandler(NativeFormHandler formHandler) { ... } public NativeFormHandler getFormHandler() { ... } }
/com/company/CustomFormHandler $class = com.company.CustomFormHandler formHandler = /atg/native/NativeFormHandler
Now you can create a real CustomFormHandler in your test, supply it with a mocked (e.g. with Mockito) NativeFormHandler and use Mockito.verify() to check that NativeFormHandler's mock handleSomething method was invoked when condition is ok.