Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Testes para desenvolvimento colaborativo

Palestra sobre testes para o FISL 11
by

Rodrigo Damazio

on 24 July 2010

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Testes para desenvolvimento colaborativo

Testes para desenvolvimento
colaborativo Rodrigo Damazio Princípios Metodologia Tipos de
Testes Código
testável Boas
práticas Ferramentas ? Teste: Provê entradas
Verifica saídas int soma(int a, int b) {
return a + b;
} void testeSoma() {
int soma = soma(2, 3);
assertEquals(5, soma);
} Caso mais real: class Database {
private static final Connection CONN = DB.openConnection();

Resultado buscaResultado() {
Query q =
new Query("SELECT * FROM ....");
return CONN.execute(q);
}
} Cobertura: 100% ? Bom, mas não suficiente
Impossível aplicar todas as possíveis entradas float inclinacao(float x1, float y1, float x2, float y2) {
return (y2-y1) / (x2-x1);
} Coberta, mas ainda
pode falhar! Cobertura de testes: Linhas de código executadas pelo teste
(supostamente, "testadas"). int divide(int a, int b) {
if (b == 0) {
throw new BadBadServerException();
}
return a / b;
} void testDivide() {
assertEquals(4, divide(20, 5));
} Não testada! Cobertura: 66% Tipos de testes Manual
Estático
Unitário
Regressão
Integração
UI / Frontend
Concorrência
Carga
Segurança
Compatibilidade Teste unitário Testa uma única unidade do código
(e.g. uma classe) de forma ISOLADA
Leve / rápido
Testa uma única unidade do código que
tem dependências "pesadas":
Inicia threads
Lê/grava arquivos
Utiliza a rede

Em geral, infraestrutura comum que isola
essas dependências.
Teste unitário grande Testa várias unidades juntas para
garantir que interoperam como esperado.

Médios:
Testam a interação entre algumas unidades
(e.g. 4-5 classes).

Grandes:
Rodam o(s) programa(s) completo(s). Código intestável? Código FACILMENTE testável class Database {
private final Connection conn =
criaConexao();

Resultado buscaResultado() {
Query q =
new Query("SELECT * FROM ....");
return conn.execute(q);
}

protected Connection criaConexao() {
return DB.openConnection()
}
} Refactoring 1: class Database {
private final Connection conn;

public Database(Connection conn) {
this.conn = conn;
}

Resultado buscaResultado() {
Query q =
new Query("SELECT * FROM ....");
return conn.execute(q);
}
} Refactoring 2: class TestableDatabase extends Database {
@Override
protected Connection criaConexao() {
return fakeConnection;
}
}

void testBuscaresultado() {
fakeConnection.setData(data);
Database db = new TestableDatabase();
Resultado res = db.buscaResultado();
assertEquals(x, res);
} Teste: Solução 1:
Criar DB real, popular com conteúdo, alterar estado de DB para apontar para ele, executar código, verificar resultado
Lento, trabalhoso
Teste de integração

