Conceito: Stubs
Esta diretriz descreve como criar um stub que pode ser utilizado como um sinalizador de substituição para componentes que ainda precisarão ser desenvolvidos. Um stub é um componente que, na realidade, não faz nada além de declarar a si próprio e os parâmetros que ele aceita. O stub contém apenas código suficiente para permitir que seja compilado e vinculado com o restante dos componentes.
Relacionamentos
Elementos Relacionados
Descrição Principal

Introdução

Um componente é testado enviando entradas à interface, aguardando que o componente as processe e, em seguida, verificando os resultados. Durante o processamento, um componente provavelmente usa outros componentes enviando-lhes entradas e usando seus resultados:

Diagrama descrito no texto associado.

Fig1: Testando um Componente implementado

Os outros componentes podem causar problemas nos testes:

  1. Eles ainda não podem ser implementados.
  2. Os componentes podem ter defeitos que impedem o funcionamento dos testes ou que fazem o usuário perder muito tempo descobrindo que uma falha de teste não foi causada pelo componente.
  3. Eles podem dificultar a execução dos testes quando você precisar. Se o componente é um banco de dados comercial, a sua empresa talvez não tenha licenças suficientes para todos. Ou um dos componentes pode ser o hardware, que só está disponível em horários programados em um laboratório separado.
  4. Eles podem fazer os testes tão devagar, de maneira que os testes não sejam executados com freqüência suficiente. Por exemplo, a inicialização do banco de dados pode levar cinco minutos por teste.
  5. Isso pode dificultar a provocação dos componentes para produzir certos resultados. Por exemplo, você pode querer que cada um dos métodos que grava em disco manipule erros de "disco cheio". Como ter certeza de que o disco está cheio no momento exato em que o método é chamado?

Para evitar esses problemas, você pode optar por utilizar os componentes stub (também chamados objetos-modelo). Os componentes stub comportam-se como os componentes reais, pelo menos nos valores que o componente envia ao responder aos testes. Eles podem ir além disso: eles podem ser emuladores de finalidade geral que buscam imitar fielmente a maioria ou todos os comportamentos do componente. Por exemplo, normalmente é uma boa estratégia criar emuladores de software para hardware. Eles se comportam exatamente como o hardware, apenas são mais lentos. Eles são úteis porque suportam melhor a depuração, há mais cópias disponíveis e podem ser usados antes de o hardware estar pronto.

Diagrama descrito no texto associado.

Fig2: Testando um Componente implementado por meio de stub de um componente do qual depende

Os stubs têm duas desvantagens.

  1. Eles podem ser de construção cara. (Esse é o caso específico dos emuladores.) Como também são software, eles precisam de manutenção.
  2. Eles podem mascarar os erros. Por exemplo, suponha que o componente usa funções trigonométricas, mas nenhuma biblioteca ainda está disponível. Os três casos de teste solicitam o seno de três ângulos: 10 graus, 45 graus e 90 graus. Você usa a calculadora para encontrar os valores corretos, em seguida, constrói um stub para o seno que retorna, respectivamente, 0,173648178, 0,707106781 e 1,0. Tudo vai bem até você integrar o componente com a biblioteca trigonométrica real, cuja função seno utiliza os argumentos em radianos e, portanto, retorna -0,544021111, 0,850903525 e 0,893996664. Esse é um defeito no seu código que é descoberto depois e com mais esforço do que você gostaria.

Stubs e Práticas de Design de Software

A menos que os stubs sejam construídos porque o componente real ainda não está disponível, você deve esperar mantê-los na implementação passada. Os testes que eles suportam provavelmente serão importantes durante a manutenção do produto. Os stubs, portanto, precisam ser escritos com padrões mais altos que o código descartado. Enquanto eles não precisarem atender aos padrões do código do produto - por exemplo, a maioria não precisa de um conjunto de testes próprio - os próximos desenvolvedores deverão mantê-los como componentes da mudança de produto. Se essa manutenção for muito difícil, os stubs serão descartados, e o investimento neles será perdido.

Especialmente quando eles precisarem ser mantidos, os stubs alteram o design do componente. Por exemplo, suponha que o seu componente usará um banco de dados para armazenar pares de chaves/valores persistentemente. Considere dois cenários de design:

Cenário 1: O banco de dados é utilizado para teste e uso normal. A existência do banco de dados não precisa ser escondida do componente. Você pode inicializá-lo com o nome do banco de dados:

 public Component(

String databaseURL) { try {     databaseConnection =         DriverManager.getConnection(databaseURL);     ... } catch (SQLException e) {...}     }

Enquanto você não quiser que cada localização leia ou escreva um valor para construir uma instrução SQL, certamente você terá alguns métodos que contenham SQL. Por exemplo, o código do componente que precisa de um valor pode chamar este método do componente:
 public String get(String key) { try {     Statement stmt =       databaseConnection.createStatement();     ResultSet rs = stmt.executeQuery(       

"SELECT value FROM Table1 WHERE key=" + key);     ... } catch (SQLException e) {...}     }

Cenário 2: Para os testes, o banco de dados é substituído por um stub. O código do componente deve parecer o mesmo, quer esteja sendo executado junto ao banco de dados real ou stub. Portanto, ele precisa ser codificado para usar os métodos de uma interface abstrata:
 

interface KeyValuePairs { String 

get(String key); void 

put(String key, String value);     }

Os testes implementariam KeyValuePairs com algo simples como uma tabela hash:
 

class FakeDatabase implements KeyValuePairs  { Hashtable table = new Hashtable(); public String 

get(String key) {     return (String) table.get(key); } public void 

put(String key, String value) {     table.put(key, value); }     }
Quando não estiver sendo utilizado em um teste, o componente deverá utilizar um objeto adaptador que converte as chamadas para o KeyValuePairs em instruções SQL:
 

class DatabaseAdapter implements KeyValuePairs { private Connection databaseConnection; public DatabaseAdapter(String databaseURL) {     try {         databaseConnection =             DriverManager.getConnection(databaseURL);         ...     } catch (SQLException e) {...} } public String 

get(String key) {     try {         Statement stmt =            databaseConnection.createStatement();         ResultSet rs = stmt.executeQuery(           "SELECT value FROM Table1 WHERE key=" + key);         ...     } catch (SQLException e) {...} } public void 

put(String key, String value) {     ... }     }

O componente pode ter um único construtor para os testes e para outros clientes. Esse construtor utilizaria um objeto que implementa KeyValuePairs. Ou pode fornecer essa interface apenas para testes, exigindo que clientes simples do componente insiram o nome do banco de dados:
 class Component { 

public Component(String databaseURL) {     this.valueStash = new DatabaseAdapter(databaseURL); } // For testing. 

protected Component(KeyValuePairs valueStash) {     this.valueStash = valueStash; }     }

Portanto, do ponto de vista dos programadores do cliente, os dois cenários de design produzem a mesma API, mas um é mais facilmente testável. (Observe que alguns testes podem usar o banco de dados real, e outros, o banco de dados stub.)

Informações Adicionais


Para obter mais informações relacionadas aos Stubs, consulte o seguinte: