Desenvolvimentoorientado a testesDenis Ferrari
SobreDenis FerrariGraduandoemSistemas de informaçãopelaFAESA;Arquiteto de software;Ministrapalestras e treinamentos;Consultor de negócios;MCP, MCTS (Web Applications e Distributed Applications), MCPD (Web Applications);EscreveartigosparaosportaisImasters, Linha de código e osdisponibilizatambémemseu blog.9 anos de experiência no mercado de TI capixabaatuandocomoInstrutor, Web Master, DesenvolvedorSênior e Gerente de Projetos;
ObjetivosdapalestraApresentar a metodologiaágile seusvalores.Apresentar o desenvolvimentoorientado a testescomoumaalternativaaodesenvolvimentotradicional.
MetodologiaÁgil
MetodologiaÁgilDesenvolvimento ágil de software (do inglês Agile software development) ou Método ágil é um conjunto de metodologias de desenvolvimento de software. O desenvolvimento ágil, tal como qualquer metodologia de software, providencia uma estrutura conceitual para reger projetos de engenharia de software.http://pt.wikipedia.org/wiki/Desenvolvimento_%C3%A1gil_de_software
HistóricoComeçou em um encontro em Fevereiro de 2001, Utah, USA.  com os seguintes representantes:  Extreme Programming, SCRUM, DSDM,   Adaptive Software Development, Crystal, Feature-DrivenDevelopment, Pragmatic Programming, e outros;O objetivo do encontro foi discutir alternativas ao rigoroso processo de   documentação necessário para o processo de desenvolvimento de software;Deu origem ao "Manifesto Ágil" assinado pelos dezessete participantes do encontro.
ConceitosDEFINIÇÃO:Movimento iniciado por programadores experientes e consultoresem desenvolvimento de software. PRINCÍPIOS:Indivíduos e interações X processos e ferramentas;Software funcional X documentação abrangente;Colaboração com o cliente X negociação de contratos;Adaptação a mudanças X seguir plano inicial.http://agilemanifesto.org/
Assinaram o manifesto:Kent Beck, Mike Beedle, Arie van Bennekum, AlistairCockburn,WardCunningham, Martin Fowler, James Grenning, Jim Highsmith,Andrew Hunt, Roland Jeffries, Jon Kern, Brian Marick, Robert C. Martin, Steve Mellor, Ken Schwaber, Jeff Sutherland; Dave Thomas.
TDD
TDD é uma técnica de desenvolvimento de software cujo processo é formado por pequenas iterações para o desenvolvimento de uma nova funcionalidade, começando pela implementação de um caso de teste, depois pelo código necessário para fazer o teste passar, e finalmente pela refatoração do código visando melhor acomodar as mudanças feitas.Não é um método para testar software, mas para construir software.TDD vem do conceito de “test-first programming” do XP (Extreme Programming), mas acabou ganhando tanto interesse, que hoje tem sido adotado independente do XP e das técnicas de programação ágil;Introdução ao TDD (1/2)
Objetivo do TDD: código limpo que funciona“Mantra” do TDD: vermelho-verde-refatorarCodifique o teste;Faça ele compilar e executar (não deve passar - vermelho);Implemente o requisito e faça o teste passar (verde);Refatore o código;Introdução ao TDD (2/2)
Garante a existência de testes unitários completos e atualizados, que:Eliminam o medo de alterarmos alguma coisa que funciona (testada manualmente), e acabarmos introduzindo algum problema;Nos permite utilizar refatoração (substituir uma implementação por outra equivalente) de forma muito mais agressiva devido à facilidade dos testes verificarem o resultado.Diminui a quantidade de erros por linha de código (código-fonte de mais qualidade)Testes unitários servem como especificação de como os componentes do sistema funcionam;Nos leva a produzir componentes de software mais desacoplados, para garantir o isolamento dos testes, o que acaba favorecendo o projeto do sistema.Principais Benefícios do TDD
Testes de Unidade;Testes de Integração;Testes de Sistema;Testes de Integração de Sistema;Testes de Aceitação;Tipos de Testes
A Metodologia TDD é conduzida através dos “testes do programador”.Frequentemente esses testes são chamados de “teste de unidade”, mas esse nem sempre é o caso (pode ser teste de integração).É importante destacar que não se trata dos testes de sistema (caixa preta) nem os de aceitação (feitos pelo usuário final)Tipos de Testes
A “Espiral da Morte” do TesteO ciclo mortal do “estou sem tempo para testar”:
1. Construa testes isolados uns dos outrosUm caso de teste não deve depender do sucesso de outro para funcionar;Deve ser possível executar um caso de testes isoladamente, sem executar nenhum outro;2. Comece definindo uma “TestList”De modo geral para uma mesma classe ou método a ser testado, existirão diferentes casos de teste. Liste-os primeiro (brain-storm);Provavelmente ao longo do desenvolvimento você adicionará novos casos de teste à lista;Princípios
Mas o que testamos?Fluxos Condicionais (IFs, Switches, etc.)PolimorfismosLoopsOperaçõesEtc..Princípios
Exemplo de “Lista de Testes” para o caso de uso “Realizar Transferência Bancária”:Realizar uma transferência normal (bem sucedida);Tentar transferir uma quantidade superior ao saldo da conta de origem;Verificar atomicidade no caso de falha de sistema antes de concluir a operação;Conta de origem inativa;Conta de destino inativa;Valor transferido menor que zero;Princípios
Exemplo de “Lista de Testes” para o caso de uso “Matricular Aluno em Disciplina”:Matricular com sucesso;Tentar matricular sem atender disciplinas pré-requisito;Tentar matricular sem atender pré-requisito de crédito;Tentar matricular com conflito de horário;Tentar matricular sem vaga;Tentar matricular em disciplina já cumprida;Tentar matricular em disciplina que já está matriculada (outra turma);Tentar matricular em disciplina que já está matriculada (mesma turma);Princípios
3. Primeiro o  TesteOportunidade para pensar no design (projeto) das classesControlar o escopo do que será implementado – somente o necessário para atender o teste corrente.4. Primeiro a AssertivaÉ melhor pensarmos no que significa o sucesso do caso de teste antes de pensarmos no restante:Vou precisar de criar um novo método?Em qual classe?Qual será o nome dele e seus parâmetros?“Primeiro a assertiva” está para o caso de teste assim como “Primeiro o teste” está para o caso de uso;Princípios
Exemplo: Testar o envio da string “Teste” através de um socket.PrincípiospublicvoidTestarComunicacaoSocket()        {Assert.IsTrue(readerSocket.Close());Assert.AreEqual(“Qualidata”, buf);        }publicvoidTestarComunicacaoSocket()        {                string buf = reader.Contents();Assert.IsTrue(readerSocket.Close());Assert.AreEqual(“Qualidata”, buf);        }publicvoidTestarComunicacaoSocket()        {SocketreaderSocket = newSocket(“localhost”, 8080);                string buf = reader.Contents();Assert.IsTrue(readerSocket.Close());Assert.AreEqual(“Qualidata”, buf);        }publicvoidTestarComunicacaoSocket()        {Serverserver = newServer(8080, “Teste”);SocketreaderSocket = newSocket(“localhost”, 8080);                string buf = reader.Contents();Assert.IsTrue(readerSocket.Close());Assert.AreEqual(“Qualidata”, buf);        }
5. Dados para TesteNão escolha números mágicos se eles não tiverem um significado específico no teste. Por exemplo, se não faz diferença utilizar “1”, “2” ou “1365”, preferia  “1”. Evite passar o mesmo valor para diferentes parâmetros. Por exemplo, para testar um método Operacao(int x, int y), não utilize Operacao(2,2), pois “Operacao” pode inverter “x” e “y” e o teste ainda assim passar. Prefira, por exemplo, Operacao(2,3).Dê preferência a dados do mundo real, especialmente quando você tem algum sistema legado com dados que podem ser aproveitadosPrincípios
6. Dados com Significado EvidenteLembre que está escrevendo um teste para alguém ler, e não somente para ser executado pelo computador.Tente escrever na assertiva expressões que não só representem o valor final esperado, mas o que eles significam.Exemplo:Princípios        [Test]publicvoid Testar_Fatorial_8()        {Assert.AreEqual(40320, Matematica.Fatorial(8));        } [Test]publicvoid Testar_Fatorial_8()        {Assert.AreEqual(8 * 7 * 6 * 5 * 4 * 3 * 2, Matematica.Fatorial(8));        }
Trata sobre quando escrever, onde escrever e quando parar de escrever testes.Qual o próximo teste da lista a implementar?Escolha um teste que você esteja confiante que pode implementá-lo facilmente;Não comece pelo teste mais realístico (completo), pois você será forçado a implementar um monte de coisas para atender o teste.Siga na direção “mais conhecidos” para “pouco conhecidos”Cada iteração “red/green/refactor” deve levar apenas alguns minutos.Exercício: Se você estiver construindo uma lista encadeada, qual seria seu primeiro caso de teste?“Red Bar Patterns”
Testes de EstudoDevemos escrever testes para componentes ou bibliotecas de terceiros que, supostamente, funcionam?Sim. Para estudar e documentar seu uso.Esse é uma forma de assegurar-se sobre como utilizar tal biblioteca de forma a obter os resultados esperados e documentar seu uso (considerando especificamente suas necessidades, e não todos os recursos da biblioteca).“Red Bar Patterns”
Testes de Outras FuncionalidadesSempre que uma discussão desviar do assunto principal e entrar em outras questões, essa pode ser uma hora de adicionar novos casos de teste à sua lista (para a funcionalidade em questão) e voltar para o assunto principal.“Red Bar Patterns”
Defeitos ReportadosSempre que um defeito é reportado nossa primeira ação deve ser escrever um caso de teste que reproduza o problema. Se isso for feito certamente o problema será resolvido.Testes de RegressãoConsiste em testar novamente uma funcionalidade que foi implementada e testada anteriormente;Teoricamente, após qualquer alteração no sistema, todo o sistema deveria ser testado para garantir que essa alteração não tenha introduzido algum efeito colateral indesejável.Uma boa prática é executar testes de regressão de todo o sistema, todos os dias, à noite, e reportar os problemas encontrados para a equipe. Existem softwares para gerenciar isso.“Red Bar Patterns”
Técnicas mais detalhadas sobre como escrever testes.Subdividir TestesQuando um teste ficou grande demais e você está demorando muito para conseguir implementá-lo (mais de 10 min já deve te encomodar), pode ser melhor dividir o teste em testes menores até conseguir fazer o teste maior passar.MockObjectsComo testar objetos que dependem de recursos externos custosos ou complexos, como bancos de dados e webservices, por exemplo?Crie uma versão falsa ou simulada do objeto (“fake”) que retorne valores constantes.Referência: www.mockobjects.comFerramentas para .NET: NMock e RhinoMocks.“TestingPatterns”
Uma vez que você tenha um teste vermelho, busque torná-lo verde o quanto antes.Use os padrões que serão discutidos para conseguir isso rapidamente, mesmo que o resultado seja algo que você não aceita conviver nem por uma hora.“Green Bar Patterns”
“Fake it ‘til youmake it” (simule até construir realmente)Por exemplo, qual é a forma mais simples de implementar o código abaixo?A forma mas direta seria:“Green Bar Patterns”publicvoidTestarSoma() {Assert.AreEqual(5, Somar(2,3));     }publicvoidTestarSoma() {Assert.AreEqual(5, Somar(2,3));     }publicint Somar(int x, int y) {return 5;     }
TriangulaçãoQuando você tem dois ou mais testes, você precisa de uma implementação que atenda a todos simultaneamente.Agora a solução trivial (constante) não atende mais,  e somos forçados e implementar algo mais abrangente.É claro que esse exemplo é apenas ilustrativo. Se você já tem uma implementação correta óbvia, então implemente. Senão, “fake it”.“Green Bar Patterns”Página 31publicvoidTestarSoma()      {Assert.AreEqual(5, Somar(2,3));Assert.AreEqual(7, Somar(3,4));     }
Implementação ÓbviaSe você consegue implementar algo diretamente, então implemente. Para que utilizar “fake” ou triangulação?Mas quando você achar que sabe implementar e se deparar com uma barra vermelha? Depois você descobre, errei aqui, e aí outra barra vermelha! Talvez seja hora de dividir o problema em problemas menores.Acreditar que você sempre vai conseguir acertar e escrever o código mais simples para o problema diretamente é exigir de você perfeição.“TestingPatterns”
InformaçõesparacontatoE-mail/MSN: denisferrari@live.comGtalk: denis.sisinf@gmail.comSite: www.denisferrari.comBlog: desenvolvimento.denisferrari.comTwitter: @denisferrari

