33
loading...
This website collects cookies to deliver better user experience
Há uma tensão natural entre o fornecedor de uma interface e seu usuário.
Os fornecedores de pacotes (aqui no sentido de módulos) e frameworks de outros fabricantes visam a uma maior aplicabilidade de modo que possam trabalhar com diversos ambientes e atender a um público maior.
Já os usuários desejam uma interface voltada para suas próprias necessidades.
Essa tensão pode causar problemas nos limites de nossos sistemas.
Exemplo: java.util.Map
, é uma interface bastante ampla com diversas capacidades. O que nos dá flexibilidade, isso é útil, mas também pode ser uma desvantagem:
Map
e passá-lo adiante.Map
não exclua nada do Map
. Mas logo no início da lista dos métodos do Map
está o método clear()
. E assim qualquer usuário do Map
pode apagá-lo.Map
pode armazenar apenas certos tipos de objetos, mas ele não restringe os tipos que podemos armazenar, assim qualquer usuário pode adicionar itens de qualquer tipo.sensors
:
Se temos um Map
de sensors
da seguinte forma:
Map sensors = new HashMap();
Sensor s = (Sensor) sensors.get(sensorId);
Object
do Map
e atribuí-lo ao tipo certo. Apesar de funcionar, não é um código limpo. E esse código não explica muito bem o que ele faz. Map<Sensor> sensors = new HashMap<Sensor>();
…
Sensor s = sensors.get(sensorId );
Map
deveria ter a chave e o tipo do valor.Map<Sensor>
oferece mais capacidade do que precisamos ou queremos.Map<Sensor>
significa que haverá vários lugares para mexer se a interface Map
mudar. Map
:public class Sensors {
private Map sensors = new HashMap();
public Sensor getById(String id) {
return (Sensor) sensors.get(id);
}
//snip
}
Não estamos sugerindo para sempre usar dessa forma.
Mas é bom não passar os Maps
ou qualquer outra interface num limite por todo o sistema.
Se for usar, mantenha numa classe ou próxima a uma família de classes em que ela possa ser usada. Evite retorná-la ou aceitá-la como parâmetro de APIs públicas.
Códigos de terceiros nos ajudam a obter mais funcionalidade em menos tempo.
Não é tarefa nossa testá-los, mas pode ser melhor para nós criar testes para os códigos externos que formos usar.
Isso quando não é claro como usar uma biblioteca de terceiros. Podemos gastar um tempo lendo a documentação e decidindo como usá-la. E então escreveremos nosso código para usar o código externo e vemos que não é o que achávamos. E poderíamos passar muito tempo depurando o código e tentando saber se o bug é no nosso código ou no deles.
Entender códigos de terceiros é difícil. Integrá-lo ao seu também é. Fazer ambos ao mesmo tempo dobra a dificuldade, e se adotássemos outra abordagem?
Se criarmos testes para explorar nosso conhecimento sobre ele. O que o Jim Newkirk chama isso de testes de aprendizagem.
Nesses testes, chamamos a API do código externo como faríamos ao usá-la em nosso aplicativo. Assim estaríamos controlando os experimentos que verificam nosso conhecimento daquela API.
O teste foca no que desejamos saber sobre a API.
log4j
do Apache, baixaremos o pacote e abriremos a documentação inicial.@Test
public void testLogCreate() {
Logger logger = Logger.getLogger("MyLogger");
logger.info("hello");
}
Appender
. Após ler um pouco mais, descobrimos o ConsoleAppender
. Então criamos o ConsoleAppender
:
@Test
public void testLogAddAppender() {
Logger logger = Logger.getLogger("MyLogger");
ConsoleAppender appender = new ConsoleAppender();
logger.addAppender(appender);
logger.info("hello");
}
Appender
não possui fluxo de saída. Depois de pesquisar tentamos o seguinte:
@Test
public void testLogAddAppender() {
Logger logger = Logger.getLogger("MyLogger");
logger.removeAllAppenders();
logger.addAppender(new ConsoleAppender(
new PatternLayout("%p %t %m%n"), ConsoleAppender.SYSTEM_OUT));
logger.info("hello");
}
ConsoleAppender
o que ele precisa escrever no console, e mais interessante é quando removemos o parâmetro ConsoleAppender.SYSTEM_OUT
e ainda é exibido “hello”. Mas quando retiramos o PatternLayout
temos a mensagem de falta de fluxo de saída.
Lendo a documentação novamente, vimos que o construtor ConsoleAppender
padrão vem “desconfigurado”, o que não parece óbvio ou prático. Parece um bug. Lendo mais detalhadamente sobre o pacote chegamos a seguinte série de testes de unidade:
public class LogTest {
private Logger logger;
@Before
public void initialize() {
logger = Logger.getLogger("logger");
logger.removeAllAppenders();
Logger.getRootLogger().removeAllAppenders();
}
@Test
public void basicLogger() {
BasicConfigurator.configure();
logger.info("basicLogger");
}
@Test
public void addAppenderWithStream() {
logger.addAppender(new ConsoleAppender(new PatternLayout("%p %t %m%n"), ConsoleAppender.SYSTEM_OUT));
logger.info("addAppenderWithStream");
}
@Test
public void addAppenderWithoutStream() {
logger.addAppender(new ConsoleAppender(new PatternLayout("%p %t %m%n")));
logger.info("addAppenderWithoutStream");
}
}
log4j
.Esses testes acabam não custando nada. Porque tivemos que aprender sobre a API mesmo, e escrever eles foi uma forma fácil de aprender.
“Os testes de aprendizagem são experimentos precisos que ajudam a aumentar nosso entendimento”.
Esses testes também geram um retorno positivo. Quando houver novas versões daquele pacote, podemos executar os testes para ver se há diferenças nas atividades.
Cada distribuição vem com um novo risco. Porém com os testes de aprendizagem podemos saber se o pacote ficou incompatível com os testes.
Também pode ser mais fácil a atualização com os testes, assim podemos ir para novas versões sem grandes problemas desde que os testes continuem passando.
33