testing sucks

Post on 17-May-2015

937 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

Presentation given in BrazilJS 2012

TRANSCRIPT

Testing SucksLeo Balter - BrazilJS 2012 JS

Testar é chato!

Testes vs Testar!

Testes humanos• Repetitivos

• Estressantes

• Cansativos

Concentração polarizada

• Nos concentramos em um único ponto

• Não vemos os demais

• falsa sensação de segurança

Olhar Viciado

Testes de Trollagem

Pare de “ficar testando” seu código

JSHint/JSLint não testa código!

• Convenção de estilo

• Erros comuns

• “As Boas Partes”

Mito do Compilador

Pânico vs Benefícios

• Muito fácil criar testes para algo que ainda não existe

• Não tão simples quando a aplicação já existe e está “no ar”.

Aplicação não testada

A Síndrome do “o que está

funcionando não se deve mexer”.

Quebre a aplicação!• Criamos testes simples que passem

• Teste o que está em produção

• Cobertura de testes

• O que é crítico?

• Especificação

• O que falta testar?

• Testes de Trollagem

Acredite:Sua aplicação vai falhar!

Já viu essa síndrome?

• Não vai ao médico pra evitar descobrir um mal grave

• Pode haver um problema latente que vai falhar em um momento crítico

• Pode acabar em morte

Na aplicação

• Não testamos para não descobrir o que falha

• Há problemas latentes que vão falhar em momentos críticos (Por exemplo: um sistema de pagamento do seu e-commerce)

• Sua empresa morre (vai a falência)

Estabeleça o que é crítico

• Quão crítico é cada parte do seu sistema?

• Se não funcionar, quais as consequências?

Impacto

Ocorrência

Baixo

Baixo Alto

Alto

Risco de baixo nível

Risco de nível médio

Risco Crítico

http://bit.ly/11tTwNGerenciamento de Risco

Automatização

• --Retrabalho

• --Surpresas

• Mais tempo para o que é interessante

• Menos tempo para testar tudo novamente

Automatização

• --Retrabalho

• --Surpresas

• Mais tempo para o que é interessante

• Menos tempo para testar tudo novamente

Metodologias Ágeis

• Aplicar testes é uma rotina contida em Metodologias Ágeis

• Testes não dependem de metodologias ágeis

• Ex.: jQuery

Funciona

Faz o que deveria

Esse código funciona!

• Um código pode funcionar perfeitamente

• Estar 100% testado

• e não cumprir 10% do seu papel

Fluxo

Especificação

Fluxo

Especificação

Plano de Testes

Fluxo

Especificação

Plano de Testes

TestesFluxo

Validação

Plano de Testes

Validação

Especificação

Plano de Testes

Validação

Especificação

Plano de Testes Testes

Validação

Especificação

Plano de Testes Testes

Aplicação

Validação

Testes não validam especificação!

Testes não criam especificação!

Testes vs Especificação

• Se os testes validassem especificação, não poderiamos prever correções de bugs e comportamentos de ambientes.

• Bugs não fazem parte de uma especificação.

Baby Steps (Testes Unitários)

Teste que Falha Código Refatorar

Sempre em Baby Steps

• Ótimo para pegar o ritmo

• função 1, 2, 3, pin

• Treino do Lutador

Coberturade

Código

