Zombando da classe do concreto - Não recomendado


11

Acabei de ler um trecho do livro "Growing Object-Oriented Software", que explica algumas razões pelas quais zombar de classes concretas não é recomendado.

Aqui estão alguns exemplos de código de um teste de unidade para a classe MusicCentre:

public class MusicCentreTest {
  @Test public void startsCdPlayerAtTimeRequested() {
    final MutableTime scheduledTime = new MutableTime();
    CdPlayer player = new CdPlayer() { 
      @Override 
      public void scheduleToStartAt(Time startTime) {
        scheduledTime.set(startTime);
      }
    }

    MusicCentre centre = new MusicCentre(player);
    centre.startMediaAt(LATER);

    assertEquals(LATER, scheduledTime.get());
  }
}

E sua primeira explicação:

O problema dessa abordagem é que ela deixa implícita a relação entre os objetos. Espero que tenhamos deixado claro até agora que a intenção do Desenvolvimento Orientado a Testes com o Mock Objects é descobrir relacionamentos entre objetos. Se eu subclasse, não há nada no código do domínio para tornar visível esse relacionamento, apenas métodos em um objeto. Isso torna mais difícil ver se o serviço que suporta esse relacionamento pode ser relevante em outro lugar e terei que fazer a análise novamente na próxima vez que trabalhar com a classe.

Não consigo descobrir exatamente o que ele quer dizer quando diz:

Isso torna mais difícil ver se o serviço que suporta esse relacionamento pode ser relevante em outro lugar e terei que fazer a análise novamente na próxima vez que trabalhar com a classe.

Entendo que o serviço corresponde ao MusicCentremétodo chamado startMediaAt.

O que ele quer dizer com "em outro lugar"?

O trecho completo está aqui: http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html


Adicionado um comentário em seu blog, pois eu não conseguia entender o que ele queria dizer com essas citações.
precisa

@oligofren É realmente um grande enigma :) ...
Mik378

Respostas:


6

O autor desse post está promovendo o uso de interfaces sobre o uso de classes membros.

It turns out that my MusicCentre object only uses the starting and stopping methods on the CdPlayer, the rest are used by some other part of the system. I'm over-specifying my MediaCentre by requiring it to talk to a CdPlayer, what it actually needs is a ScheduledDevice.

O relacionamento que ele está preocupado em descobrir mais tarde é o fato de que a classe MediaCentre não precisa de todo o objeto CdPlayer. Ele afirma que, ao usar uma interface (presumivelmente limitada a apenas iniciar | parar), é mais fácil entender o que realmente é a interação.

"outro lugar" significa simplesmente que outros objetos podem ter relacionamentos igualmente limitados e o objeto de membro completo não é necessário - um subconjunto da funcionalidade agrupada por uma Interface deve ser suficiente.

A reivindicação começa a fazer mais sentido quando você explode toda a funcionalidade potencial:

  • começar
  • Pare
  • pausa
  • registro
  • ordem de reprodução aleatória
  • faixas de amostra, início da música
  • faixas de amostra, amostra aleatória de música
  • fornecer informações da mídia
  • ...

Agora, sua afirmação de "Eu só preciso começar e parar" faz mais sentido. O uso do objeto membro concreto em vez de uma Interface torna menos claro para futuros desenvolvedores o que é realmente necessário. A execução de testes de unidade do MediaCentre em todas as outras funções do CdPlayer é um desperdício de esforços de teste, pois eles pertencem ao estado "não me importo". Se a Recordfunção não estava funcionando nesse caso, nós realmente não nos importamos, pois ela não é necessária. Mas um futuro mantenedor não necessariamente saberia isso com base no código, como está escrito.

Por fim, a premissa do autor é usar apenas o necessário e esclarecer aos futuros mantenedores o que era necessário antes. O objetivo é minimizar o retrabalho / reanalisar o módulo de código durante a manutenção subseqüente.


Obrigado por esta ótima resposta. No entanto, você disse: "A execução de testes de unidade em todas as outras funções é um desperdício de esforços de teste, pois eles pertencem ao estado" não me importo "". Não é bem assim: "Criar zombarias para cada uma das outras funções é um desperdício de esforço de teste, pois elas pertencem ao estado" não me importo "".
precisa

@ Mik378 - sim, era exatamente isso que eu estava falando, eu apenas escrevia de maneira diferente. E atualizei minha resposta para deixar isso mais claro.

Mas acho que o termo "executar testes de unidade" é confuso. Isso significaria que o MusicCentre está prestes a testar seu colaborador ... enquanto, na verdade, MOCKS seu colaborador para testar seus próprios serviços. By the way, agora eu entendo o significado :)
Mik378

@ Mik378 - estamos dizendo a mesma coisa, e provavelmente estou usando uma terminologia menos precisa para fazer isso. Desculpas pela confusão.

4

Isso torna mais difícil ver se o serviço que suporta esse relacionamento pode ser relevante em outro lugar e terei que fazer a análise novamente na próxima vez que trabalhar com a classe.

Depois de pensar muito sobre isso, recebo uma possível interpretação dessa citação:

O "serviço" mencionado corresponde ao "fato de agendamento". Isso pode ser expresso por uma interface bem nomeada e "focada em uma função" denominada "ScheduledDevice" ou expressa implicitamente por uma implementação concreta de método que não depende de nenhuma interface.

Na amostra acima, o agendamento é expresso por todo o objeto com todos os recursos nomeado CDPlayer. Assim, ainda leva a um relacionamento implícito entre MusicCentree "fato de agendamento".

Portanto, se começarmos a injetar classes concretas e a zombar delas em objetos de alto nível; quando queremos testar esses objetos, temos que analisar cada objeto "concreto" injetado para ver se ele apresenta um relacionamento específico que TEMOS QUE MOCK porque são ocultos (implícitos). Pelo contrário, a codificação SEMPRE pela interface permite que o desenvolvedor descubra diretamente que tipo de relacionamento está prestes a ser atendido pelo objeto de alto nível e, portanto, detecte recursos que precisam ser zombados para isolar o teste de unidade.


Eu acho que você entendeu agora. Infelizmente, não recebi uma notificação do seu comentário.
21812 Steve

3

O serviço que eu quis dizer aqui foi CDPlayer.scheduleToStartAt (). É isso que o MediaCentre chama - o colaborador que ele precisa para funcionar. O MediaCentre é o objeto em teste.

A idéia é que, se eu explicitar exatamente o que o MediaCentre depende, não uma classe de implementação, eu posso dar um nome a essa função de dependência e falar sobre isso. Tudo o que o MediaCentre precisa saber é que ele conversa com o ScheduledDevices. À medida que o resto do sistema muda, não precisarei alterar o MediaCentre, a menos que seus recursos sejam alterados.

Isso ajuda?


(autor deste ótimo artigo :)) o que eu queria interpretar foi esta frase: "Isso dificulta ver se o serviço que suporta esse relacionamento pode ser relevante em outro lugar e eu terei que fazer a análise novamente na próxima vez em que trabalhar com a turma ". Que tipo de análise? O fato de detectar qual método do objeto deve implementar o relacionamento, já que este está claramente oculto?
Mik378
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.