Solução 2:
Criar DB falso que aceita o mesmo tipo de conexão
Ainda mais trabalhoso Teste: Teste: void testBuscaresultado() {
fakeConnection.setData(data);
Database db = new Database(fakeConnection);
Resultado res = db.buscaResultado();
assertEquals(x, res);
} Fatores para testabilidade Estado global
Mixing of concerns
Conclusão Todo código é testável
Alguns mais facilmente
Refactoring
Projetos Open Source Muitos colaboradores
Nem todos conhecem o sistema todo
Mudança "inocente" pode causar caos
Pior em projetos grandes (GNOME, Android, Kernel, etc.) Por que testar? Garantir a funcionalidade e qualidade do código
Permitem alterar a implementação e garantir que a funcionalidade ou outros aspectos são mantidos Programador 1 escreve código+testes
(testes passam) Testes falham Programador 2 modifica código A Testes passam Código é submetido Em um projeto fechado e pequeno Todos conhecem o código
Testes protegem contra enganos/acidentes Workflow de integração Contribuição é recebida
Revisão manual do código (teste estático)
Patch / pull da mudança
Testes (automáticos e manuais)
submit / push da mudança Teste manual Usar manualmente o sistema
Capaz de detectar grande parte dos erros
Demorado e trabalhoso
Estado global Variáveis globais / estáticas
Singletons
Não-idempotência
Métodos estáticos (em algumas linguagens)
Registry/Directory
ThreadLocal Mixing of concerns Classe tem responsabilidade demais
Construtor faz trabalho
Misturar instanciação e processamento class X {
public:
X(int x) {
if (x==0) {
QuitWithSuccess();
}
}

void QuitWithSuccess() {
delete this;
}

virtual void Done() {
// Do something here.
QuitWithSuccess();
}
private:
A* a;
}; class Y : public X {
public:
Y(int x) : X(x) {}

virtual void Done() {
// Do something else here
QuitWithSuccess();
}
}; Preenche vtable delete this Estado global: class Counter {
private static int x = 0;

static int next() {
return x++;
}
} class Counter {
private int x = 0;

int next() {
return x++;
}
} SEM estado global: class FakeCounter
extends Counter {
@Override
int next() {
return 42;
}
} Counter c = Mockito.mock(Counter.class);
Mockito.when(c.next()).thenReturn(42); Exemplo anterior: void testBuscaResultado() {
Connection c = mock(Connection.class);
Query expectedQuery = new Query("SELECT * FROM bla WHERE ID=1");
when(c.execute(expectedQuery)).thenReturn(data);

Database db = new Database(c);
Resultado res = db.buscaResultado(1);

verify(c).execute(expectedQuery);
verifyNoMoreInteractions(c);
assertEquals(data, res);
}

Mocking: Entradas e saídas do teste passam a ser o comportamento do mock (chamadas feitas/esperadas).
Ideal para testar (uso de) serviços O que NÃO fazer com Mocking: Especificar demais => testes frágeis / falsos negativos
Especificar de menos => não testa realmente / falsos positivos
Mock de objeto de dados - não importa como são lidos String fetchUrlWithParams(String base, Map<String, String> params) {
String url = base + "?";
for (Map.Entry<String, String> param : params.entrySet()) {
url += param.getKey() + "=" + param.getValue() + "&";
}

long oldestCache = clock.now() - MAX_CACHED_TIME;

return fetcher.fetch(url, oldestCache);
} Decoupling - ler URL com parâmetros: Exemplo - ler URL com parâmetros, teste 1: void testFetchUrlWithParams() {
Map<String, String> params = mock(Map.class);
Set<Map.Entry<String, String>> paramSet = mock(Set.class);
when(params.entrySet()).thenReturn(entrySet);
Iterator<Map.Entry<String, String>> it = mock(Iterator.class);
when(paramSet.iterator()).thenReturn(iterator);
when(iterator.hasNext()).thenReturn(true);
when(iterator.next()).thenReturn(entry1);
when(iterator.hasNext()).thenReturn(true);
when(iterator.next()).thenReturn(entry2);
when(iterator.hasNext()).thenReturn(false);
...
Clock clock = mock(Clock.class);
when(clock.now()).thenReturn(10000L);
when(fetcher.fetch("...", 8000L)).thenReturn("ble");

String result = fetchUrlWithParams("base", params);
assertEquals("ble", result);
verify(clock).now();
verifyNoMoreInteractions(clock);
} Exemplo - ler URL com parâmetros, teste 2: void testFetchUrlWithParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("a", "1");
params.put("b", "2");

Clock clock = mock(Clock.class);
when(clock.now()).thenReturn(10000L);
when(fetcher.fetch("...", 8000L)).thenReturn("ble");

