Back-End

3 mai, 2011

Um pensamento sobre testes de interação

Publicidade

Este artigo é sobre fazer testes concisos ao especificar o que aconteceu, em vez de (como em testes de interação) quem fez ou (como em um teste
baseado no estado) em diferentes tipos de coisas que já aconteceram.

Eu tenho um teste que diz que o objeto Availability deveria usar o objeto TupleCache para conseguir valores
particulares para: todos os animais (all animals), animais que ainda estão
trabalhando (animals that are still working), e animais que podem ser removidos
do serviço (animals that have been removed from service):

all animals - animals still working - animals already removed from service

Aqui está um teste com estilo mock
que descreve como o Availability usa
o TupleCache:

should “use tuple cache to produce a list of animals“ do
@availability.override(mocks(:tuple_cache))
during {
@availability.animals_that_can_be_removed_from_service
}.behold! {
@tuple_cache.should_receive(:all_animals).once.
and_return([{:animal_name => ‘out-of-service jake‘},
{:animal_name => ‘working betsy‘},
{:animal_name => ‘some…‘},
{:animal_name => ‘…other…‘},
{:animal_name => ‘…animals‘}])
@tuple_cache.should_receive(:animals_still_working_hard_on).once.
with(@timeslice.first_date).
and_return([{:animal_name => ‘working betsy‘}])
@tuple_cache.should_receive(:animals_out_of_service).once.
and_return([{:animal_name => ‘out-of-service jake‘}])
}
assert_equal([”…animals“, “…other…“, “some…“], @result)
end

Não estou muito
entusiasmado com tantos detalhes no teste, mas vamos deixar isso de lado. Note
que os resultados do teste implicam que o Availability
está modificando as tuples (“tuplas”
– pense nelas como hashes ou
dicionários) em uma simples lista de strings.
Note também que a lista de strings
está classificada. Ao notar isso, duas questões surgem:

  • Sobre
    a classificação – ela usa a classificação ASCII, que classifica todos os
    caracteres maiúsculos na frente dos minúsculos? Ou é o tipo de
    classificação que o usuário espera (onde isso é irrelevante)?
  • Os
    duplicados são retirados dos resultados?

Eu quero a responsabilidade de converter tuples dentro de listas para
pertencerem a outro objeto. Preferiria que o Availability tivesse a responsabilidade apenas de fazer as
perguntas certas para os dados persistentes, não de também massagear os
resultados. Eu gostaria de dar essa responsabilidade para um objeto Reshaper. Aqui está um teste expandido que
faz isso:

should “use tuple cache to produce a list of animals“ do
@availability.override(mocks(:tuple_cache, :reshaper))
during {
@availability.animals_that_can_be_removed_from_service
}.behold! {
@tuple_cache.should_receive(:all_animals).once.
and_return([”…tuples-all…“])
@tuple_cache.should_receive(:animals_still_working_hard_on).once.
with(@timeslice.first_date).
and_return([”…tuples-work…“])
@tuple_cache.should_receive(:animals_out_of_service).once.
and_return([”…tuples-os…“])
# New lines
@reshaper.should_receive(:extract_to_values).once.
with(:animal_name, [’…tuples-work…‘], [”…tuples-os…“], [”…tuples-all…“]).
and_return([[”working betsy“], [’out-of-service jake‘],
[’working betsy‘, ‘out-of-service jake‘,
‘some…‘, ‘…other…‘, ‘…animals‘]])
@reshaper.should_receive(:alphasort).once.
with([’some…‘, ‘…other…‘, ‘…animals‘]).
and_return([”…animals“, “…other…“, “some…“])
}
assert_equal([”…animals“, “…other…“, “some…“], @result)
end

Ele mostra que o método Availability
chama métodos Reshaper, em que 
poderíamos ver (se procurássemos) com certeza as propriedades que queremos. Mas
eu não gosto desse teste. O relacionamento entre Availability
e Reshaper não parece ser nem de
perto tão fundamental como aquele entre Availability
e TupleCache. E eu odeio a noção
geral de que “converter uma pilha de
tuples em uma lista razoável” é feito de maneira tão específica: ele
vai deixar a manutenção mais difícil. E eu não estou empolgado (ao longo deste
teste) com a maneira como o leitor deve inferir afirmações sobre o código a
partir dos exemplos. Então, que tal isto?

should “use tuple cache to produce a list of animals“ do
@availability.override(mocks(:tuple_cache))
during {
@availability.animals_that_can_be_removed_from_service
}.behold! {
@tuple_cache.should_receive(:all_animals).once.
and_return([{:animal_name => ‘out-of-service jake‘},
{:animal_name => ‘working betsy‘},
{:animal_name => ‘some…‘},
{:animal_name => ‘…other…‘},
{:animal_name => ‘…animals‘}])
@tuple_cache.should_receive(:animals_still_working_hard_on).once.
with(@timeslice.first_date).
and_return([{:animal_name => ‘working betsy‘}])
@tuple_cache.should_receive(:animals_out_of_service).once.
and_return([{:animal_name => ‘out-of-service jake‘}])
}
assert_equal([”…animals“, “…other…“, “some…“], @result)
assert { @result.history.alphasorted }

A última linha do teste afirma que – em algum momento do passado – o
resultado da lista foi “alphasorted”.
Uma lista que foi “alphasorted”
tem as propriedades que queremos, as quais podem ser checadas ao olhar para
os testes do método Reshaper#alphasort.

Em essência, checamos se, em algum momento do passado,
o objeto para o qual estamos olhando foi “marcado” com uma descrição
apropriada de suas propriedades. Portanto, nós não temos que construir um input
de teste que cheque as várias maneiras que uma descrição pode se tornar
verdadeira – nós simplesmente confiamos em testes antigos sobre o que a
marcação significa.

Aqui está o código que adiciona a marca:

def result.history()
@history = OpenStruct.new unless @history
@history
end
result.history.alphasorted = true
result.freeze

(Note que eu “congelo” o objeto. No Ruby,
isso deixa o objeto imutável. Isso vai de acordo com minha crescente convicção
de que talvez os programas deveriam ser constituídos de códigos funcionais
prensados entre bits de configuração de códigos de estado cuidadosamente
delimitados.)

Dito isso, suspeito que a estranheza original nos testes
é sinal de que preciso de responsabilidades diferentes de factoring, em vez de criar uma solução elaborada. Mas eu ainda não descobri como o factoring
deveria ser, então ofereço a alternativa para consideração.

?

Texto original disponível
em
http://www.exampler.com/blog/2010/03/20/a-sort-of-thought-about-interaction-and-perhaps-state-based-tests/