пятница, 18 апреля 2008 г.

ГрувиМэн (эпизод1) Поможет вам тестировать код

Простой Java программист уже много дней пишет online игру "Дурак". Пишет на голой Java: сервлеты, jsp странички и javascript. И, конечно же, он использует JUnit для UNIT тестирования. Примерно вот такие вот тесты:


Player player1 = new Player ();
player1.setName ("John");
player1.setCards ("6h7h8h9h10h");

Player player2 = new Player ();
player2.setName ("Bill");
player2.setCards (...



после чего код по установке кард на столе, колоды и т.д.


game.attack (player1, listOfCards);
game.defend (player2, listOfPairs);


и уже после код, которые проверял


assert player1.hasCards("h6");


Простой Java программист расстроен, видя, как много кода занимают тесты. Каждый тест - 15-20 строк (с учетом того, что часть pre и post вызовов выноситься за рамки одного теста). И главное, так лень писать тест за тестом, они такие большие, но, то тут, то там, обнаруживается ошибка, то в AI, то в логике... И нужно бы добавить ещё один тест...




Время для ГрувиМэна, который спасет ваш проект, написав билдер для тестирования!




class TestBuilder {
def players
def deck
def table

static runTest(args, closure){
def tb = new TestBuilder(args)
closure.delegate = tb
closure()
}

def methodMissing(String name, args){
handle name, args
}

def propertyMissing(String name){
Object[] emptyArray = []
handle name, emptyArray
}

def handle(String name, args){
def player = players.find {it.name == name.split ('_')[0]}
def action = name.split ('_')[1]

if (action == "Has")
assert player.hasCards (args[0])
//we can get a player with this name, and process an action
}
}

class Person{
def name
def cards

def hasCards (String cards){
//test
}
}

def john = new Person (name: "John", cards: "6h 7h 8h 9h 10h Kh")
def mike = new Person (name: "Mike", cards: "6s 7s 8s 9s 10s Ah")

TestBuilder.runTest (players: [john, mike], table:[:], deck:[]){
John_Attacks ('6h')
Mike_Defends ('6h': 'Ah')
John_Has ("7h 8h 9h 10h Kh")
}



Теперь количество строк кода в каждом тесте сократилось в 4-5 раз, тесты стали куда читаемее. Простой Java программист успокоился, теперь он пишет тесты с радостью, улыбается, смотрит в окно и благодарит ГрувиМэна.

воскресенье, 23 марта 2008 г.

Enable Globally

ExpandoMetaClass по умолчанию не используется. Например:




Object.metaClass.puts = {obj->
println obj
}

puts "hello"


Мы увидим MissingMethodException. Т.е. вся иерархия классов не получает данный метод, только Object. Однако если мы напишем:



Integer.metaClass


То все объекты класса Integer будут использовать ExpandoMetaClass и начнут понимать puts:



5.puts “yes”

Но что, если мы не хотим перечислять все классы в которые мы хотим добавить наш метод? Для этого мы используем:



ExpandoMetaClass.enableGlobally()


После чего:
puts “hello”
будет работать везде

delegate, owner и this (часть 1)

Эти три переменные очень важны, если вы решили написать DSL. Хотя в простейших случаях они указывают на один и тот же объект, они все могут отличаться друг от друга. Вас не интересовала, как работает identity в Groovy? Быть может, вы думаете, что там кроется какая-та хитрость и только создатели языка да ещё 10-20 программистов знают, как все работает. Давайте напишем, свой вариант identity и назовем его по старой памяти with.




class Sample {
def foo (){
println "foo"
}

def with (Closure s){
s.delegate = this
s ()
}
}

new Sample ().with {
foo()
}



Установив delegate для Closure, мы намекаем Groovy, где искать ненайденные методы - в объекте delegate, в данном случае в объекте Sample. Очень просто? Да.




Давайте теперь вздохнем глубоко и напишем:




class Sample {
def foo (){
println "foo"
}
}

ExpandoMetaClass.enableGlobally()

Object.metaClass.with = {Closure s->
s.delegate = delegate
s ()
}



Теперь для любого класса, не только для Sample, будет работать наш with. Опять-таки очень просто. Подробнее о втором примере в следующий раз.

пятница, 21 марта 2008 г.

Ещё одна отличная книга о Groovy

В издательстве Pragmatic Programmer готовится к печати ещё одна книжка по Groovy: Venkat Subramanian – Programming Groovy. Вы можете купить её за 20 долларов в формате PDF. Найти её бесплатную я так и не сумел, поэтому пришлось раскошелиться. Стоит отметить, книжка стоит этих 470 рублей.

Книга состоит из трех основных частей: Beginning Groovy, Using Groovy, MOPing Groovy. Я уже довольно длительное время программирую на Groovy, написал на нем пару проектов, поэтому первые две части особого интереса для меня не представляли. Кроме разве что главы про Closures: более доступно объяснить разницу между owner, this, delegate мне кажется невозможно.

Но покупал то я эту книгу ради раздела про метапрограммирование. Читая в Groovy In Action главу про MOP я как-то не очень проникся, да и с того времени Groovy сильно нарастил свои метапрограммисткие мышцы. Я надеялся, что книжка Венката поможет мне не плавать в этой теме. Надежды оправдались, раздел про MOP написан блестяще. Очень подробно, в деталях, описывая все нюансы, автор просто вбивает вам в голову знание MOP. Он начинает с азов, а затем демонстрирует все возможные методы синтеза кода и заканчивает главами о тестировании (очень подробно: отдельно о том, как тестировать Java и Groovy), построении билдеров и написании DSL. Дочитав до конца, я включил консоль и без труда, без особых размышлений «как», написал два DSL.

Как мне кажется, книжка все-таки больше пригодится тем, кто уже пишет на Groovy. Венкат доступно и очень информативно описывает многие тонкости языка: тестирование, метапрограммирование и др. Да, самое главное: после прочтения я определенно стал понимать Groovy лучше.

Groovy & Grails _Ru