- Aprender
- Computação e TI
- Programação
- Programação orientada a objetos com Java
- Pacotes e testes unitários em Java
Pacotes, diagramas de classes e testes unitários em Java
BásicoAula 10 de 11 do curso Curso de Programação orientada a objetos com Java
O que você vai aprender
- Agrupe classes relacionadas em pacotes, armazene cada arquivo-fonte onde o compilador e as ferramentas de construção esperam encontrá-lo e use classes entre pacotes com importações e o nível de acesso adequado.
- Declare pacotes e organize as pastas correspondentesEscreva uma declaração de pacote, escolha um nome de pacote convencional, armazene o arquivo na pasta correspondente e refira-se a classes por seus nomes totalmente qualificados.
- Use classes de outros pacotes por meio de importaçõesImporte classes individuais ou pacotes inteiros, saiba quais classes não precisam de importação, resolva conflitos entre classes com o mesmo nome e use importações estáticas.
- Controle o acesso entre pacotesEscolha entre public, protected, acesso de pacote e private para que cada pacote exponha apenas o que os outros pacotes precisam.
- Declare pacotes e organize as pastas correspondentes
- Interprete e desenhe diagramas de classes que mostram classes, seus membros e as relações entre elas, e passe de um diagrama para o código Java e vice-versa.
- Desenhe e interprete a representação de uma classeMostre o nome, os atributos e as operações de uma classe com tipos e visibilidade e marque membros estáticos, classes abstratas e interfaces.
- Represente associações e multiplicidadeConecte classes por associações, interprete e escreva multiplicidades e navegabilidade e distinga composição, agregação e dependência.
- Modele herança e transite entre diagramas e códigoMostre herança e implementação de interfaces, escreva código Java a partir de um diagrama e desenhe um diagrama a partir de código existente.
- Desenhe e interprete a representação de uma classe
- Divida um programa de modo que a interface do usuário, a lógica da aplicação e o armazenamento de dados fiquem em classes e pacotes separados, com dependências apontando em uma única direção.
- Separe a interface do usuário da lógica do programaMantenha a leitura de dados de entrada e a exibição de saídas em uma classe de interface do usuário, mantenha as regras do programa em classes de lógica e conecte as duas no método main.
- Organize camadas, pacotes e dependênciasOrganize classes em uma camada de apresentação, uma camada de lógica e uma camada de acesso a dados, mantenha as dependências apontando para baixo e conecte as camadas por interfaces.
- Separe a interface do usuário da lógica do programa
- Escreva testes unitários automatizados com JUnit, escolha casos de teste que revelem defeitos, mantenha os testes independentes e legíveis e desenvolva código novo escrevendo os testes primeiro.
- Escreva e execute seu primeiro teste JUnitCrie uma classe de teste, escreva métodos de teste com asserções, execute-os e interprete o resultado de um teste que passa e de um que falha.
- Escolha casos de teste que revelem defeitosEscolha dados de entrada típicos, de valor-limite e inválidos, teste exceções esperadas e dê a cada teste um objetivo e um nome claros.
- Mantenha os testes independentes e fáceis de manterPrepare novos objetos de teste antes de cada teste, evite estado compartilhado, execute um teste com várias entradas e teste a lógica separadamente da entrada e da saída.
- Desenvolva código escrevendo os testes primeiro com TDDSiga o ciclo vermelho–verde–refatorar a partir de uma lista de testes, dê passos pequenos e saiba o que o desenvolvimento orientado por testes promete e o que não promete.
- Escreva e execute seu primeiro teste JUnit
- Use uma ferramenta de construção para gerenciar bibliotecas, seguir a estrutura padrão do projeto e compilar, testar e empacotar um programa com um único comando.
- Configure e construa um projeto MavenDescreva um projeto em pom.xml com coordenadas e dependências, siga a estrutura padrão de pastas e execute o ciclo de vida da construção.
- Configure um projeto Gradle e corrija falhas de construçãoEscreva o script de construção Gradle equivalente, execute testes JUnit na JUnit Platform, compare as duas ferramentas e encontre a causa de uma falha de construção.
- Configure e construa um projeto Maven
Guia de estudo de Pacotes e testes unitários em Java
1Organize um programa Java em pacotes
À medida que um programa cresce, nomes de classes e pastas precisam de uma estrutura que tanto as pessoas quanto Java possam seguir. Pacotes agrupam tipos relacionados e definem limites de acesso. Importações tornam esses tipos mais fáceis de usar sem expor seus detalhes internos. Você relacionará uma declaração ao caminho de seu código-fonte, resolverá nomes entre pacotes e decidirá quais classes, construtores e métodos outra parte do programa deve poder chamar.
1.1 Nomes de pacotes e pastas de código-fonte
Um pacote agrupa classes e interfaces relacionadas sob um mesmo nome. Ele também separa nomes: duas classes chamadas Parcel podem coexistir quando seus nomes de pacote são diferentes. Um pacote oferece ao Java um limite de acesso, além de uma forma de organizar arquivos.
Declare o pacote primeiro
Um arquivo-fonte comum começa com sua declaração de pacote, seguida das importações e depois da classe. Comentários podem aparecer antes da declaração. Por exemplo:
package com.example.parcels.domain;
public class Parcel {
private int weightGrams;
public Parcel(int weightGrams) {
this.weightGrams = weightGrams;
}
}
Todo tipo de nível superior nesse arquivo pertence a com.example.parcels.domain. Um arquivo tem no máximo uma declaração de pacote. A classe pública Parcel deve ficar em Parcel.java; alterar apenas a pasta não altera o pacote declarado.
Transforme pontos em pastas
O diretório raiz do código-fonte é a pasta onde começa o caminho do pacote. Em um projeto Maven ou Gradle padrão, o código Java principal começa em src/main/java. Substitua cada ponto no nome do pacote por uma barra e depois acrescente o nome da classe:
src/main/java/
com/example/parcels/domain/Parcel.java
O pacote é com.example.parcels.domain, não src.main.java.com.example.parcels.domain. As três primeiras pastas descrevem a estrutura da construção; elas não fazem parte do nome Java. Quando uma IDE informa uma incompatibilidade de pacote, compare a declaração com o caminho abaixo do diretório raiz do código-fonte. Mova o arquivo ou corrija a declaração para que correspondam.
Nomes de pacotes usam letras minúsculas. Organizações geralmente invertem seu domínio: example.com torna-se com.example, seguido do projeto e de sua área. Um projeto sem domínio pode usar parcels.domain. Se um componente do domínio invertido contiver um hífen, substitua-o por um sublinhado, como em org.example.hyphenated_name. Evite o espaço de nomes java e javax da plataforma.
Use o nome completo da classe
Um nome totalmente qualificado une o pacote e o nome simples da classe: com.example.parcels.domain.Parcel. Ele identifica a classe mesmo quando outro pacote também tem uma Parcel. O código pode nomear uma classe acessível por extenso sem importação:
java.util.ArrayList<String> names = new java.util.ArrayList<>();
Para executar uma classe principal em um pacote, forneça seu nome completo e um caminho de classes apontando para a raiz da árvore de pacotes compilada. Na pasta do projeto:
javac -d out src/main/java/com/example/parcels/Main.java src/main/java/com/example/parcels/domain/Parcel.java
java -cp out com.example.parcels.Main
Aqui, javac -d out cria a árvore de saída, e java -cp out busca a partir da raiz dessa árvore. Iniciar java Main de uma pasta de pacote perde a parte do pacote na identidade da classe. Ferramentas de construção e IDEs normalmente gerenciam esses comandos.
Distinga pacotes próximos
com.example.parcels e com.example.parcels.domain são pacotes separados. Compartilhar um prefixo de nome não concede direitos de acesso adicionais. Um arquivo sem declaração pertence ao pacote sem nome. Pacotes nomeados não podem importar suas classes, portanto use pacotes nomeados em todo programa com várias partes.
Principais conclusões
- Coloque a declaração de pacote antes das importações e da classe.
- Faça o caminho do pacote corresponder ao caminho abaixo do diretório raiz do código-fonte; não inclua
src/main/javano nome do pacote. - Use nomes de pacotes em minúsculas e o nome totalmente qualificado da classe quando necessário.
- Um prefixo compartilhado no nome dos pacotes não concede acesso de pacote entre eles.
- Execute classes em pacotes a partir da raiz da árvore compilada usando seus nomes totalmente qualificados.
1.2 Importações e conflitos de nomes de classes
Uma importação permite que um arquivo-fonte use o nome simples de um tipo acessível. Ela informa ao compilador o significado do nome. Não copia a classe, não baixa uma biblioteca nem torna pública uma classe oculta.
Leia o início de um arquivo-fonte
A interface do usuário pertence a um pacote e usa classes de outros dois:
package com.example.parcels.ui;
import com.example.parcels.domain.Parcel;
import com.example.parcels.domain.ShippingCalculator;
import java.util.Scanner;
public class TextUI {
private Scanner scanner;
private ShippingCalculator calculator;
}
Parcel e ShippingCalculator vêm do pacote de domínio do programa; Scanner vem da biblioteca Java. Cada importação se aplica apenas a esse arquivo. Outro arquivo que usa Scanner precisa de sua própria importação ou do nome completo java.util.Scanner.
Escolha um tipo único ou um curinga
import java.util.Scanner; é uma importação de tipo único. Sua classe exata fica visível para quem lê. import java.util.*; é uma importação sob demanda: tipos acessíveis nesse pacote podem ser nomeados quando usados. O curinga não carrega classes não utilizadas no programa nem o torna mais lento.
O curinga se limita àquele pacote. Ele não inclui java.util.function.Function nem java.util.stream.Collectors, que estão em pacotes separados. A mesma regra se aplica ao seu próprio código: import com.example.parcels.*; não alcança com.example.parcels.domain.Parcel.
Três casos não precisam de importação: tipos no mesmo pacote, tipos disponíveis automaticamente em java.lang e tipos nomeados por extenso. Por isso, String, System, Math, Integer e Object aparecem frequentemente sem importações.
Resolva um nome simples compartilhado
Tanto java.util quanto java.awt têm uma classe chamada List. Duas importações de tipo único para essas classes diferentes causam erro de compilação. Duas importações com curinga podem coexistir, mas o nome simples List torna-se ambíguo quando usado. Uma importação de tipo único tem precedência sobre uma importação com curinga.
Se ambas as classes forem necessárias, importe uma e escreva a outra por extenso:
import java.util.List;
class NameDisplay {
private List<String> names;
private java.awt.List widget;
}
As importações Java não oferecem uma sintaxe de apelido como import ... as .... O nome totalmente qualificado é a maneira de distinguir o segundo tipo.
Importe um membro estático
Uma importação estática dispensa o nome da classe proprietária na chamada de um membro estático:
import static java.lang.Math.sqrt;
O arquivo agora pode chamar sqrt(16.0). O código de teste frequentemente usa import static org.junit.jupiter.api.Assertions.assertEquals; para que as asserções sejam lidas como verificações diretas. No código comum do programa, use importações estáticas com critério para que quem lê ainda consiga ver de onde vêm as operações.
Siga o que o erro informa
“Cannot find symbol” pode indicar uma importação ausente ou um tipo escrito incorretamente. “Package ... does not exist” aponta para um problema de grafia do pacote, diretório raiz do código-fonte ou dependência. “Is not public ... cannot be accessed from outside package” significa que Java encontrou o tipo, mas o acesso está bloqueado. Adicionar outra importação não corrige esse erro de acesso; examine a declaração da classe e os membros de que você precisa.
Principais conclusões
- Uma importação resolve nomes em um arquivo e não concede direitos de acesso.
- Um curinga inclui tipos de um pacote, sem seus subpacotes.
- Tipos do mesmo pacote e tipos de
java.langnão precisam de importação. - Use uma importação de tipo único e um nome totalmente qualificado quando duas classes necessárias compartilham um nome.
- Uma importação estática permite chamar um membro estático sem o nome de sua classe.
- Distinga um erro de resolução de nomes de um erro de acesso.
1.3 Uma interface pública pequena
Um pacote deve expor as classes e os métodos de que outras partes precisam, mantendo ocultos os detalhes de implementação. Modificadores de acesso permitem impor esse limite. Uma importação pode encurtar um nome, mas não pode atravessar um limite fechado pelas regras de acesso do Java.
Escolha o acesso para um membro de classe
| Acesso | Quem pode usar o membro? |
|---|---|
public | Código que pode acessar o tipo que o declara |
protected | O mesmo pacote, além do acesso de subclasses entre pacotes |
| Sem modificador | Exatamente o mesmo pacote |
private | A classe que o declara e seus tipos aninhados |
A ausência de modificador significa acesso de pacote para membros comuns de classes e construtores. Não se trata de uma palavra-chave chamada default. Uma classe de nível superior tem apenas duas opções: pública ou com acesso de pacote. Uma classe pública de nível superior é armazenada em um arquivo com seu nome, como ShippingCalculator.java.
Verifique o tipo e a operação
Suponha que a interface do usuário precise construir uma encomenda. Uma classe pública, por si só, não é suficiente:
package com.example.parcels.domain;
public class Parcel {
private int weightGrams;
Parcel(int weightGrams) {
this.weightGrams = weightGrams;
}
}
O construtor tem acesso de pacote. O código em com.example.parcels.ui pode nomear Parcel, mas não pode chamar new Parcel(1200). Torne esse construtor public se a criação de encomendas fizer parte do serviço público do pacote. Mantenha weightGrams privado e exponha uma operação apropriada em vez de abrir o campo.
O acesso de pacote compara o nome completo do pacote. Um pacote que parece ser pai ou filho não é o mesmo pacote. O código em com.example.parcels não recebe acesso especial às classes auxiliares em com.example.parcels.domain.
Entenda o acesso protegido
protected permite acesso de classes sem relação no pacote que declara o membro, além de subclasses. Entre pacotes, um membro de instância é acessado no contexto de herança da subclasse. Uma subclasse pode usar seu próprio campo herdado, mas não pode usar um objeto arbitrário da superclasse para contornar o limite do pacote.
Por exemplo, se Item declara protected int weightGrams, uma Letter extends Item em outro pacote pode usar this.weightGrams. Dentro de Letter, um parâmetro declarado apenas como Item não permite item.weightGrams; uma referência declarada como Letter permite o acesso herdado. Essa restrição impede que os privilégios da subclasse se tornem acesso geral a cada Item.
Oculte classes auxiliares deliberadamente
Torne ShippingCalculator e feeInCents públicos se a interface os chamar. Uma classe auxiliar WeightBands pode manter acesso de pacote. Assim, o código fora do pacote de domínio depende do comportamento da calculadora, não de como ela armazena a tabela. Substituir essa classe auxiliar exige menos alterações em outros lugares.
Testes podem usar o mesmo nome de pacote da classe testada enquanto ficam em src/test/java. Isso lhes dá acesso de pacote sem colocar código de teste entre arquivos de produção. Isso não concede acesso a campos privados. Tornar cada membro público apenas para satisfazer um teste espalha dependências de detalhes internos; em vez disso, teste o comportamento público ou uma operação útil com acesso de pacote.
Principais conclusões
- Membros de classe com acesso de pacote são acessíveis apenas no mesmo pacote exato.
- Quem faz uma chamada precisa ter acesso tanto à classe quanto ao construtor ou método que usa.
- O acesso protegido inclui acesso no mesmo pacote e acesso restrito de subclasses entre pacotes.
- Mantenha campos privados e exponha apenas as operações de que outro pacote precisa.
2Esboce a estrutura do programa com diagramas de classes UML
Um diagrama de classes oferece uma visão compacta de um programa antes que você leia os corpos de seus métodos. Caixas descrevem tipos e operações; linhas descrevem ligações, quantidades e relações entre subtipos. Você interpretará essas marcações com precisão e as traduzirá em declarações Java. O programa de encomendas fornece exemplos de classes individuais, coleções de objetos associados e implementações de repositório intercambiáveis.
2.1 O que a caixa de uma classe UML informa
Um diagrama de classes UML mostra a estrutura estática: classes, seus membros e relações. Ele ajuda quem lê a ver que tipos de objetos existem e o que oferecem. Não acompanha a ordem das chamadas de métodos durante a execução.
Leia os três compartimentos
Uma caixa de classe tem o nome da classe no topo, os atributos no meio e as operações na parte inferior. Um atributo geralmente representa um campo Java; uma operação representa a assinatura visível de um método. Um esboço pode omitir compartimentos que não atendem ao seu objetivo.
Para uma encomenda, uma representação textual da caixa é:
Parcel
-----------------------------
- weightGrams: int
- recipient: String
-----------------------------
+ Parcel(weightGrams: int, recipient: String)
+ getWeightGrams(): int
+ isHeavierThan(limitGrams: int): boolean
O construtor tem o nome da classe e não tem tipo de retorno. O método isHeavierThan aceita um inteiro chamado limitGrams e retorna um booleano.
Traduza a ordem de nomes e tipos
UML coloca o nome antes do tipo: weightGrams: int. Java coloca o tipo primeiro: int weightGrams. Uma operação segue o mesmo padrão de nome primeiro para parâmetros e depois coloca o tipo de retorno após o parêntese de fechamento.
private int weightGrams;
public boolean isHeavierThan(int limitGrams) {
return weightGrams > limitGrams;
}
Esse código corresponde a - weightGrams: int e + isHeavierThan(limitGrams: int): boolean. O corpo de um método geralmente não aparece no diagrama; a operação declara o que pode ser chamado, não o algoritmo.
Preserve a visibilidade e as marcações especiais
Os símbolos de visibilidade são + public, - private, # protected e ~ acesso de pacote. Coloque o símbolo antes do nome do membro. Se um diagrama fornecido omitir um símbolo, não invente sua visibilidade; adicione o símbolo pretendido ao documentar o código.
Um membro estático é sublinhado. Ele pertence à classe, e não a cada objeto. Uma classe ou operação abstrata é escrita em itálico ou marcada com {abstract} quando o itálico não é conveniente. A marcação de abstrato significa que uma subclasse precisa fornecer o comportamento ausente antes que um objeto possa ser criado a partir de uma classe concreta.
Uma caixa de interface traz «interface» acima do nome. Por exemplo, ParcelRepository pode oferecer + save(parcel: Parcel): void e + findAll(): List<Parcel>. A caixa declara um contrato; ela não escolhe uma implementação de armazenamento.
Escolha os detalhes de que quem lê precisa
Um esboço de planejamento pode mostrar apenas Parcel, Shipment e suas principais operações. Um diagrama para implementar uma classe precisa de tipos e visibilidade. Listar cada método de acesso rotineiro pode ocultar as relações que o diagrama deveria mostrar. Selecione membros úteis e mantenha-os consistentes com o código: um campo Java privado não deve aparecer com um símbolo público +, e um método estático precisa ser sublinhado.
Principais conclusões
- Uma caixa de classe separa o nome, os atributos e as operações.
- UML usa
name: Type; Java usaType name. - Use
+,-,#e~para as visibilidades public, private, protected e de pacote. - Sublinhe membros estáticos; marque elementos abstratos com itálico ou
{abstract}. - Uma caixa de interface mostra
«interface»e as operações de seu contrato.
2.2 Leia as extremidades das linhas de relacionamento
Uma associação mostra uma ligação estrutural entre objetos. Em Java, um objeto geralmente mantém uma referência a outro em um campo. A linha também pode mostrar quantos objetos são permitidos e em que direção a referência pode ser seguida.
Conte a partir da extremidade oposta
A multiplicidade fica ao lado da classe que ela conta. Se 1..* aparece ao lado de Parcel e 1 ao lado de Shipment, uma remessa tem uma ou mais encomendas, e cada encomenda pertence a exatamente uma remessa. Leia cada extremidade separadamente, começando por um objeto na extremidade oposta.
| Multiplicidade | Significado |
|---|---|
1 | Exatamente um |
0..1 | Zero ou um |
* ou 0..* | Qualquer quantidade, incluindo zero |
1..* | Pelo menos um |
2..4 | De dois a quatro |
Uma referência única pode representar 1 ou 0..1; uma coleção representa uma extremidade com vários valores. Uma List Java, por si só, não impõe um limite mínimo. Uma Shipment com uma regra de 1..* encomendas deve rejeitar uma lista inicial vazia e impedir que operações posteriores a deixem vazia. Da mesma forma, uma referência única obrigatória precisa de uma verificação contra null.
Siga a referência
Uma ponta de seta aberta em Parcel significa que uma referência pode ser seguida de Shipment até Parcel. Um diagrama de classes pode deixar a navegabilidade não especificada. Na convenção didática usada aqui, setas marcam referências em uma só direção; uma linha sem marcação pode significar acesso bidirecional ou direção não especificada. Não deduza um campo de volta apenas de uma seta em direção a Parcel.
Um nome de papel como parcels identifica o significado da ligação a partir da remessa. Mostre essa referência como uma associação ou como - parcels: List<Parcel> na caixa. Exibir ambas sem motivo pode fazer um campo parecer dois elementos de estado diferentes.
Escolha o significado de todo–parte
A composição usa um losango preenchido no todo. Uma parte pertence a no máximo um todo composto por vez e normalmente é removida com esse todo, como um pedido e suas linhas de pedido. A agregação usa um losango vazado para um agrupamento mais flexível cujas partes podem existir independentemente. Uma equipe pode agrupar jogadores sem controlar seu tempo de vida.
Java não tem uma palavra-chave de losango e não impõe a propriedade UML apenas porque existe uma lista. Modele composição apenas quando o projeto incluir essa regra de propriedade. Quando o projeto diz apenas que um objeto conhece outro, uma associação simples é mais clara que um losango sem fundamento.
Mostre um uso sem afirmar propriedade
Uma dependência é uma linha tracejada com uma ponta de seta aberta em direção ao tipo usado. Uma calculadora com feeInCents(Parcel parcel) depende de Parcel porque a assinatura de seu método usa esse tipo. Uma dependência é uma relação geral de uso; ela não se limita a código sem campos. Uma associação fornece informações estruturais mais específicas quando um campo é importante para o modelo.
Antes de desenhar uma linha, pergunte o que o código ou o requisito estabelece: uma referência, uma quantidade permitida, um uso ou propriedade. Uma coleção estabelece armazenamento de vários valores, mas por si só não estabelece propriedade exclusiva nem uma quantidade mínima.
Principais conclusões
- A multiplicidade ao lado de uma classe conta seus objetos para um objeto na outra extremidade.
- Um tipo de coleção não impõe uma multiplicidade mínima ou máxima.
- Uma seta de associação aponta para o tipo que a origem pode acessar por navegação.
- Coloque um losango de composição ou agregação na extremidade do todo.
- Uma dependência é uma seta tracejada de uso; uma associação descreve uma ligação estrutural.
- Mostre uma referência uma vez, a menos que o diagrama precise deliberadamente das duas representações.
2.3 De um diagrama para declarações Java
Herança e implementação de interface conectam um tipo específico a um mais geral. UML usa um triângulo vazado no tipo geral, mas o estilo da linha distingue as duas relações. Essa distinção indica qual declaração Java escrever.
Leia o triângulo e sua linha
Uma linha contínua com um triângulo vazado em Item significa que uma subclasse como Letter estende Item. Isso é generalização. Uma linha tracejada com um triângulo vazado em ParcelRepository significa que uma classe como InMemoryParcelRepository implementa essa interface. Isso é realização.
O triângulo sempre toca a superclasse ou a interface. Uma seta aberta tracejada, sem o triângulo, indica dependência. O estilo da linha e a ponta da seta juntos transmitem o significado; uma linha tracejada sozinha não basta.
Construa o esqueleto em uma ordem útil
- Crie cada classe ou interface em seu pacote.
- Traduza atributos em campos, preservando nomes, tipos e visibilidade.
- Traduza associações navegáveis em campos de referência ou de coleção.
- Adicione
extendsouimplementsa partir das linhas de generalização ou realização. - Traduza operações em cabeçalhos de métodos e depois forneça seus corpos.
Suponha que uma interface ofereça save e findAll. Uma implementação tem uma associação de vários valores com Parcel, com o papel parcels:
public class InMemoryParcelRepository implements ParcelRepository {
private List<Parcel> parcels = new ArrayList<>();
@Override
public void save(Parcel parcel) {
parcels.add(parcel);
}
@Override
public List<Parcel> findAll() {
return new ArrayList<>(parcels);
}
}
O triângulo com linha tracejada fornece implements ParcelRepository. A associação fornece o campo de lista. As assinaturas das operações fornecem os dois métodos públicos. Os corpos dos métodos são uma escolha de implementação: aqui save acrescenta um objeto, e findAll retorna uma nova lista para que quem chama não possa adicionar ou remover entradas diretamente na lista armazenada. Os próprios objetos de encomenda continuam compartilhados.
Desenhe no sentido inverso
Comece com uma caixa para cada classe ou interface. Um campo de valor simples torna-se um atributo. Uma referência a uma classe do programa torna-se uma associação; uma coleção sugere uma extremidade com vários valores. Examine as verificações de construtores e métodos antes de afirmar que o limite mínimo é um. Traduza extends em uma linha contínua com triângulo e implements em uma linha tracejada com triângulo.
Campos e operações herdados geralmente ficam na caixa da superclasse. Coloque membros novos ou sobrescritos na subclasse. Adicione uma dependência para um parâmetro, tipo de retorno ou uso local quando essa relação ajudar a explicar o projeto.
Pacotes podem envolver suas classes em caixas com formato de pasta e uma pequena aba. Esses grupos ajudam a revelar quais áreas dependem de quais outras. Mantenha atualizado um diagrama de documentação quando o código mudar; identifique um esboço inicial de planejamento para que quem lê compreenda seu objetivo.
Uma Shipment que contém encomendas não é um tipo de Parcel. Sua lista fornece uma relação de “tem”, portanto desenhe uma associação. Use herança para uma relação de “é um tipo de” que o sistema de tipos e o comportamento Java realmente sustentem.
Principais conclusões
- A generalização usa uma linha contínua e um triângulo vazado na superclasse.
- A realização usa uma linha tracejada e um triângulo vazado na interface.
- Traduza campos, associações, herança e assinaturas de métodos antes de escrever os corpos.
- Preserve os tipos e a visibilidade do diagrama ao traduzi-lo.
- O tipo declarado de uma coleção, por si só, não estabelece uma quantidade mínima.
- Use associações para objetos contidos e herança para relações entre subtipos.
- Mantenha os diagramas de documentação consistentes com o código que descrevem.
3Estruture um programa em camadas
Um programa fica mais fácil de testar e alterar quando seu diálogo, suas regras e seu armazenamento têm responsabilidades separadas. Você acompanhará um comando de encomenda por essas partes, moverá os cálculos para fora do código de console e passará colaboradores por construtores. Interfaces permitem que a lógica trabalhe com armazenamento real ou com um substituto em memória, enquanto uma leitura cuidadosa das importações revela os tipos dos quais cada camada depende.
3.1 Mantenha o diálogo fora dos cálculos
A interface do usuário lê comandos e exibe resultados. A lógica da aplicação contém as regras e os dados sobre os quais esses comandos atuam. Separá-las permite que um teste chame um cálculo diretamente e que outra interface o reutilize sem copiar a regra.
Dê a cada parte uma tarefa clara
TextUI cuida da interação: pede um peso, lê uma linha com Scanner, transforma o texto em um número, chama a lógica e exibe o resultado. ShippingCalculator recebe um peso em gramas e retorna uma tarifa em centavos. Ela não pergunta nada ao usuário nem exibe uma resposta.
Um método chamado printFee mistura essas tarefas se ele calcula e exibe o resultado. Altere a operação da lógica para feeInCents(int weightGrams). A interface pode então exibir "Fee: " + calculator.feeInCents(weightGrams) + " cents". Um teste pode verificar o inteiro retornado com uma asserção sem capturar a saída do console.
Escreva a regra como um valor de retorno
A regra de encomendas cobra 500 centavos para pesos positivos até 2.000 g, 900 centavos até 10.000 g e depois mais 100 centavos para cada bloco iniciado de 1.000 g acima de 10.000 g.
public int feeInCents(int weightGrams) {
if (weightGrams <= 0) {
throw new IllegalArgumentException("weight must be positive");
}
if (weightGrams <= 2000) {
return 500;
}
if (weightGrams <= 10000) {
return 900;
}
long excess = (long) weightGrams - 10000;
long startedBlocks = (excess + 999) / 1000;
return (int) (900 + 100 * startedBlocks);
}
Os dois primeiros ramos que retornam um valor incluem seus limites superiores. Para 12.500 g, o excesso é de 2.500 g. A divisão inteira após a adição de 999 conta três blocos iniciados, resultando em 1.200 centavos. O cálculo intermediário com long também lida com pesos positivos grandes do tipo int sem estouro ao somar 999.
Um peso inválido provoca uma IllegalArgumentException: o método rejeita um argumento que viola sua regra. A interface pode capturar essa exceção e escolher uma mensagem legível. A regra pertence à lógica; a redação da mensagem pertence à interface.
Conecte os objetos em main
Passe o leitor de entrada e a calculadora ao construtor da interface:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
ShippingCalculator calculator = new ShippingCalculator();
TextUI ui = new TextUI(scanner, calculator);
ui.start();
}
Crie um scanner para o fluxo de entrada e compartilhe-o com a interface. Vários scanners sobre System.in podem interferir na entrada armazenada em buffer uns dos outros. O método main monta o programa; start executa o laço de diálogo.
Ao revisar uma classe, observe suas responsabilidades. Um Scanner ou System.out dentro de uma calculadora de tarifas revela uma mistura de entrada e saída com regras. Uma fórmula de tarifa dentro da interface revela o mesmo problema pelo outro lado. Mova o cálculo para um método com entrada explícita e um resultado; mantenha a interpretação de comandos e o texto de exibição juntos na interface.
Principais conclusões
- A interface lê e exibe; métodos de lógica recebem valores e retornam resultados.
- Mantenha regras de tarifa e validação na lógica para que os testes possam chamá-las diretamente.
- Deixe a interface escolher como exibir um resultado ou uma entrada rejeitada.
- Crie colaboradores em
maine passe-os ao construtor da interface. - Conte um bloco de peso parcialmente usado como um bloco iniciado quando a regra exigir.
3.2 Conecte camadas por um repositório
Um programa em camadas agrupa classes por responsabilidade. A camada de apresentação cuida do diálogo, a camada de lógica aplica regras e a camada de acesso a dados salva e recupera informações. Uma classe usa outra camada por meio de uma operação clara, sem acessar diretamente seus campos internos.
Posicione as classes de encomendas
O pacote de interface contém TextUI. O pacote de domínio contém Parcel, ShippingCalculator e ParcelService. O pacote de dados contém ParcelRepository e suas implementações de armazenamento. Main monta os objetos acima dessas camadas.
Um comando segue este caminho: o usuário digita “add 1200 Alex”; a interface o interpreta e chama service.register(1200, "Alex"); o serviço valida o peso, cria uma encomenda, salva-a por meio do repositório e retorna 500 centavos; a interface exibe a tarifa. A leitura de texto, a decisão sobre o que é permitido e o armazenamento do resultado têm lugares separados.
Dependa de um contrato
O serviço precisa do comportamento de armazenamento, não de um formato de arquivo específico. Expresse esse comportamento por uma interface:
public interface ParcelRepository {
void save(Parcel parcel);
List<Parcel> findAll();
}
O serviço armazena uma referência do tipo ParcelRepository e a recebe no construtor:
public class ParcelService {
private final ParcelRepository repository;
public ParcelService(ParcelRepository repository) {
this.repository = repository;
}
}
Isso é injeção pelo construtor, uma forma de injeção de dependência: quem chama fornece um colaborador. Um serviço que constrói new FileParcelRepository(...) dentro de seus próprios métodos escolhe o armazenamento em arquivo para si e dificulta testes isolados.
Altere a implementação na montagem
O programa em execução pode criar new ParcelService(new FileParcelRepository("parcels.csv")). Um teste pode fornecer new InMemoryParcelRepository(). Ambas as implementações atendem à mesma interface. Chamadas por meio da interface usam polimorfismo para chegar aos métodos do objeto real.
Com o contrato do repositório inalterado, trocar o armazenamento exige uma nova implementação e uma alteração na montagem. As regras de tarifa e o código de diálogo não precisam saber como os dados são armazenados.
Verifique a direção das dependências com precisão
O caminho descendente de uso dos serviços vai da interface para a lógica e depois para o armazenamento. O domínio não deve importar nem chamar classes da interface. Se a lógica precisar informar um resultado ao usuário, retorne-o à interface. Se a interface salvar diretamente pelo repositório, ela poderá contornar a validação do serviço.
As referências a tipos Java também importam: uma assinatura de repositório que nomeia Parcel depende desse tipo de domínio. Com ParcelRepository no pacote de dados e o serviço no pacote de domínio, esses pacotes se referem um ao outro. Para remover esse ciclo de dependências entre pacotes, coloque a interface do repositório junto aos tipos de domínio e deixe que as implementações de dados a importem. O serviço continua usando o mesmo contrato; apenas sua localização muda. Examine tanto o caminho das chamadas de métodos quanto as importações ao verificar o projeto.
Programas maiores podem primeiro agrupar pacotes por funcionalidade, como pedidos ou clientes, e depois separar responsabilidades dentro de cada funcionalidade. Nomes de pacotes ajudam quem lê a localizar responsabilidades; parâmetros de construtores e assinaturas de métodos mostram as dependências reais.
Principais conclusões
- Separe as responsabilidades de apresentação, lógica e acesso a dados.
- Passe uma interface de repositório ao construtor do serviço.
- Escolha uma implementação em arquivo ou em memória no ponto em que o programa é montado.
- Encaminhe ações da interface pela lógica para que a validação seja aplicada.
- Retorne resultados à interface em vez de chamar seu código a partir da lógica.
- Distinga a direção de uso dos serviços das dependências de tipos Java ao verificar ciclos entre pacotes.
4Verifique o código com testes JUnit
Testes automatizados transformam o comportamento esperado em verificações que você pode executar novamente após cada alteração. Escrever uma verificação é apenas o começo: entradas úteis expõem limites, conjuntos de teste novos mantêm os testes independentes e falhas legíveis ajudam você a encontrar a causa. Você criará testes Jupiter para a lógica de encomendas, controlará seus dados e colaboradores e usará um ciclo curto com testes primeiro para desenvolver uma regra, um caso por vez.
4.1 Um teste que verifica um resultado
Um teste unitário chama uma pequena unidade de código com dados de entrada escolhidos e verifica o resultado automaticamente. Ele torna um requisito executável. Executar novamente os testes após uma alteração ajuda a encontrar uma regressão, um defeito em um comportamento que funcionava antes.
Crie um teste Jupiter
JUnit Jupiter é o modelo de programação usado pelo JUnit 5 e 6. JUnit 6 precisa de Java 17 ou posterior. Anotações e asserções de teste usam org.junit.jupiter.api.
Coloque ShippingCalculatorTest.java em src/test/java/com/example/parcels/domain, com a mesma declaração de pacote da calculadora. O diretório raiz separado mantém os testes afastados do código do programa entregue, preservando o acesso ao mesmo pacote.
package com.example.parcels.domain;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class ShippingCalculatorTest {
@Test
void lightParcelCostsBaseFee() {
ShippingCalculator calculator = new ShippingCalculator();
int fee = calculator.feeInCents(1500);
assertEquals(500, fee);
}
}
A classe e o método de teste podem ter acesso de pacote. Um método de teste retorna void e não pode ser privado. Use o @Test do Jupiter; org.junit.Test do JUnit 4 pertence a outro modelo de programação e não é encontrado pelo mecanismo Jupiter. Asserções do JUnit 4 podem funcionar quando sua biblioteca está disponível, mas importações consistentes do Jupiter mantêm a configuração clara.
Leia preparar–agir–verificar
A primeira parte prepara uma calculadora. A segunda age pedindo a tarifa. A asserção final compara os 500 centavos esperados com o valor retornado. Linhas em branco tornam essas tarefas fáceis de identificar. Exibir apenas fee deixaria para uma pessoa a decisão sobre sua correção; a asserção torna essa decisão parte do teste.
assertEquals(expected, actual) coloca o requisito primeiro e o resultado observado em segundo. Uma mensagem opcional vem por último, como em assertEquals(500, fee, "fee for 1,500 g"). Inverter os dois primeiros argumentos geralmente mantém o resultado de aprovação ou falha para esses valores, mas inverte o significado do relatório de falha.
Escolha a asserção adequada
Use assertTrue ou assertFalse para um booleano e assertNull ou assertNotNull quando a presença de um objeto for o resultado. Verificações de igualdade de objetos usam equals; sem uma sobrescrita, dois objetos distintos com campos correspondentes ainda podem ser considerados diferentes.
Resultados em ponto flutuante precisam de uma tolerância apropriada. O cálculo 0.1 + 0.2 produz 0.30000000000000004 em um double Java:
assertEquals(0.3, 0.1 + 0.2, 1e-9);
O terceiro argumento permite uma diferença absoluta de até 0.000000001. Escolha uma tolerância adequada ao cálculo; uma tolerância excessivamente grande pode ocultar um erro significativo. Cálculos com centavos inteiros não precisam de tolerância decimal.
Leia uma falha
Execute um teste pela IDE ou pela ferramenta de construção. Um relatório como “expected: <500> but was: <900>” informa os resultados exigido e observado; o rastreamento da pilha aponta para a asserção. Uma exceção inesperada também faz um teste falhar. Verde significa que os casos verificados passaram; escolher esses casos continua sendo uma parte separada do teste.
Principais conclusões
- Use anotações e asserções do Jupiter de
org.junit.jupiter.api. - Coloque testes em
src/test/java, normalmente no pacote da classe testada. - Prepare os objetos, aja sobre eles e verifique o resultado com uma asserção.
- Coloque o valor esperado primeiro em
assertEquals. - Use uma tolerância justificada para comparações em ponto flutuante.
- Leia o nome do teste que falhou, os valores esperado e real e a localização da asserção.
4.2 Escolha entradas nos limites das regras
Um conjunto útil de testes verifica como a especificação divide as entradas em casos. Uma entrada comum raramente testa uma regra inteira. Casos de valor-limite no limite e imediatamente ao seu lado são especialmente bons para encontrar uma condição escrita com o operador relacional errado.
Divida o espaço de entradas
Uma classe de equivalência agrupa entradas que devem ser tratadas da mesma forma. A regra de envio tem pesos inválidos em zero ou abaixo, uma faixa de 500 centavos até 2.000 g, uma faixa de 900 centavos até 10.000 g e um cálculo por bloco iniciado acima disso. Escolha entradas representativas das classes e depois examine seus limites.
| Peso em gramas | Resultado esperado |
|---|---|
0, -5 | Rejeitar com IllegalArgumentException |
1, 2000 | 500 centavos |
2001, 10000 | 900 centavos |
10001, 11000 | 1.000 centavos |
11001 | 1.100 centavos |
Alterar <= 2000 para < 2000 afeta exatamente 2.000 g. Um teste com 1.500 g continua passando. Acima de 10.000 g, teste tanto um bloco inteiro quanto o primeiro grama do bloco seguinte para verificar o arredondamento para cima.
Os valores esperados vêm do requisito e do cálculo manual. Para 12.500 g, o excesso é de 2.500 g, exigindo três blocos iniciados, portanto o resultado esperado é 1.200 centavos. Executar o método e copiar sua resposta para o teste também copiaria qualquer defeito.
Teste entradas rejeitadas
Um teste de exceção fornece ao JUnit a operação a executar:
assertThrows(IllegalArgumentException.class,
() -> calculator.feeInCents(0));
A expressão () -> ... é uma lambda: um pequeno trecho de código fornecido ao JUnit. JUnit o executa dentro da verificação. Chamar o método antes de fornecê-lo à asserção deixaria a exceção escapar antes que a verificação pudesse examiná-la.
assertThrows aceita o tipo de exceção nomeado ou uma subclasse. Ele falha se a operação terminar normalmente ou lançar um tipo incompatível. Ele retorna a exceção, portanto uma mensagem exigida pode ser verificada:
IllegalArgumentException error = assertThrows(
IllegalArgumentException.class,
() -> calculator.feeInCents(0));
assertEquals("weight must be positive", error.getMessage());
Dê um nome legível a cada cenário
Nomes como zeroWeightIsRejected e feeIsBaseFeeAtExactly2000Grams identificam o comportamento pretendido. Um teste pode ter várias asserções quando elas verificam o mesmo resultado. Após adicionar encomendas de 1.200 g e 800 g a uma remessa, verificar uma contagem de duas e um peso total de 2.000 g descreve esse único cenário. assertAll pode agrupar verificações para que todas as falhas sejam relatadas.
Verifique objetos alterados por meio de seu comportamento, como parcelCount() e totalWeightGrams(), em vez de expor campos privados para testes. Assim, uma refatoração pode alterar detalhes de armazenamento preservando as verificações. Cobertura completa de linhas significa que os testes executaram as linhas; ela ainda pode deixar passar um limite incorreto ou uma entrada inválida não testada.
Principais conclusões
- Divida as entradas conforme a especificação e teste casos representativos e limites.
- Verifique o próprio limite e valores próximos para detectar erros de uma unidade.
- Calcule os resultados esperados independentemente da implementação.
- Passe a operação a
assertThrowscomo uma lambda. - Nomeie um cenário por teste; várias asserções relacionadas podem verificar esse cenário.
- Teste o estado alterado por meio do comportamento observável por quem chama.
4.3 Testes que funcionam sozinhos e em qualquer ordem
Cada teste deve preparar tudo de que precisa. Um teste que só passa depois que outro adicionou dados depende de um histórico oculto. Esses testes tornam-se difíceis de confiar quando um executor altera a ordem ou executa apenas um método.
Crie um novo conjunto de teste
Um conjunto de dados e objetos de teste é o conjunto de objetos e dados que um teste usa. Coloque a preparação repetida de cada teste em @BeforeEach:
class ShipmentTest {
private Shipment shipment;
@BeforeEach
void setUp() {
shipment = new Shipment();
}
@Test
void newShipmentIsEmpty() {
assertEquals(0, shipment.parcelCount());
}
@Test
void addingParcelIncreasesCount() {
shipment.add(new Parcel(1200, "Alex"));
assertEquals(1, shipment.parcelCount());
}
}
A preparação é executada antes de cada método. O segundo teste não altera a remessa usada pelo primeiro. Por padrão, Jupiter também cria uma nova instância da classe de teste para cada método de teste. Campos estáticos mutáveis continuam compartilhados, portanto uma lista estática ainda pode transmitir estado entre essas instâncias.
@AfterEach faz a limpeza após cada teste. @BeforeAll e @AfterAll são executados uma vez para a classe e são estáticos no ciclo de vida padrão. Dados de referência compartilhados somente para leitura podem ser adequados para uma preparação feita uma vez por classe; objetos que mudam geralmente precisam ser criados novamente para cada teste. Os testes devem funcionar individualmente, sem depender da ordem no arquivo-fonte.
Use um corpo de teste para uma tabela
Um teste parametrizado executa a mesma verificação de comportamento para várias linhas:
@ParameterizedTest
@CsvSource({"1, 500", "2000, 500", "2001, 900",
"10000, 900", "10001, 1000"})
void feeMatchesTable(int grams, int expected) {
assertEquals(expected,
new ShippingCalculator().feeInCents(grams));
}
Cada linha é uma invocação separada com seu próprio relatório. @ValueSource(ints = {0, -5}) fornece um inteiro por invocação para testes de entradas inválidas. Anotações de testes parametrizados vêm de org.junit.jupiter.params; CsvSource e ValueSource estão em seu pacote provider. A dependência agregadora junit-jupiter inclui o módulo de testes parametrizados.
@DisplayName adiciona um rótulo legível ao relatório. @Disabled registra um teste ignorado com um motivo; uma verificação ignorada não fornece evidência de aprovação para o comportamento que testaria.
Controle o ambiente
Um teste de serviço pode receber um InMemoryParcelRepository, uma implementação simplificada para testes funcional que armazena encomendas em memória. Isso evita o conteúdo de arquivos reais e isola a lógica. Passe um Scanner construído a partir de uma cadeia de caracteres conhecida a uma interface de texto ao testar seu tratamento de entrada. Uma data fixa ou um Random com semente fixa também torna previsível uma entrada externa.
Um teste instável muda seu resultado entre execuções sem uma alteração relevante no código. Dados mutáveis compartilhados, o horário atual, valores aleatórios não controlados e arquivos remanescentes são causas comuns. Dê ao teste controle dessas entradas. Mantenha métodos auxiliares pequenos e a preparação legível; um conjunto de teste que constrói muitos objetos sem relação pode revelar que a classe testada tem tarefas demais.
Principais conclusões
- Use
@BeforeEachpara fornecer novos conjuntos de teste aos testes. - Uma nova instância da classe de teste não reinicia o estado estático mutável.
- Testes parametrizados executam um método com linhas de entrada relatadas separadamente.
- Use implementações simplificadas para testes e entradas fornecidas para manter testes de lógica independentes do armazenamento real e do relógio.
- Mantenha cada teste executável sozinho, sem uma ordem obrigatória de métodos.
4.4 Desenvolva comportamento com vermelho, verde e refatorar
O desenvolvimento orientado por testes (TDD) começa uma mudança de comportamento com um teste. Um ciclo curto torna visível o comportamento ausente, implementa-o e depois melhora a estrutura do código. Uma lista de testes guarda os casos que ainda precisam ser explorados sem exigir que todos sejam escritos de uma vez.
Escolha um item da lista de testes
Para um verificador de anos bissextos gregorianos, liste quatro comportamentos: um ano comum não bissexto, um ano divisível por quatro, um ano de século que não é divisível por 400 e um ano de século que é divisível por 400. Esses casos representam partes diferentes da regra.
Vermelho: escreva um teste e execute-o. Um método ausente pode causar um erro de compilação. Quando o método existir, verifique se o teste falha para o comportamento que você pretende adicionar. Uma asserção ausente, a chamada do método errado ou um teste não encontrado podem tornar enganoso um resultado aparentemente verde. Um novo caso também pode já ser satisfeito pelo código existente; examine esse resultado antes de alterar a implementação.
Verde: adicione o código mais simples que faça o novo teste e todos os anteriores passarem. Refatorar: melhore a estrutura sem alterar o comportamento e execute novamente os testes após cada pequena alteração. Depois, escolha o próximo caso.
Deixe os casos desenvolverem a regra
A primeira verificação é assertFalse(LeapYears.isLeapYear(2023)). Criar o método com return false; faz esse caso passar. É um primeiro passo deliberado, não uma regra de calendário concluída.
A próxima verificação, assertTrue(LeapYears.isLeapYear(2024)), falha. Altere o método para return year % 4 == 0;. Ambos os casos agora passam.
Em seguida, assertFalse(LeapYears.isLeapYear(1900)) expõe a exceção dos anos de século. Alterar a expressão para year % 4 == 0 && year % 100 != 0 faz esses três testes passarem.
Por fim, assertTrue(LeapYears.isLeapYear(2000)) expõe a exceção à regra dos anos de século. A implementação fica:
public static boolean isLeapYear(int year) {
return year % 400 == 0
|| (year % 4 == 0 && year % 100 != 0);
}
Execute as quatro verificações após a alteração. Cada nova falha apontou para uma parte ausente do comportamento, em vez de pedir que você depurasse uma implementação grande e inacabada.
Melhore a estrutura enquanto os testes estão verdes
Refatoração altera nomes ou organização preservando o comportamento. Renomear um método, extrair um método auxiliar, remover lógica repetida e mover uma classe para um pacote mais adequado são refatorações quando o comportamento observável permanece igual. Alterar a tarifa cobrada em um limite é uma mudança de comportamento e precisa de um teste que descreva essa alteração.
Se uma limpeza fizer um teste falhar, restabeleça o comportamento pretendido antes de adicionar outra funcionalidade. Pequenas alterações ajudam a localizar a causa de uma falha. Pular a limpeza deixa código que passa nos testes, mas se torna mais difícil de ampliar; escrever uma implementação grande para muitos casos futuros cria mais código para examinar quando a próxima verificação falhar.
TDD depende da qualidade dos requisitos e dos casos selecionados. Uma regra mal compreendida pode aparecer tanto no teste quanto na implementação. A lógica com entradas e saídas claras é um ponto de partida útil porque o resultado esperado pode ser declarado independentemente e verificado rapidamente.
Principais conclusões
- Mantenha uma lista de testes e implemente um comportamento por vez.
- Vermelho torna visível o comportamento ausente; verde faz todos os testes atuais passarem.
- Examine um teste que passou inesperadamente antes de supor que ele verificou a alteração pretendida.
- Refatore a estrutura preservando o comportamento e execute novamente os testes.
- Use casos sucessivos para expor regras comuns e exceções.
5Construa e teste projetos com Maven ou Gradle
Um projeto precisa de mais do que arquivos-fonte: precisa de bibliotecas, configurações de compilação, um executor de testes e uma maneira de empacotar o resultado. Maven e Gradle descrevem essas escolhas para que um comando possa repetir a construção. Você usará suas pastas padrão, declarará dependências do Jupiter, executará o inicializador incluído e distinguirá um erro de código de um problema de descoberta de testes ou de dependência lendo a saída da construção.
5.1 Construa um projeto Maven a partir de sua descrição
Uma ferramenta de construção transforma arquivos-fonte em um resultado verificado e empacotado. Maven lê uma descrição do projeto, baixa bibliotecas declaradas, compila código, executa testes e cria um artefato. A mesma descrição fornece à linha de comando e à IDE uma visão compartilhada do projeto.
Use a estrutura padrão
Coloque o código Java de produção em src/main/java, os testes em src/test/java, os recursos de produção em src/main/resources e os dados de teste em src/test/resources. As pastas de pacotes começam abaixo de cada diretório raiz do código-fonte Java. Maven grava a saída gerada em target; edite os arquivos-fonte, não as cópias compiladas nessa pasta.
O arquivo pom.xml na raiz do projeto, o Project Object Model, identifica o projeto usando coordenadas: groupId, artifactId e version. Por exemplo, com.example:parcels:1.0-SNAPSHOT identifica uma versão de desenvolvimento do programa de encomendas. Uma dependência de biblioteca também tem coordenadas.
Declare JUnit para testes
A seguir está uma configuração Maven pequena e completa. Ela compila para Java 17 e seleciona um executor de testes compatível com Jupiter:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>parcels</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>6.1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
</plugin>
</plugins>
</build>
</project>
Maven obtém dependências de repositórios, normalmente o Maven Central, e as armazena em cache em .m2/repository dentro de sua pasta pessoal. Bibliotecas necessárias para uma dependência são dependências transitivas. O escopo de teste disponibiliza JUnit para compilar e executar testes, não para a compilação do código principal nem para o caminho de classes do programa em execução. Um JAR comum mantém os JARs de suas dependências separados, a menos que o empacotamento seja configurado para reuni-los.
Solicite uma fase do ciclo de vida
O ciclo de vida principal passa por compile, test, package e install, entre outras fases. Solicitar package também executa as fases anteriores. Com o inicializador, ./mvnw test compila e testa; ./mvnw package também cria o JAR em target; ./mvnw install adicionalmente coloca o artefato no repositório local. O ciclo de vida separado de limpeza, ./mvnw clean, remove a saída gerada.
Uma falha de teste interrompe a construção normal antes do empacotamento de um novo artefato. Um JAR antigo ainda pode estar presente até a limpeza, portanto sua presença não comprova que a construção mais recente foi bem-sucedida.
Torne visível a descoberta de testes
Os padrões do Surefire incluem por padrão nomes que começam com Test ou terminam com Test, Tests ou TestCase. ShippingChecks não corresponde a eles; use ShippingCalculatorTest e examine a quantidade de testes informada. Testes colocados em src/main/java não podem usar uma dependência com escopo de teste.
O Maven Wrapper, mvnw ou mvnw.cmd no Windows, executa a versão do Maven especificada pelo projeto. Use um projeto que inclua seus arquivos de inicialização; um POM sozinho não cria os scripts. A partir da raiz do projeto, o inicializador fornece um comando de construção consistente sem uma instalação separada do Maven.
Principais conclusões
- Mantenha código principal, testes e recursos em suas pastas de origem padrão.
- Identifique projetos e bibliotecas com coordenadas de grupo, artefato e versão.
- Declare JUnit com escopo de teste e use um executor Surefire compatível.
- Uma fase Maven solicitada executa as fases anteriores de seu ciclo de vida.
- Examine as quantidades de testes; uma construção bem-sucedida ainda pode ter deixado de executar um teste com nome inadequado.
- A saída gerada fica em
targete pode ser reconstruída. - O inicializador executa a versão do Maven selecionada pelo projeto.
5.2 Execute testes Gradle e acompanhe erros de construção
Gradle descreve uma construção com um script e executa tarefas com suas dependências. Um script Kotlin chama-se build.gradle.kts; um script Groovy chama-se build.gradle. Plugins fornecem tarefas, e configurações de dependência indicam onde as bibliotecas são necessárias.
Configure uma aplicação e Jupiter
Um settings.gradle.kts na raiz do projeto pode conter rootProject.name = "parcels". Um script de construção Kotlin correspondente é:
plugins {
application
}
repositories {
mavenCentral()
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
dependencies {
testImplementation(platform("org.junit:junit-bom:6.1.3"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
application {
mainClass = "com.example.parcels.Main"
}
tasks.test {
useJUnitPlatform()
}
O plugin application fornece tarefas de construção Java e adiciona run. mavenCentral() seleciona um repositório de dependências. A BOM, ou lista de materiais, alinha as versões dos módulos JUnit, portanto a versão aparece uma vez. testImplementation fornece bibliotecas necessárias para compilar e executar testes. testRuntimeOnly fornece o inicializador apenas durante sua execução. Bibliotecas do código principal usam implementation, que também as disponibiliza para o código de teste.
useJUnitPlatform() seleciona a plataforma que executa testes Jupiter. Adicionar bibliotecas Jupiter sem configurar o executor pode deixar esses testes sem serem encontrados. O Gradle atual falha quando há arquivos-fonte de teste, mas nenhum teste é encontrado sob sua configuração padrão; examine a saída e a configuração da tarefa de teste em vez de remover a verificação.
Execute tarefas com o inicializador
Use o inicializador incluído no projeto: ./gradlew test executa testes; ./gradlew build compila, testa e empacota; ./gradlew run inicia a classe principal configurada; e ./gradlew clean remove build. No Windows, use gradlew.bat. Gradle usa a mesma estrutura de código-fonte principal e de teste que Maven e grava a saída gerada em build.
Uma tarefa marcada como UP-TO-DATE tem entradas e saídas monitoradas inalteradas, portanto Gradle reutiliza seu resultado anterior. Essa mensagem é diferente de uma execução de teste recém-realizada. O relatório HTML de testes está em build/reports/tests/test/index.html.
Encontre a etapa que falhou
Comece pelo primeiro erro útil. “Cannot find symbol” durante a compilação principal aponta para o código, a importação ou a dependência mencionados. A ausência de org.junit.jupiter.api durante a compilação dos testes aponta para a configuração da dependência de teste; o mesmo erro durante a compilação principal pode significar que um teste foi colocado na pasta de código-fonte principal. Um erro de resolução de dependência aponta para coordenadas, acesso ao repositório ou disponibilidade da rede.
Uma falha durante a execução de um teste é diferente: o teste foi compilado e encontrado, mas sua verificação falhou. Se “expected: <500> but was: <900>” aparecer no limite de tarifa de 2.000 g, examine a comparação que escolhe a faixa. Alterar dependências não corrigirá esse cálculo.
Maven oferece uma descrição declarativa em XML com fases fixas de ciclo de vida. Gradle oferece scripts e tarefas configuráveis, incluindo a possibilidade de pular trabalho atualizado. Ambos automatizam construções Java repetíveis; use a ferramenta escolhida para o projeto e leia a primeira falha que permita agir antes de alterar o código.
Principais conclusões
- Plugins adicionam tarefas Gradle; configurações controlam onde as dependências ficam disponíveis.
- Alinhe versões do JUnit com uma BOM e execute Jupiter na JUnit Platform.
- Use o Gradle Wrapper incluído para tarefas de teste, construção, execução e limpeza.
- Distinga resultados de tarefas em cache, erros de compilação, erros de dependência e falhas de asserção.
- Leia o nome do teste que falhou e os valores esperado e real antes de alterar a implementação.
Exercícios resolvidos de Pacotes e testes unitários em Java
1Mova uma classe de encomenda para um pacote
13 passos
- 1IntroduçãoDê à classe um lugar definido
Um programa de encomendas tem uma calculadora e uma interface de texto. Quando essas classes são movidas para pacotes separados, Java precisa de nomes, pastas e níveis de acesso que correspondam entre si.
Acompanhe a mudança em três etapas: posicione a classe, permita que outro arquivo a nomeie e depois verifique se quem a chama pode usar seu construtor e seus métodos.
- 2ExemploComece abaixo do diretório raiz do código-fonte
O diretório raiz do código-fonte é
src/main/java. ParaParcelemcom.example.parcels.domain, transforme os pontos do pacote em pastas e acrescente o nome do arquivo:src/main/java/com/example/parcels/domain/Parcel.javaEscreva
package com.example.parcels.domain;no início do arquivo. As pastassrc/main/javado diretório raiz descrevem a estrutura da construção e ficam fora da declaração de pacote. - 3ExplicaçãoMantenha intacta a identidade da classe
O nome simples é
Parcel; o nome totalmente qualificado écom.example.parcels.domain.Parcel. Mover o arquivo, por si só, não reescreve essa identidade.Uma classe pública chamada
Parceldeve ficar emParcel.java. Execute uma classe principal em um pacote a partir da raiz da árvore compilada usando um comando comojava -cp out com.example.parcels.Main, para que Java possa encontrar as pastas de pacotes dentro deout. - 4DesafioCorrija uma localização incompatível
Uma classe pública chamada
Receiptdeclarapackage com.example.parcels.domain;, mas está armazenada emsrc/main/java/com/example/parcels/ui/. O pacote deve permanecer inalterado. Para onde o arquivo deve ir?---reveal---
Mova-o para
src/main/java/com/example/parcels/domain/Receipt.java. O caminho do pacote começa abaixo do diretório raiz do código-fonte, e o nome do arquivo corresponde à classe pública. Alterar uma importação em outro lugar não corrigiria a estrutura do código-fonte. - 5ExemploFacilite o uso do nome do tipo
A interface tem sua própria declaração e depois importa o tipo de domínio:
package com.example.parcels.ui; import com.example.parcels.domain.Parcel;O corpo de sua classe agora pode usar o nome simples
Parcel. A importação se aplica apenas a esse arquivo. Ela resolve um nome; não copia uma classe nem disponibiliza uma dependência para a construção. - 6Ideia-chaveUm curinga se limita a um pacote
import com.example.parcels.*;não inclui o pacote de domínio. O prefixo compartilhado no nome parece uma árvore genealógica, mas cada nome completo de pacote designa um pacote separado.A mesma regra explica por que
java.util.*não fornecejava.util.function.Function. Use uma importação de tipo único, o curinga do pacote adequado ou um nome totalmente qualificado para esse tipo. - 7DesafioResolva o conflito entre duas listas
Um arquivo precisa de
java.util.Listejava.awt.List. Atualmente ele tem importações de tipo único para ambas, e a compilação falha. Como o arquivo pode nomear os dois tipos sem ambiguidade?---reveal---
Mantenha uma importação de tipo único, como
import java.util.List;, e escrevajava.awt.Listpor extenso onde esse tipo for necessário. Duas importações de tipo único diferentes não podem dar ao mesmo nome simples dois significados em um arquivo. - 8DicaReconheça nomes que não precisam de importação
Um arquivo pode usar tipos de seu próprio pacote sem importá-los. Tipos acessíveis de
java.lang, incluindoString,MatheSystem, também estão disponíveis automaticamente.Uma importação estática tem outra função: importar
Assertions.assertEqualsestaticamente permite uma chamada direta aassertEquals(...). Ela encurta uma referência a membro estático, não um nome de pacote. - 9ExemploVerifique também o construtor
O tipo é público, mas seu construtor não tem modificador:
public class Parcel { Parcel(int grams, String recipient) { } }Esse construtor tem acesso de pacote. A interface consegue resolver
Parcelpor meio da importação, mas não pode construí-lo a partir do pacote da interface. Marque o construtor como público se criar encomendas fizer parte das operações públicas pretendidas do domínio; mantenha seus campos privados. - 10DesafioAbra a operação e mantenha a classe auxiliar oculta
A interface precisa chamar o método
feeInCentsde uma calculadora pública, atualmente declarado sem modificador de acesso. Apenas a calculadora usa sua classe auxiliarWeightBands, que tem acesso de pacote. Qual alteração de acesso é necessária?---reveal---
Torne
feeInCentspúblico e deixeWeightBandscom acesso de pacote. A interface precisa da operação de cálculo, não da classe auxiliar da tabela. Importar qualquer uma das classes não altera seus direitos de acesso. - 11ExplicaçãoUma subclasse tem privilégios específicos
Membros protegidos são acessíveis a todo o pacote que os declara e a subclasses segundo as regras de herança entre pacotes. Em outro pacote, uma subclasse pode usar seu próprio membro de instância herdado.
Ela não pode usar uma referência declarada com o tipo da superclasse para acessar esse membro em um objeto arbitrário da superclasse. Portanto, o acesso protegido é mais amplo que o acesso exclusivo de subclasses, mas mais restrito que o acesso público.
- 12DesafioDiagnostique todo o caminho de acesso
TextUIimporta corretamente umaParcelpública. Sua chamada anew Parcel(800, "Jo")informa que o construtor não pode ser acessado de fora do pacote. O construtor não tem modificador. O que corrige essa criação pretendida entre pacotes?---reveal---
Torne o construtor público. O pacote e a importação já resolvem o tipo, portanto mover o arquivo ou adicionar uma importação com curinga não resolve a falha de acesso informada. Os campos privados da encomenda podem continuar privados.
- 13RecapitulaçãoUse três verificações separadas
Verifique a localização comparando a declaração com as pastas abaixo do diretório raiz do código-fonte. Verifique a nomenclatura por meio de importações ou nomes totalmente qualificados. Verifique o acesso ao tipo e à operação que quem chama realmente usa.
Essa ordem ajuda a transformar um problema vago de “Java não consegue usar esta classe” em uma correção específica da estrutura do código-fonte, da resolução de nomes ou da visibilidade.
2Interprete um modelo de encomendas e construa seu esqueleto Java
13 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoLeia a estrutura antes de escrever os corpos
- 2ExemploTraduza um campo e um método
- 3ExplicaçãoIdentifique um membro da classe
- 4DesafioPreserve os símbolos
- 5ExemploConte encomendas a partir de uma remessa
- 6ExemploEscolha uma representação e imponha a regra
- 7Ideia-chaveSiga uma ligação em uma só direção
- 8DesafioVerifique uma quantidade mínima
- 9ExplicaçãoColoque a propriedade na extremidade correta
- 10ExemploLeia duas linhas com triângulo
- 11DicaConstrua as declarações em ordem
- 12DesafioCombine um contrato e uma coleção
- 13RecapitulaçãoMantenha distinta a função de cada símbolo
3Acompanhe um comando por responsabilidades separadas
11 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoAcompanhe um comando pelo programa
- 2ExemploExtraia a regra do código de console
- 3ExemploCalcule um bloco iniciado
- 4Ideia-chaveMantenha a validação junto da regra
- 5DesafioTorne um cálculo reutilizável
- 6ExemploPasse o armazenamento ao serviço
- 7ConexãoSubstitua arquivos por uma implementação simplificada funcional
- 8DicaVerifique chamadas e importações separadamente
- 9DesafioCorrija um atalho que contorna o serviço
- 10DesafioConfigure um teste de serviço controlado
- 11RecapitulaçãoColoque cada alteração onde sua tarefa pertence
4Desenvolva um conjunto útil de testes Jupiter
19 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoTransforme requisitos em verificações executáveis
- 2ExemploPrepare, aja e verifique
- 3ExplicaçãoInterprete corretamente o esperado e o real
- 4DicaPermita o arredondamento numérico de forma deliberada
- 5DesafioCorrija um teste que apenas exibe o resultado
- 6ExemploEncontre as mudanças de tarifa
- 7Ideia-chaveMantenha o resultado esperado independente
- 8ExemploColoque a chamada rejeitada dentro da verificação
- 9DesafioEscolha o caso que revela a alteração
- 10ExemploDê a cada teste uma nova remessa
- 11ExemploExecute uma tabela com um único corpo de teste
- 12ConexãoControle o colaborador, não a ordem dos testes
- 13DesafioDiagnostique dados deixados por outro teste
- 14ExemploComece o ciclo com um ano comum
- 15ExemploAdicione a regra geral de divisibilidade por quatro
- 16ExemploUse casos de anos de século para refinar a condição
- 17DesafioEscolha o próximo passo preservando as verificações
- 18DesafioCombine seleção de casos e isolamento
- 19RecapitulaçãoConfie em evidências específicas e repetíveis
5Leve um projeto do arquivo de construção ao relatório de testes
13 passos
Salve esta aula para seguir cada Lição Interativa passo a passo.
- 1IntroduçãoFaça um comando repetir a construção
- 2ExplicaçãoSepare arquivos-fonte da saída gerada
- 3ExemploNomeie o projeto e sua biblioteca de teste
- 4ExemploSolicite a fase necessária
- 5DicaNão confunda zero testes com comportamento verificado
- 6DesafioEncontre um teste na pasta errada
- 7ExplicaçãoSelecione as tarefas Gradle
- 8ExemploMantenha os módulos Jupiter alinhados
- 9ExemploInforme à tarefa de teste como executar Jupiter
- 10DesafioCorrija a descoberta de testes
- 11ExemploAcompanhe a falha de um teste executado
- 12DesafioCombine a descoberta com uma nova construção
- 13RecapitulaçãoInterprete a construção como evidência em etapas
Questões de Pacotes e testes unitários em Java
Conjunto 1: Nomes, pastas e acesso em Java10 Questões de Prática
Questão 1 de 10
Ticket declara package org.example.events.model;. Qual caminho ela deve ter abaixo do diretório raiz do código-fonte Java principal?Mostrar respostaOcultar resposta
Ticket.java. O diretório raiz já é o ponto de partida, portanto adicionar src/main/java novamente duplicaria a parte do caminho relativa à estrutura de construção.Questão 2 de 10
shop.ui precisa de uma classe auxiliar que atualmente não tem declaração de pacote. Como a classe auxiliar deve ser organizada para que a interface possa importá-la?Mostrar respostaOcultar resposta
Questão 3 de 10
import java.util.*; e usa java.util.function.Predicate por seu nome simples. Qual importação adicional de tipo único fornece esse nome?Mostrar respostaOcultar resposta
Predicate está no pacote separado java.util.function, portanto seu nome completo de tipo é necessário na importação de tipo único. O curinga existente java.util.* não alcança subpacotes, e uma importação estática é usada para membros estáticos.Questão 4 de 10
java.util.* e java.awt.* e depois declara List<String> names. Como ele pode fazer List referir-se à interface de coleção mantendo as duas importações com curinga?Mostrar respostaOcultar resposta
List como java.util.List. As duas importações com curinga podem permanecer; uma importação estática não identificaria esse tipo de nível superior como membro estático.Questão 5 de 10
assertTrue(valid) sem o prefixo da classe Assertions. Qual importação disponibiliza essa chamada direta?Mostrar respostaOcultar resposta
assertTrue é um membro estático de Assertions, portanto uma importação estática permite chamá-lo diretamente. Importar apenas a classe permite Assertions.assertTrue(...), mas não remove o nome da classe proprietária da chamada.Questão 6 de 10
book.data.Record é pública e tem um construtor declarado como Record(int id). Uma classe sem relação em book.ui a importa, mas não consegue construí-la. Qual alteração de acesso permite essa chamada?Mostrar respostaOcultar resposta
book.ui é um pacote diferente. O acesso público permite essa chamada por uma classe sem relação; o acesso protegido não permitiria, pois quem chama não está no pacote que declara o construtor nem usa privilégios de subclasse.Questão 7 de 10
class RateTable não tem modificador de acesso. Onde outras classes de nível superior podem usá-la?Mostrar respostaOcultar resposta
public tem acesso de pacote, o que restringe seu uso ao mesmo pacote exato. Importações resolvem nomes sem conceder permissão, portanto uma importação explícita de outro pacote não pode abrir o acesso a essa classe auxiliar.Questão 8 de 10
# threshold: int e um + reset(): void sublinhado. Como esses membros são traduzidos para Java?Mostrar respostaOcultar resposta
# indica visibilidade protegida, enquanto o sublinhado marca um membro estático. O sinal de mais torna reset público e seu tipo de retorno é void; o sublinhado não significa abstrato, que usa itálico ou uma marcação de abstrato.Questão 9 de 10
public boolean accepts(int grams)?Mostrar respostaOcultar resposta
Questão 10 de 10
base tem a classe Engine com acesso de pacote. Seu construtor declarado explicitamente e seu método run() não têm modificador de acesso. Uma Screen sem relação em base.ui precisa chamar new Engine().run(). Quais são as alterações mínimas de visibilidade que permitem essa chamada?Mostrar respostaOcultar resposta
base.ui é um pacote separado, portanto os três precisam de acesso público para essa classe sem relação. Abrir apenas o tipo e o método ainda deixa a construção bloqueada.Conjunto 2: Relações entre classes e responsabilidades em camadas10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
0..* está escrito ao lado de Book. A quantos livros uma biblioteca pode estar ligada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
List<Session> começa vazio, e nenhum método verifica seu tamanho. O que falta na implementação?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
Printable. Qual declaração Java de subtipo essa linha representa?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Basket contém private List<Product> products; e não estende Product. Como a relação principal deve ser representada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
CatalogRepository. Como um teste pode evitar o banco de dados real?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
rules.OrderService importa storage.OrderRepository. Essa interface nomeia rules.Order em suas assinaturas, e implementações de armazenamento a implementam. Como o contrato pode ser realocado para remover o ciclo entre os pacotes rules e storage, mantendo o armazenamento intercambiável?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 3: Asserções, seleção de casos e testes independentes10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
actual, e seu resultado exigido é 42. Qual asserção de igualdade do Jupiter identifica corretamente os valores?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
double difere de 0,6 em cerca de 0,0000000000000001. A precisão numérica exigida é 0,000001. Como seu resultado esperado deve ser verificado?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
reading <= 800 para reading < 800. Qual leitura válida revela o erro?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
IllegalArgumentException. Como um teste Jupiter deve executar a chamada?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
250 + ((grams - 3000) / 500) * 50 acima do limite. Qual novo teste revela mais diretamente seu defeito de arredondamento?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 4: Configuração de construção e relatórios de falhas10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
TotalsChecks.java importa org.junit.Test. Os padrões de inclusão do Surefire não foram alterados. Qual conjunto de correções permite que a classe seja compilada e executada como um teste Jupiter descoberto?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
platform(...) do Gradle. Por que as declarações de dependências JUnit relacionadas podem omitir números de versão individuais?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
org.junit.jupiter.api está ausente. Sua única declaração do Jupiter é testRuntimeOnly("org.junit.jupiter:junit-jupiter"), e a tarefa de teste já usa a JUnit Platform. Qual correção de configuração resolve essa etapa?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Conjunto 5: Diagnostique uma alteração Java completa10 Questões de Prática
Salve esta aula para ver todas as respostas e praticar cada conjunto.
Questão 1 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 2 de 10
1 ao lado de Department e 0..* ao lado de Employee. O que o modelo permite para um departamento e exige para um funcionário?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 3 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 4 de 10
base.Item tem um campo de instância protegido weight. Em outro pacote, Special extends Item tem um método de instância com os parâmetros Item a e Special b. Dentro desse método, quais expressões podem acessar diretamente o campo herdado?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 5 de 10
MemoryArchive realizando a interface Archive e mantendo um ou mais objetos Entry por meio de uma associação navegável. Uma classe gerada atualmente declara extends Archive e cria uma lista vazia sem verificações. Qual conjunto de correções preserva o modelo?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 6 de 10
Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 7 de 10
year % 4 == 0 e passa nos testes de 2031 e 2032. Um novo teste para 2100 deve rejeitar esse ano, e um teste para 2400 deve aceitá-lo posteriormente. Qual sequência de alterações de comportamento atende aos casos pendentes?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 8 de 10
< 2500 para a faixa inferior. Qual par adicional de verificações revela os dois defeitos?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 9 de 10
testRuntimeOnly("org.junit.jupiter:junit-jupiter:6.1.3") e tasks.test { }. Uma classe de teste Jupiter está armazenada em src/main/java, e sua anotação não compila. Quais alterações permitem que a classe seja compilada e executada no Jupiter?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Questão 10 de 10
target. O que esse arquivo comprova sobre o código-fonte de hoje?Mostrar respostaOcultar resposta
Salve esta aula para ver esta resposta.
Travou em uma questão sua? Ajuda com Tarefas
Termos-Chave
A
Acesso de pacote
Acesso protegido
Agregação
Agrupamento por pacote
Asserção
Asserção de exceção
assertThrows pode verificar se um peso zero é rejeitado e retornar a exceção para uma verificação de mensagem.Associação
parcels na extremidade da encomenda.Atributo
- weightGrams: int representa um campo inteiro privado que armazena o peso da encomenda.C
Camada
Camada de acesso a dados
Camada de apresentação
Caso de valor-limite
Ciclo de dependências entre pacotes
Ciclo de vida da construção
Classe de equivalência
Composição
Configuração de dependência
Conflito de nomes
List pode importar um e usar o nome totalmente qualificado do outro.Conjunto de dados e objetos de teste
Contrato da interface
save por meio do contrato do repositório, seja o armazenamento em memória ou em arquivo.Coordenadas do artefato
D
Declaração de importação
Scanner pode usar o nome simples em sua declaração de campo em vez de java.util.Scanner.Declaração de pacote
package com.example.parcels.domain; torna esse pacote parte do nome completo da classe.Dependência
Parcel cria uma dependência da definição do tipo de encomenda.Dependência transitiva
Desenvolvimento orientado por testes
Diagrama de classes UML
Diretório raiz do código-fonte
src/main/java para código de produção em uma construção padrão.parcels.domain.Parcel, o caminho abaixo do diretório raiz do código-fonte é parcels/domain/Parcel.java.E
Escopo da dependência
src/test/java sem torná-lo uma dependência do código principal.Expressão lambda
() -> calculator.feeInCents(0) permite que JUnit execute a chamada inválida dentro de sua asserção.F
Ferramenta de construção
G
Generalização
Letter em direção a Item traduz-se em uma declaração Java na qual Letter estende Item.I
Implementação simplificada para testes
Importação com curinga
java.util.* ainda deixa java.util.function.Function precisando de sua própria importação ou nome completo.Importação de tipo único
import java.util.ArrayList; identifica exatamente qual ArrayList o arquivo usa.Importação estática
assertEquals estaticamente para que cada verificação seja lida como uma chamada direta de asserção.Independência dos testes
Inicializador da ferramenta de construção
./mvnw test.Injeção de dependência
Interface do usuário
Interface pública do pacote
J
JUnit Jupiter
@Test do Jupiter mantém a anotação de teste consistente com o mecanismo selecionado pela construção.JUnit Platform
useJUnitPlatform() quando os testes do projeto usam anotações Jupiter.L
Lista de materiais
Lista de testes
Lógica da aplicação
M
Membro estático
Método de ciclo de vida
@BeforeEach recria objetos que mudam, enquanto @AfterEach pode remover recursos temporários de teste.Modificador de acesso
Montagem dos objetos
Multiplicidade
1..* ao lado de Parcel exige que cada remessa mantenha pelo menos uma encomenda ligada.N
Navegabilidade
Nome totalmente qualificado
java.util.List distingue a interface de coleção de outro tipo também chamado List.O
Operação
+ totalWeightGrams(): int descreve um método público sem fornecer seu algoritmo.P
Pacote
Pacote sem nome
Plugin
Preparar–agir–verificar
R
Realização
Refatoração
Regressão
Repositório
Resultado esperado
S
Separação de responsabilidades
Símbolo de visibilidade
+ public, - private, # protected ou ~ acesso de pacote.- deve manter a visibilidade privada quando o diagrama for traduzido para Java.Superclasse
Surefire
ShippingCalculatorTest corresponde a um padrão de inclusão padrão do Surefire.T
Tarefa do Gradle
Teste instável
Teste parametrizado
Teste unitário
Tolerância
V
Vermelho–verde–refatorar
- 1Primeiros programas em Java
- 2Condições e laços em Java
- 3Métodos em Java
- 4Listas e vetores em Java
- 5Classes e objetos em Java
- 6Objetos e referências em Java
- 7HashMaps e coleções em Java
- 8Herança, interfaces e polimorfismo em Java
- 9Exceções em Java
- 10Pacotes e testes unitários em Java
- 11Genéricos e expressões lambda em Java
Sobre esta aula
Crie seus próprios Materiais de Estudo
Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.