Saturday, October 31, 2009

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.

Labels: , ,

Friday, March 09, 2007

Shortcuts for option and list matching in Scala

Instead of:
option match
{
   case None => 0
   case Some(value) => value.intField
}
you can write:
option.map(.intField).getOrElse(0)
Instead of:
list match
{
case1 Nil => 0
case list => list.reduceLeft((a,b)=>a+b)
}
you can write:
list.foldLeft(0)((a,b)=>a+b)

Labels:

From now on this will be a blog about the Scala language. My English is far from perfect, but I think I'm able to convey my thoughts in this area :)

Monday, November 07, 2005

Многие хотят получать фан от РИ отдельно от остальных участников. Особенно это заметно, когда на форумах заводят треды про обязанности мастера и игроков - типа, игроки дают заявки, мастер дает ответы на них, а метагейм и внеигровуха минимальны. То есть, игрок играет свою роль и получает от этого свой фан. Мастер мастерит мир и получает от этого свой фан. Если кому-нибудь что-нибудь не нравится - он уходит. С какой-то ТЗ это даже правильно: каждый себе делает свой фан, а если не умеет - его личные проблемы, которые фана других не касаются. Гарантия такая выходит: независимо от других ты всегда свою долю можешь получить. А фишка в том, что можно играть вместе. Но тогда твой фан будет зависеть от других.

Thursday, September 29, 2005

Смотрю сейчас Honey and Clover и думаю: почему я в РИ не встречал живых персонажей? За семь серий HnC - чуть больше двух часов - я узнал и понял о главных героях больше, чем за десятки часов игр - о наших персах. И дело явно не в длине квенты, и, наверное, даже не в пресловутом отыгрышЪе :-/ Может быть, потому что героев не бывает? А создавать негероя как-то не принято.

Monday, September 05, 2005

Второй раз в жизни вижу упоминание о Polaris, и уже хочу ее купить :) Идея про особые фразы для начала игры, представления персонажей просто замечательная. А то часто бывает так, что игра уже началась, но кто-то догенеривается, кто-то решает, как распределить экспу, кто-то за компом в игрушку доигрывает - в общем, разброд и шатание. Да и созданию атмосферы такое способствует.

Monday, August 29, 2005

МЕ Я ВИД О?

Третий раз пытаюсь завести дневник и третий раз сомневаюсь, пойдет ли мне это на пользу. :( Психологи, наверное, меня засмеют, но у меня есть теория на счет дневников и подобных вещей. Если я сюда напишу что-нибудь - ну там, мысль какую-то, идею или факт о себе - в общем, чего-нибудь, что знаю только я сам - то вещь эта "зафиксируется". Пока она только у меня в голове, я могу в любой момент сделать вид, что ее там нет, не было и не будет, потому что в своей голове я хозяин. А вот если ее записать, то будет "объективное свидетельство", что эта идея мою голову посещала, и тут уже никак не отвертецца (разве что пост удалить). Вот такая вот дилемма.