function foo( bar ) { if ( bar ) { console.log( 'yes!' ); else { console.log( 'no!' ); }}

function foo( bar ) { if ( bar ) { console.log( 'yes!' ); else { console.log( 'no!' ); }}

function foo( bar ) { if ( bar ) { console.log( 'yes!' ); else { console.log( 'no!' ); }}

function foo( bar ) { if ( bar ) { console.log( 'yes!' ); else { console.log( 'no!' ); }}

function foo( bar ) { if ( bar ) { console.log( 'yes!' ); else { console.log( 'no!' ); }}

function foo( bar ) { if ( bar ) { console.log( 'yes!' ); else { console.log( 'no!' ); }}

Código 100% cobertopor testes!

• JSCoverage

• CoverJS

Tudo é testável

• Complexidade vs Criticidade

• window.location.replace()

Testes em JavaScript

JS

Testes

Unitários

FuncionaisInterface

IntegraçãoDesempenho

Open Web

vários ambientes

Chrome

Firefox

Opera

IE 8, 9[, 10, ...]

Chrome

Firefox

Opera

IE 8, 9[, 10, ...]

Mobile

PhantomJS

Webkit

Ferramentas

QUnit

JasmineMocha

NodeUnit

Vows

Estilos de Testes

TDD BDD Exports

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

module( "Basics" );

test( "hello test", function() {  ok( 1 == "1", "Passed!" );});

http://qunitjs.com/

TDD

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

describe("A suite", function() { it("spec with an expectation",

function(){ expect(true).toBe(true); });

});

BDD

describe("A suite", function() { it("spec with an expectation",

function(){ expect(true).toBe(true); });

});

BDD

describe("A suite", function() { it("spec with an expectation",

function(){ expect(true).toBe(true); });

});

BDD

describe("A suite", function() { it("spec with an expectation",

function(){ expect(true).toBe(true); });

});

BDD

describe("A suite", function() { it("spec with an expectation",

function(){ expect(true).toBe(true); });

});

BDD

describe("A suite", function() { it("spec with an expectation",

function(){ expect(true).toBe(true); });

});

BDD

Exports

module.exports = { test1: function (test) { test.equals(this.foo, 'bar'); test.done(); }};

Exports

module.exports = { test1: function (test) { test.equals(this.foo, 'bar'); test.done(); }};

Exports

module.exports = { test1: function (test) { test.equals(this.foo, 'bar'); test.done(); }};

Exports

module.exports = { test1: function (test) { test.equals(this.foo, 'bar'); test.done(); }};

Exports

module.exports = { test1: function (test) { test.equals(this.foo, 'bar'); test.done(); }};

Como começar?

HTML Estático

JavaScript

Testes

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <div id="qunit-fixtures"> <div id="foo">bar</div> </div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <title>QUnit Example</title>  <link rel="stylesheet" href="/t/qunit.css"></head><body>  <div id="qunit"></div>  <div id="qunit-fixtures"> <div id="foo">bar</div> </div>  <script src="/t/qunit.js"></script> <script src="/js/script.js"></script>  <script src="/t/unit/tests.js"></script></body></html>

Código testado tem bom desempenho?

• Testes unitários não medem desempenho

• Existem testes de desempenho

• Código sem erros pode evitar lentidão

DexterJS

Dexter.spy

http://jsbin.com/osijuq/7/edit

function carly( crazy ) { if ( typeof ( crazy ) === 'number' ) { return 'Call me maybe?'; } else { return 'Hey, I just met you!, and this is ' + crazy; }}

function meSad( maybe ) { return carly( maybe ? 5555555 : 'crazy!' );}

http://jsbin.com/osijuq/7/edit

function carly( crazy ) { if ( typeof ( crazy ) === 'number' ) { return 'Call me maybe?'; } else { return 'Hey, I just met you!, and this is ' + crazy; }}

function meSad( maybe ) { return carly( maybe ? 5555555 : 'crazy!' );}

http://jsbin.com/osijuq/7/edit

function carly( crazy ) { if ( typeof ( crazy ) === 'number' ) { return 'Call me maybe?'; } else { return 'Hey, I just met you!, and this is ' + crazy; }}

function meSad( maybe ) { return carly( maybe ? 5555555 : 'crazy!' );}

http://jsbin.com/osijuq/7/edit

function carly( crazy ) { if ( typeof ( crazy ) === 'number' ) { return 'Call me maybe?'; } else { return 'Hey, I just met you!, and this is ' + crazy; }}

function meSad( maybe ) { return carly( maybe ? 5555555 : 'crazy!' );}

http://jsbin.com/osijuq/7/edit

function carly( crazy ) { if ( typeof ( crazy ) === 'number' ) { return 'Call me maybe?'; } else { return 'Hey, I just met you!, and this is ' + crazy; }}

function meSad( maybe ) { return carly( maybe ? 5555555 : 'crazy!' );}

http://jsbin.com/osijuq/7/edit

function carly( crazy ) { if ( typeof ( crazy ) === 'number' ) { return 'Call me maybe?'; } else { return 'Hey, I just met you!, and this is ' + crazy; }}

function meSad( maybe ) { return carly( maybe ? 5555555 : 'crazy!' );}

http://jsbin.com/osijuq/7/edit

function carly( crazy ) { if ( typeof ( crazy ) === 'number' ) { return 'Call me maybe?'; } else { return 'Hey, I just met you!, and this is ' + crazy; }}

function meSad( maybe ) { return carly( maybe ? 5555555 : 'crazy!' );}

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

test( 'carly()', 4, function() {

var spy = Dexter.spy( window, 'carly', function( arg1 ) {

equal( arg1, 'crazy!', 'me sad got crazy!' );

});

meSad( false );

equal( spy.called, 1, 'carly was called once' );

spy.callback = 0;

equal( meSad( true ), 'Call me maybe?', 'keep asking' );

equal( spy.called, 2, 'carly called again!' );

spy.restore();

});

Dexter.stub

function carly( crazy ) { if ( typeof ( crazy ) === 'number' ) { return 'Call me maybe?'; } else { return 'Hey, I just met you!, and this is ' + crazy; }}

function meSad( maybe ) { return carly( maybe ? 5555555 : 'crazy!' );}

test( 'stubbed carly()', 1, function() { var stub = Dexter.stub( window, 'carly', function() { return 'relax'; }); equal( meSad( true ), 'relax', 'stubbed carly!' ); stub.restore();});

test( 'stubbed carly()', 1, function() { var stub = Dexter.stub( window, 'carly', function() { return 'relax'; }); equal( meSad( true ), 'relax', 'stubbed carly!' ); stub.restore();});

test( 'stubbed carly()', 1, function() { var stub = Dexter.stub( window, 'carly', function() { return 'relax'; }); equal( meSad( true ), 'relax', 'stubbed carly!' ); stub.restore();});

test( 'stubbed carly()', 1, function() { var stub = Dexter.stub( window, 'carly', function() { return 'relax'; }); equal( meSad( true ), 'relax', 'stubbed carly!' ); stub.restore();});

test( 'stubbed carly()', 1, function() { var stub = Dexter.stub( window, 'carly', function() { return 'relax'; }); equal( meSad( true ), 'relax', 'stubbed carly!' ); stub.restore();});

test( 'stubbed carly()', 1, function() { var stub = Dexter.stub( window, 'carly', function() { return 'relax'; }); equal( meSad( true ), 'relax', 'stubbed carly!' ); stub.restore();});

test( 'stubbed carly()', 1, function() { var stub = Dexter.stub( window, 'carly', function() { return 'relax'; }); equal( meSad( true ), 'relax', 'stubbed carly!' ); stub.restore();});

Dexter.fakeXHR

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

test( 'a fakeXHR', 2, function() { var fakeXHR = Dexter.fakeXHR(); $.get( '/ajax.url', function() { ok( true, 'ajax completed' ); });

equal( fakeXHR.requests.length, 1, 'requests === 1' );

fakeXHR.respond({ body : 'this is the ajax returned text', headers : { foo2 : 'bar2' }, status : 200 });});

Começar a criar testes?

Experimente!

• Coding Dojo

• Inicialize um projeto com o GruntJS

• TDD - Kent Back

• Engenharia de Software - Roger S Pressman

• Testable JavaScript - Mark Ethan Trostler *

Referências

Obrigado!

• @garu_rj

• @blabos

• @aoqfonseca

JS@leobalterleo@balter.com.br

http://search.cpan.org/~leobalter/

top related