String result = fetchUrlWithParams("base", params);
assertEquals("ble", result);
verify(clock).now();
verifyNoMoreInteractions(clock);
} Implementação problemática: String fetchUrlWithParams(String base, Map<String, String> params) {
String url = base + "?";
for (String paramName : params.keySet()) {
String paramValue = params.get(paramName);
url += paramName + "=" + paramValue + "&";
}

long oldestCache = clock.now() - MAX_CACHED_TIME;

return fetcher.fetch(url, oldestCache);
} Implementação problemática String fetchUrlWithParams(String base, Map<String, String> params) {
String url = base + "?";
for (Map.Entry<String, String> param : params.entrySet()) {
url += param.getKey() + "=" + param.getValue() + "&";
}

System.out.println("Fetching at " + clock.now());
long oldestCache = clock.now() - MAX_CACHED_TIME;

return fetcher.fetch(url, oldestCache);
} void testFetchUrlWithParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("a", "1");
params.put("b", "2");

Clock clock = mock(Clock.class);
when(clock.now()).thenReturn(10000L);
when(fetcher.fetch("...", 8000L)).thenReturn("ble");

String result = fetchUrlWithParams("base", params);
assertEquals("ble", result);
} void testFetchUrlWithParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("a", "1");
params.put("b", "2");

Clock clock = mock(Clock.class);
when(clock.now()).thenReturn(10000L);
when(fetcher.fetch("...", 8000L)).thenReturn("ble");

String result = fetchUrlWithParams("base", params);
assertEquals("ble", result);
verify(fetcher).fetch("...", 8000L);
verifyNoMoreInteractions(fetcher);
} String fetchUrlWithParams(String base, Map<String, String> params) {
String url = base + "?";
for (Map.Entry<String, String> param : params.entrySet()) {
url += param.getKey() + "=" + param.getValue() + "&";
}

long oldestCache = clock.now() - MAX_CACHED_TIME;

System.out.println("Contents are: " + fetcher.fetch(url, oldestCache));
return fetcher.fetch(url, oldestCache);
}
Implementação problemática Exemplo - ler URL com parâmetros, teste 5: new dentro da classe
impede injeção de mocks
exceção: objetos de dados Injeção de mocks/dependências Toda dependência é recebida de fora da classe
e.g. construtor
Funciona na teoria... ...na prática: Injeção automática de dependências (Guice/Spring): class Crawler {
@Inject
Crawler(UrlFetcher fetcher) { ... }
}

class UrlFetcher {
@Inject
UrlFetcher(Clock clock){ ... }
}

@Singleton
class Clock {
...
}
Injeção automática de dependências (Guice/Spring): Sem chamadas new (exceto no teste)
Injector p/ objeto top-level
Efetivamente abstrai instanciação da lógica
Guice "se vira" para saber como criar uma instância
Default = 1 instância por injeção
@Singleton, @RequestScoped, módulo, ...
if? class UrlFetcher {
@Inject
UrlFetcher(Clock clock,
Provider<URLConnection> connProvider) {
this.clock = clock;
this.connProvider = connProvider;
}

String fetchUrlWithParams(...) {
String url = ...;
URLConnection conn = connProvider.get();
return conn.get(url);
}
} Injeção lazy: long tempoAtras(long atras) {
long agora = System.currentTimeMillis();
return agora + atras;
}

void testTempoAtras() {
long agora = System.currentTimeMillis();
long antes = tempoAtras(1000);
assertEquals(agora + 1000, antes);
} String buildUrlWithParams(String base, Map<String, String> params) {
String url = base + "?";
for (Map.Entry<String, String> param : params.entrySet()) {
url += param.getKey() + "=" + param.getValue() + "&";
}

return url;
} void testFetchUrlWithParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("a", "1");
params.put("b", "2");

String url = buildUrlWithParams("bla", params);
assertEquals("bla?a=1&b=2", url);
} String fetchUrl(String url) {
long oldestCache = clock.now() - MAX_CACHED_TIME;

return fetcher.fetch(url, oldestCache);
} void testFetchUrlWithParams() {
Clock clock = mock(Clock.class);
when(clock.now()).thenReturn(10000L);
when(fetcher.fetch("url", 8000L)).thenReturn("ble");

String result = fetchUrlWithParams("url");
assertEquals("ble", result);
verify(fetcher).fetch("...", 8000L);
verifyNoMoreInteractions(fetcher);
} Teste de regressão Teste (geralmente pequeno) que verifica que um bug
anterior foi removido (escrito antes de removê-lo). Teste de integração Capazes de exibir a interface gráfica do
aplicativo e injetar interações nela,
vendo como o sistema se comporta.

