Introdução ao Sistema Sob Teste (SUT)
Ao concluirmos esta aula, estaremos familiarizados com as classes que serão utilizadas nas demais aulas deste tópico para demonstrar diferentes técnicas de criação de dados de teste.
O objetivo desta aula não é criticar a forma como essas classes foram originalmente projetadas. Contudo, se você quiser estudar mais a fundo as decisões de design discutidas aqui (como o antipadrão de construtor telescópico e o padrão JavaBeans versus imutabilidade em Kotlin), recomendamos a leitura dos materiais complementares indicados nas caixas de nota ao longo da aula.
Introdução à Classe MailingAddress
A classe MailingAddress armazena as informações de um único endereço de correspondência. Como a nossa aplicação precisa lidar tanto com pedidos domésticos (enviados para a Finlândia) quanto com pedidos internacionais, a classe MailingAddress deve conter as seguintes informações:
- O nome do destinatário (receiver)
- Endereço de rua (street address)
- Caixa postal (post office box)
- Código postal / CEP (post code)
- Cidade (city)
- Estado (state)
- País (country)
A estrutura inicial de propriedades da classe MailingAddress em Kotlin tem a seguinte aparência:
|
|
Há três regras de domínio importantes que devemos observar sobre esses atributos:
- Um endereço pode ter um endereço de rua (
streetAddress) ou uma caixa postal (postOfficeBox). - A informação de estado (
state) não é obrigatória em todos os países (como na Finlândia ou na Suécia). Entretanto, se quisermos entregar um pedido nos Estados Unidos, precisamos obrigatoriamente fornecer essa informação. - A informação de país (
country) não é obrigatória se quisermos entregar pedidos em endereços domésticos (Finlândia).
Os desenvolvedores originais da nossa aplicação decidiram utilizar o (anti)padrão do construtor telescópico (telescoping constructor (anti)pattern) para criar novos objetos MailingAddress. Eles escreveram três construtores sobrecarregados, que são descritos passo a passo a seguir:
Primeiro, se quisermos entregar um pedido nos Estados Unidos, precisamos utilizar o construtor que nos permite definir todos os campos do objeto MailingAddress criado. O código-fonte da classe com este construtor completo tem a seguinte aparência:
|
|
Podemos utilizar este construtor de 7 parâmetros para criar novos objetos MailingAddress que representam os seguintes endereços nos Estados Unidos (um com rua e outro com caixa postal):
|
|
Segundo, se quisermos entregar um pedido em um país estrangeiro que não exige a informação de estado (como a Suécia), podemos utilizar o construtor secundário de 6 parâmetros, que delega para o construtor principal passando null no lugar de state. O código-fonte deste construtor tem a seguinte aparência:
|
|
Podemos utilizar este construtor para criar novos objetos MailingAddress que contêm os seguintes endereços na Suécia:
|
|
Terceiro, se quisermos entregar um pedido em um endereço doméstico na Finlândia, podemos utilizar o construtor secundário de 5 parâmetros, que delega para o construtor de 6 parâmetros passando null para o país (country). O código-fonte completo com o terceiro construtor tem a seguinte aparência:
|
|
Podemos utilizar este terceiro construtor para criar novos objetos MailingAddress que contêm os seguintes endereços domésticos na Finlândia:
|
|
Nota sobre Kotlin vs. Java Clássico:
Em Java, não havia suporte a valores padrão de parâmetros ou argumentos nomeados, o que levava desenvolvedores a encadear múltiplos construtores (this(...)) — o chamado construtor telescópico. Em Kotlin 2.4, mesmo quando interagimos com classes legadas ou modelos que possuem sobrecargas telescópicas (ou anotadas com @JvmOverloads), podemos declarar um construtor primário conciso e tirar proveito de argumentos nomeados, como exploraremos em detalhes na próxima aula.
Leitura Adicional:
A seguir, examinaremos em detalhes a segunda classe de domínio do nosso sistema sob teste: a classe Task.
Introdução à Classe Task
A classe Task é utilizada em um rastreador de tarefas simples (task tracker) e armazena as informações de uma única tarefa. Por esse motivo, a classe Task deve conter as seguintes informações:
- Identificador (
id) - O responsável pela tarefa (
assignee— usuário a quem a tarefa foi atribuída) - O usuário que fechou a tarefa (
closer) - O usuário que criou a tarefa (
creator) - Descrição (
description) - O usuário que modificou a tarefa (
modifier) - Título (
title) - Status da tarefa (
status) - Resolução da tarefa (
resolution)
A estrutura de atributos da classe Task tem a seguinte aparência:
|
|
As classes Assignee, Closer, Creator e Modifier são objetos de valor (value objects) imutáveis que armazenam o userId do usuário correspondente em cada papel.
O código-fonte da classe Assignee tem a seguinte aparência:
|
|
O código-fonte da classe Closer tem a seguinte aparência:
|
|
O código-fonte da classe Creator tem a seguinte aparência:
|
|
O código-fonte da classe Modifier tem a seguinte aparência:
|
|
O enum TaskStatus especifica os possíveis status de uma tarefa. Seu código-fonte tem a seguinte aparência:
|
|
O enum TaskResolution especifica as possíveis resoluções de uma tarefa quando ela é encerrada. Seu código-fonte tem a seguinte aparência:
|
|
O diagrama de classes a seguir ilustra os relacionamentos entre todos esses componentes:
Os desenvolvedores da nossa aplicação de rastreamento de tarefas decidiram utilizar o padrão JavaBeans ao criar a classe Task. Isso significa que:
- Podemos criar um novo objeto
Taskutilizando um construtor sem argumentos (Task()). - Podemos definir os valores das propriedades do objeto criado utilizando métodos setter (ou atribuição direta às propriedades
varem Kotlin, que invocam os setters na JVM). - Podemos obter os valores das propriedades do objeto criado utilizando métodos getter (ou leitura direta das propriedades em Kotlin).
Em Java tradicional, a classe Task exigia dezenas de linhas de métodos get*() e set*() explícitos. Em Kotlin, propriedades mutáveis (var) geram automaticamente os getters e setters compatíveis com a especificação JavaBeans no bytecode da JVM, embora também possamos visualizar o equivalente completo com construtor vazio e métodos explícitos quando lidamos com classes legadas:
|
|
Caso você esteja inspecionando uma classe Task vinda de código legado estilo JavaBeans com getters e setters explícitos traduzidos literalmente, ela expõe o seguinte contrato completo:
|
|
Leitura Adicional:
Agora estamos familiarizados com as classes que serão utilizadas nas demais aulas deste tópico. Vamos resumir o que aprendemos nesta aula.
Resumo
Esta aula nos ensinou quatro pontos principais:
- A classe
MailingAddressarmazena as informações de um único endereço de correspondência (doméstico ou internacional). - Podemos criar novos objetos
MailingAddressutilizando o (anti)padrão do construtor telescópico. - A classe
Taskarmazena as informações de uma única tarefa no nosso sistema de acompanhamento de tarefas, relacionando-se com os objetos de valorAssignee,Closer,CreatoreModifiere com os enumsTaskStatuseTaskResolution. - A classe
Taskutiliza o padrão de construção estilo JavaBeans (construtor sem argumentos e mutação de estado por propriedades/setters).