Introdução aos Factory Methods para Testes
Ao concluirmos esta aula, nós:
- Saberemos o que é um factory method (método fábrica) no contexto de testes automatizados.
- Conseguiremos escrever bons factory methods (na teoria).
- Compreenderemos onde podemos posicionar nossos factory methods dentro da estrutura de testes.
Esta aula faz parte da abordagem moderna do curso Test With Spring adaptada para Kotlin e JUnit Jupiter 6. Na próxima aula, colocaremos esses conceitos em prática implementando métodos fábrica locais passo a passo.
Esta aula pressupõe que:
O Que É um Factory Method?
A Wikipedia define o padrão de projeto Factory Method (do catálogo GoF) da seguinte forma:
Na programação baseada em classes, o padrão factory method é um padrão criacional que utiliza métodos fábrica para lidar com o problema de criar objetos sem precisar especificar a classe exata do objeto que será criado.
Quando falamos sobre factory methods na escrita de testes automatizados, não estamos falando de hierarquias polimórficas que cumprem essa definição clássica do GoF. Quando escrevemos testes, um factory method (ou função fábrica em Kotlin) é simplesmente um método ou função que:
- Cria um novo objeto, define os valores dos campos/propriedades desse objeto criado e retorna a instância pronta.
- Pode receber parâmetros de entrada, mas não é obrigado a recebê-los.
Aqui estão dois exemplos simples de factory methods em Kotlin:
|
|
Agora que descrevemos o que é um factory method para testes, vamos descobrir como podemos escrever bons factory methods.
Escrevendo Bons Factory Methods
Antes de escrevermos um factory method, precisamos seguir três etapas fundamentais de design:
Primeiro, precisamos decidir qual é o seu propósito. Utilizar um factory method é uma excelente escolha quando queremos enfatizar uma propriedade específica (ou um pequeno conjunto de propriedades) do objeto criado — ou o seu estado de negócio. Por outro lado, não devemos utilizar factory methods como construtores de propósito geral onde precisamos passar todas as informações do objeto como parâmetros do método.
Por exemplo, se quisermos criar uma tarefa aberta (open task), é uma ótima ideia usar um factory method se não nos importarmos com as demais propriedades do objeto criado naquele cenário de teste. Em contrapartida, se quisermos criar um objeto Task passando todos os valores de todos os seus campos como parâmetros do método, não devemos usar um factory method, pois fazê-lo não traz nenhum benefício em relação a invocar o construtor diretamente.
Segundo, precisamos encontrar um bom nome para ele. Isso é crucial porque toda a ideia de utilizar factory methods baseia-se na premissa de que eles tornam nosso código de teste mais fácil de ler, escrever e manter. É por isso que devemos rejeitar nomes genéricos e escolher um nome que identifique imediatamente a essência (o estado de negócio) do objeto criado.
Por exemplo, createTask() não é um nome muito bom, pois não nos ajuda a entender que tipo de tarefa é criada pelo método fábrica (ela está aberta? em andamento? fechada?). Por outro lado, createOpenTask() é um excelente nome porque identifica com clareza a essência do objeto criado.
Terceiro, precisamos decidir quantos parâmetros o método terá. Se quisermos enfatizar uma propriedade ou propriedades específicas do objeto criado, podemos combinar duas ferramentas:
- Podemos usar o próprio nome do nosso factory method (para expressar o estado geral, como “tarefa aberta” ou “tarefa fechada”).
- Se os nossos métodos de teste exigirem objetos que possuem valores de campos ligeiramente diferentes, podemos enfatizar esses campos relevantes utilizando parâmetros do método.
Quando um factory method tem um propósito claro, normalmente ele não exige muitos parâmetros. Por isso, devemos sempre buscar minimizar o número de parâmetros do método.
Não especificaremos um número máximo rígido aqui, pois essa é uma decisão que você deve tomar analisando o seu contexto. Contudo, quando você começa a cometer erros na ordem dos argumentos ou sente que criar novos objetos nos testes está ficando trabalhoso, seu factory method provavelmente tem parâmetros demais.
Vamos analisar alguns exemplos em Kotlin que demonstram como devemos (e como não devemos) utilizar parâmetros ao escrever factory methods:
|
|
Agora que estamos familiarizados com os fundamentos para escrever bons factory methods, vamos avançar e descobrir onde devemos colocá-los em nosso projeto.
Onde Devemos Colocar Nossos Factory Methods?
Quando escrevemos um novo factory method, podemos escolher uma destas duas opções principais:
Primeiro, podemos adicionar o factory method diretamente dentro da própria classe de teste (como um método private fun ou uma função local). Isso nos ajuda a eliminar código duplicado, desde que todos os métodos de teste que utilizam esse factory method estejam localizados na mesma classe de teste.
Por exemplo, se utilizarmos essa abordagem local, nossa classe de teste com JUnit Jupiter 6 terá a seguinte aparência:
|
|
Segundo, podemos criar uma classe conhecida como Object Mother (Mãe de Objetos) e adicionar nosso factory method a ela. Isso nos ajuda a eliminar código duplicado quando o mesmo factory method precisa ser compartilhado por múltiplas classes de teste.
Nota sobre Kotlin vs. Java:
Em Java tradicional, uma classe Object Mother exigia a criação de uma classe public final class TaskFactory com um construtor privado vazio (private TaskFactory() {}) para impedir instanciação e métodos public static. Em Kotlin 2.4, podemos declarar simplesmente um object TaskFactory (singleton nativo da linguagem) ou até mesmo funções top-level no pacote de testes!
Por exemplo, se utilizarmos essa abordagem em Kotlin com object, nossa classe Object Mother terá a seguinte aparência:
|
|
E as diferentes classes de teste que utilizam esse factory method compartilhado terão a seguinte aparência:
|
|
|
|
Exploraremos essas duas técnicas na prática e em detalhes nas próximas aulas deste tópico. Por esse motivo, não entraremos na implementação interna de cada método fábrica nesta aula introdutória.
Agora estamos familiarizados com os conceitos fundamentais sobre factory methods para testes. Vamos resumir o que aprendemos nesta aula.
Resumo
Esta aula nos ensinou cinco pontos essenciais:
- Um factory method (método fábrica) cria os dados de teste que são utilizados pelos nossos métodos de teste.
- Um bom factory method possui um propósito claro e específico.
- O nome de um factory method deve identificar imediatamente a essência (o estado ou papel semântico) do objeto criado.
- Um bom factory method possui o menor número possível de parâmetros, destacando apenas os valores relevantes para o cenário.
- Podemos colocar nossos factory methods localmente dentro de uma classe de teste ou de forma compartilhada em uma classe Object Mother (
objectem Kotlin).