Podem ter a interface conectada ao sistema
completo (integração com UI) ou a
componentes falsos. String fetchUrlWithParams(String base, Map<String, String> params) {
String url = base + "?";
for (Map.Entry<String, String> param : params.entrySet()) {
url += param.getKey() + "=" + param.getValue() + "&";
}

long oldestCache = clock.now() - MAX_CACHED_TIME;

return fetcher.fetch(url, oldestCache);
} Ler URL com parâmetros: Teste também e codigo:
Como garantir a funcionalidade do teste?
Como medir a qualidade dos testes? Ferramentas p/ code coverage Java: EMMA, Quilt, Jcoverage, ...
C++: COVTOOL, GTC, ?
Python: coverage.py, figleaf, ... Workflow de desenvolvimento (TDD) Mudança/adição e necessária
Modifica-se testes antigos se necessário
Adiciona-se testes para novas funcionalidades ou aspectos
Codigo para a mudança é escrito
Garante-se que todos os testes passam
Envia-se a contribuição Workflow de desenvolvimento Mudança/adição e necessária
Codigo para a mudança e escrito
Garante-se que testes antigos passam (modifica-se o teste apenas se necessário)
Adiciona-se testes para novas funcionalidades ou aspectos
Garante-se que todos os testes passam
Envia-se a contribuição Workflow de bugfix Bug é detectado (não pêgo pelos testes)
Adiciona-se teste(s) que falham por existir o bug
Corrige-se o bug
Garante-se que todos os testes passam
Envia-se a contribuição Continuous build Mudança é submetida (ou recebida)
CB automaticamente compila e roda todos os testes com a mudança
Alerta (e-mail/pager/etc) quando o build "quebra" Ferramentas p/ unit tests Java: JUnit
C++: CppUnit, Unit++, GoogleTest, ...
Python: pyunit
Outros: gUnit (GTK), Android Instrumentation, JSUnit (Javascript), ... Selenium / Webdriver (web)
Android instrumentation
GUITAR
Abbot
... Ferramentas p/ UI testing Ferramentas p/ continuous building CruiseControl
Apache Continuum
DamageControl
Hudson
Ivy
... Guice
Snake Guice (Guice p/ Python)
GIN (Guice p/ GWT)
RoboGuice (Guice p/ Android)
Spring (Java, Python)
... Ferramentas p/ dependency injection Java: Mockito, Easymock, JMock, ...
C++: googlemock, ?
Python: Mox, pMock, Dingus, Mocker, ... Ferramentas p/ mocking Rodrigo Damazio

rdamazio@google.com

Slides:
http://bit.ly/testing_fisl Pixy
XSSer
XSS Rays
... Ferramentas p/ testes de segurança Testes de concorrência Testes com o único objetivo de proteger
contra condições de corrida.

Podem ser tanto no nível de uma classe ou algoritmo, como em uma parte maior do sistema. Testam um sistema completo (geralmente
um servidor) com a maior quantidade de
requisições possível para determinar:
Uso de recursos (CPU, RAM, etc.)
Quantidade máxima de requisições/segundo
Latência / contenção
Reação à sobrecarga Testes de carga Executa os mesmos testes em diversos
sistemas operacionais ou configurações
para garantir que o código tem o mesmo
efeito em todos.

Comum em código de infraestrutura, que
isola chamadas de sistema. Testes de compatibilidade Qual tipo de teste escrever? TODOS! Testes pequenos para serem executados com freqüência
Testes maiores para serem executados periodicamente / antes de releases
Teste de UI / Frontend Testes de segurança Exemplo - ler URL com parâmetros, teste 3: Instanciação no lugar errado: Flakiness: Repete lógica
dentro do teste
(deveria ser -, mas o teste passa!) Vão retornar o mesmo valor?
(deveria injetar
clock falso no teste) Ler manualmente o codigo
Necessário, mas trabalhoso e lento

Teste estático Ferramentas p/ testes de concorrencia thread weaver
Full transcript