TDD (Resumo)

  • 1.
  • 2.
    SobreDenis FerrariGraduandoemSistemas deinformaçãopelaFAESA;Arquiteto de software;Ministrapalestras e treinamentos;Consultor de negócios;MCP, MCTS (Web Applications e Distributed Applications), MCPD (Web Applications);EscreveartigosparaosportaisImasters, Linha de código e osdisponibilizatambémemseu blog.9 anos de experiência no mercado de TI capixabaatuandocomoInstrutor, Web Master, DesenvolvedorSênior e Gerente de Projetos;
  • 3.
    ObjetivosdapalestraApresentar a metodologiaágileseusvalores.Apresentar o desenvolvimentoorientado a testescomoumaalternativaaodesenvolvimentotradicional.
  • 4.
  • 5.
    MetodologiaÁgilDesenvolvimento ágil desoftware (do inglês Agile software development) ou Método ágil é um conjunto de metodologias de desenvolvimento de software. O desenvolvimento ágil, tal como qualquer metodologia de software, providencia uma estrutura conceitual para reger projetos de engenharia de software.http://pt.wikipedia.org/wiki/Desenvolvimento_%C3%A1gil_de_software
  • 6.
    HistóricoComeçou em umencontro em Fevereiro de 2001, Utah, USA. com os seguintes representantes: Extreme Programming, SCRUM, DSDM, Adaptive Software Development, Crystal, Feature-DrivenDevelopment, Pragmatic Programming, e outros;O objetivo do encontro foi discutir alternativas ao rigoroso processo de documentação necessário para o processo de desenvolvimento de software;Deu origem ao "Manifesto Ágil" assinado pelos dezessete participantes do encontro.
  • 7.
    ConceitosDEFINIÇÃO:Movimento iniciado porprogramadores experientes e consultoresem desenvolvimento de software. PRINCÍPIOS:Indivíduos e interações X processos e ferramentas;Software funcional X documentação abrangente;Colaboração com o cliente X negociação de contratos;Adaptação a mudanças X seguir plano inicial.http://agilemanifesto.org/
  • 8.
    Assinaram o manifesto:KentBeck, Mike Beedle, Arie van Bennekum, AlistairCockburn,WardCunningham, Martin Fowler, James Grenning, Jim Highsmith,Andrew Hunt, Roland Jeffries, Jon Kern, Brian Marick, Robert C. Martin, Steve Mellor, Ken Schwaber, Jeff Sutherland; Dave Thomas.
  • 9.
  • 10.
    TDD é umatécnica de desenvolvimento de software cujo processo é formado por pequenas iterações para o desenvolvimento de uma nova funcionalidade, começando pela implementação de um caso de teste, depois pelo código necessário para fazer o teste passar, e finalmente pela refatoração do código visando melhor acomodar as mudanças feitas.Não é um método para testar software, mas para construir software.TDD vem do conceito de “test-first programming” do XP (Extreme Programming), mas acabou ganhando tanto interesse, que hoje tem sido adotado independente do XP e das técnicas de programação ágil;Introdução ao TDD (1/2)
  • 11.
    Objetivo do TDD:código limpo que funciona“Mantra” do TDD: vermelho-verde-refatorarCodifique o teste;Faça ele compilar e executar (não deve passar - vermelho);Implemente o requisito e faça o teste passar (verde);Refatore o código;Introdução ao TDD (2/2)
  • 12.
    Garante a existênciade testes unitários completos e atualizados, que:Eliminam o medo de alterarmos alguma coisa que funciona (testada manualmente), e acabarmos introduzindo algum problema;Nos permite utilizar refatoração (substituir uma implementação por outra equivalente) de forma muito mais agressiva devido à facilidade dos testes verificarem o resultado.Diminui a quantidade de erros por linha de código (código-fonte de mais qualidade)Testes unitários servem como especificação de como os componentes do sistema funcionam;Nos leva a produzir componentes de software mais desacoplados, para garantir o isolamento dos testes, o que acaba favorecendo o projeto do sistema.Principais Benefícios do TDD
  • 13.
    Testes de Unidade;Testesde Integração;Testes de Sistema;Testes de Integração de Sistema;Testes de Aceitação;Tipos de Testes
  • 14.
    A Metodologia TDDé conduzida através dos “testes do programador”.Frequentemente esses testes são chamados de “teste de unidade”, mas esse nem sempre é o caso (pode ser teste de integração).É importante destacar que não se trata dos testes de sistema (caixa preta) nem os de aceitação (feitos pelo usuário final)Tipos de Testes
  • 15.
    A “Espiral daMorte” do TesteO ciclo mortal do “estou sem tempo para testar”:
  • 16.
    1. Construa testesisolados uns dos outrosUm caso de teste não deve depender do sucesso de outro para funcionar;Deve ser possível executar um caso de testes isoladamente, sem executar nenhum outro;2. Comece definindo uma “TestList”De modo geral para uma mesma classe ou método a ser testado, existirão diferentes casos de teste. Liste-os primeiro (brain-storm);Provavelmente ao longo do desenvolvimento você adicionará novos casos de teste à lista;Princípios
  • 17.
    Mas o quetestamos?Fluxos Condicionais (IFs, Switches, etc.)PolimorfismosLoopsOperaçõesEtc..Princípios
  • 18.
    Exemplo de “Listade Testes” para o caso de uso “Realizar Transferência Bancária”:Realizar uma transferência normal (bem sucedida);Tentar transferir uma quantidade superior ao saldo da conta de origem;Verificar atomicidade no caso de falha de sistema antes de concluir a operação;Conta de origem inativa;Conta de destino inativa;Valor transferido menor que zero;Princípios
  • 19.
    Exemplo de “Listade Testes” para o caso de uso “Matricular Aluno em Disciplina”:Matricular com sucesso;Tentar matricular sem atender disciplinas pré-requisito;Tentar matricular sem atender pré-requisito de crédito;Tentar matricular com conflito de horário;Tentar matricular sem vaga;Tentar matricular em disciplina já cumprida;Tentar matricular em disciplina que já está matriculada (outra turma);Tentar matricular em disciplina que já está matriculada (mesma turma);Princípios
  • 20.
    3. Primeiro o TesteOportunidade para pensar no design (projeto) das classesControlar o escopo do que será implementado – somente o necessário para atender o teste corrente.4. Primeiro a AssertivaÉ melhor pensarmos no que significa o sucesso do caso de teste antes de pensarmos no restante:Vou precisar de criar um novo método?Em qual classe?Qual será o nome dele e seus parâmetros?“Primeiro a assertiva” está para o caso de teste assim como “Primeiro o teste” está para o caso de uso;Princípios
  • 21.
    Exemplo: Testar oenvio da string “Teste” através de um socket.PrincípiospublicvoidTestarComunicacaoSocket() {Assert.IsTrue(readerSocket.Close());Assert.AreEqual(“Qualidata”, buf); }publicvoidTestarComunicacaoSocket() { string buf = reader.Contents();Assert.IsTrue(readerSocket.Close());Assert.AreEqual(“Qualidata”, buf); }publicvoidTestarComunicacaoSocket() {SocketreaderSocket = newSocket(“localhost”, 8080); string buf = reader.Contents();Assert.IsTrue(readerSocket.Close());Assert.AreEqual(“Qualidata”, buf); }publicvoidTestarComunicacaoSocket() {Serverserver = newServer(8080, “Teste”);SocketreaderSocket = newSocket(“localhost”, 8080); string buf = reader.Contents();Assert.IsTrue(readerSocket.Close());Assert.AreEqual(“Qualidata”, buf); }
  • 22.
    5. Dados paraTesteNão escolha números mágicos se eles não tiverem um significado específico no teste. Por exemplo, se não faz diferença utilizar “1”, “2” ou “1365”, preferia “1”. Evite passar o mesmo valor para diferentes parâmetros. Por exemplo, para testar um método Operacao(int x, int y), não utilize Operacao(2,2), pois “Operacao” pode inverter “x” e “y” e o teste ainda assim passar. Prefira, por exemplo, Operacao(2,3).Dê preferência a dados do mundo real, especialmente quando você tem algum sistema legado com dados que podem ser aproveitadosPrincípios
  • 23.
    6. Dados comSignificado EvidenteLembre que está escrevendo um teste para alguém ler, e não somente para ser executado pelo computador.Tente escrever na assertiva expressões que não só representem o valor final esperado, mas o que eles significam.Exemplo:Princípios [Test]publicvoid Testar_Fatorial_8() {Assert.AreEqual(40320, Matematica.Fatorial(8)); } [Test]publicvoid Testar_Fatorial_8() {Assert.AreEqual(8 * 7 * 6 * 5 * 4 * 3 * 2, Matematica.Fatorial(8)); }
  • 24.
    Trata sobre quandoescrever, onde escrever e quando parar de escrever testes.Qual o próximo teste da lista a implementar?Escolha um teste que você esteja confiante que pode implementá-lo facilmente;Não comece pelo teste mais realístico (completo), pois você será forçado a implementar um monte de coisas para atender o teste.Siga na direção “mais conhecidos” para “pouco conhecidos”Cada iteração “red/green/refactor” deve levar apenas alguns minutos.Exercício: Se você estiver construindo uma lista encadeada, qual seria seu primeiro caso de teste?“Red Bar Patterns”
  • 25.
    Testes de EstudoDevemosescrever testes para componentes ou bibliotecas de terceiros que, supostamente, funcionam?Sim. Para estudar e documentar seu uso.Esse é uma forma de assegurar-se sobre como utilizar tal biblioteca de forma a obter os resultados esperados e documentar seu uso (considerando especificamente suas necessidades, e não todos os recursos da biblioteca).“Red Bar Patterns”
  • 26.
    Testes de OutrasFuncionalidadesSempre que uma discussão desviar do assunto principal e entrar em outras questões, essa pode ser uma hora de adicionar novos casos de teste à sua lista (para a funcionalidade em questão) e voltar para o assunto principal.“Red Bar Patterns”
  • 27.
    Defeitos ReportadosSempre queum defeito é reportado nossa primeira ação deve ser escrever um caso de teste que reproduza o problema. Se isso for feito certamente o problema será resolvido.Testes de RegressãoConsiste em testar novamente uma funcionalidade que foi implementada e testada anteriormente;Teoricamente, após qualquer alteração no sistema, todo o sistema deveria ser testado para garantir que essa alteração não tenha introduzido algum efeito colateral indesejável.Uma boa prática é executar testes de regressão de todo o sistema, todos os dias, à noite, e reportar os problemas encontrados para a equipe. Existem softwares para gerenciar isso.“Red Bar Patterns”
  • 28.
    Técnicas mais detalhadassobre como escrever testes.Subdividir TestesQuando um teste ficou grande demais e você está demorando muito para conseguir implementá-lo (mais de 10 min já deve te encomodar), pode ser melhor dividir o teste em testes menores até conseguir fazer o teste maior passar.MockObjectsComo testar objetos que dependem de recursos externos custosos ou complexos, como bancos de dados e webservices, por exemplo?Crie uma versão falsa ou simulada do objeto (“fake”) que retorne valores constantes.Referência: www.mockobjects.comFerramentas para .NET: NMock e RhinoMocks.“TestingPatterns”
  • 29.
    Uma vez quevocê tenha um teste vermelho, busque torná-lo verde o quanto antes.Use os padrões que serão discutidos para conseguir isso rapidamente, mesmo que o resultado seja algo que você não aceita conviver nem por uma hora.“Green Bar Patterns”
  • 30.
    “Fake it ‘tilyoumake it” (simule até construir realmente)Por exemplo, qual é a forma mais simples de implementar o código abaixo?A forma mas direta seria:“Green Bar Patterns”publicvoidTestarSoma() {Assert.AreEqual(5, Somar(2,3)); }publicvoidTestarSoma() {Assert.AreEqual(5, Somar(2,3)); }publicint Somar(int x, int y) {return 5; }
  • 31.
    TriangulaçãoQuando você temdois ou mais testes, você precisa de uma implementação que atenda a todos simultaneamente.Agora a solução trivial (constante) não atende mais, e somos forçados e implementar algo mais abrangente.É claro que esse exemplo é apenas ilustrativo. Se você já tem uma implementação correta óbvia, então implemente. Senão, “fake it”.“Green Bar Patterns”Página 31publicvoidTestarSoma() {Assert.AreEqual(5, Somar(2,3));Assert.AreEqual(7, Somar(3,4)); }
  • 32.
    Implementação ÓbviaSe vocêconsegue implementar algo diretamente, então implemente. Para que utilizar “fake” ou triangulação?Mas quando você achar que sabe implementar e se deparar com uma barra vermelha? Depois você descobre, errei aqui, e aí outra barra vermelha! Talvez seja hora de dividir o problema em problemas menores.Acreditar que você sempre vai conseguir acertar e escrever o código mais simples para o problema diretamente é exigir de você perfeição.“TestingPatterns”
  • 34.
    InformaçõesparacontatoE-mail/MSN: denisferrari@live.comGtalk: denis.sisinf@gmail.comSite:www.denisferrari.comBlog: desenvolvimento.denisferrari.comTwitter: @denisferrari