Analytics

27 abr, 2011

Mocks e legacy code

Publicidade

Durante minha grande viagem pela Europa, eu passei um dia em uma empresa legal, na qual um grupo divertido de
pessoas fazia um bom trabalho na base de legacy
codes (códigos de legado). Eles me desafiaram a melhorar um teste já
existente usando mocks. O teste era
típico de situações de legacy codes:
existia muito código de setup porque não se podia instanciar nenhum objeto
sem instanciar também zilhões deles, e a complexidade do teste fez com que ele
ficasse difícil.

Depois de alguma conversa, descobrimos que o que o
teste realmente queria checar era que quando uma Quote é recalculada devido a sua falta de data, você tem uma Quote nova. Em vez de usar morph no teste, eu tentei escrevê-lo no
meu estilo mock. Muito da
complexidade do teste estava em configurar as coisas de modo que uma Quote existente deveria ser recuperada.
Como o meu estilo atualmente é empurrar qualquer trabalho difícil para um novo
objeto em mock, decidi que deveríamos
ter um objeto QuoteFinder que faria
todo a busca para nós. O teste (em Ruby) seria algo assim:

quote_finder = flexmock(”quote finder“)
quote = flexmock(”quote“)

during {
… some function …
}.behold! {
quote_finder.should_receive(:find_quote).once.
with(…whatever…).
and_return(quote)

}

Em seguida, a nova quote teria que ser gerada. A maneira mais preguiçosa de fazer isso
seria adicionar o comportamento Quote:

quote_finder = flexmock(”quote finder“)
quote = flexmock(”quote“)

during {
… some function …
}.behold! {
quote_finder.should_receive(:find_quote).once.
with(…whatever…).
and_return(quote)
quote.should_receive(:create_revised_quote).once.
with(…whatever…).
and_return(”a new quote“)
}

Finalmente, o resultado da função em teste deveria
ser a nova quote:

quote_finder = flexmock(”quote finder“)
quote = flexmock(”quote“)

during {
… some function …
}.behold! {
quote_finder.should_receive(:find_quote).once.
with(…whatever…).
and_return(quote)
quote.should_receive(:create_revised_quote).once.
with(…whatever…).
and_return(”a new quote“)
}

assert { @result == “a new quote“ }

Me sinto um pouco como uma fraude, uma vez que eu
empurrei um monte de comportamentos importantes dentro de testes que
precisariam ser escritos por outra pessoa (incluindo o objetivo original do
teste, que era garantir que a próxima Quote
fosse um objeto diferente do que o último). O time, no entanto, me deu mais
crédito.

Eles tiveram dois momentos Aha! Primeiramente, a ideia de
“encontrar uma quote” foi
expandida pelo resto do código, e seria mais bem localizada em um objeto QuoteFinder. Em segundo lugar, eles decidiram
que realmente fazia sentido fazer com que as Quotes gerassem novas versões delas mesmas (em vez de deixar essa
responsabilidade em algum outro lugar).

Então, esse teste deu ao time dois
caminhos que eles poderiam seguir para melhorar seu código. No início, o QuoteFinder e o  Quote#create_revised_quote iriam, provavelmente, apenas delegar seu trabalho para
o legacy code já existente, mas agora existiam dois novos centros organizacionais que poderiam atrair comportamento. Isso se parece muito com Strangling an App (“estrangulando” um App – pdf), mas ele evita o problema potencial daquele “then a miracle occurs” (“e então um milagre acontece”) de precisar de uma boa arquitetura para
poder “estrangular”: em vez disso, ao seguir a estratégia do
faça-um-objeto-quando-você-hesitar que o mock
encoraja, você pode produzir um. Eu nunca vi algo elaborado sobre o uso de mocks para lidar com legacy code. Você
já?

PS: É possível que eu tenha errado nos detalhes da
história, mas acredito que o essencial esteja correto.

?

Texto original disponível em http://www.exampler.com/blog/2010/04/19/mocks-and-legacy-code/