Sou um desenvolvedor de software relativamente novo e uma das coisas que acho que devo melhorar é a minha capacidade de testar meu próprio código. Sempre que desenvolvo uma nova funcionalidade, acho realmente difícil seguir todos os caminhos possíveis para encontrar bugs. Eu costumo seguir o caminho onde tudo funciona. Sei que esse é um problema bem conhecido dos programadores, mas não temos testadores no meu atual empregador e meus colegas parecem ser muito bons nisso.
Na minha organização, não realizamos desenvolvimento orientado a testes nem testes de unidade. Isso me ajudaria muito, mas não é provável que isso mude.
O que vocês acham que eu poderia fazer para superar isso? Qual abordagem você usa ao testar seu próprio código?
Respostas:
O trabalho de um codificador é construir coisas.
O trabalho de um testador é quebrar as coisas.
O mais difícil é quebrar as coisas que você acabou de construir. Você terá sucesso apenas ultrapassando essa barreira psicológica.
fonte
Franciso, vou fazer algumas suposições aqui, com base no que você disse:
"Não fazemos TDD nem testes de unidade. Isso me ajudaria muito, mas não é provável que isso mude."
A partir disso, desconfio que sua equipe não valorize muito os testes ou a gerência não terá um orçamento para tentar organizar o código existente e reduzir ao mínimo a dívida técnica .
Em primeiro lugar, você precisa convencer sua equipe / gerência do valor dos testes. Seja diplomático. Se a gerência estiver mantendo sua equipe em frente, você precisará mostrar alguns fatos, como a taxa de defeitos em cada versão. O tempo gasto na correção de defeitos poderia ser melhor gasto em outras coisas, como melhorar o aplicativo e torná-lo mais adaptável aos requisitos futuros.
Se a equipe e a gerência em geral são apáticas em relação à correção do código e você se sente insatisfeito com isso, pode ser necessário procurar outro local para trabalhar, a menos que possa convencê-los, como eu disse. Encontrei esse problema em diferentes graus em todos os lugares em que trabalhei. Pode ser qualquer coisa, desde a falta de um modelo de domínio adequado até a falta de comunicação na equipe.
Importar-se com o seu código e a qualidade do produto que você desenvolve é um bom atributo e que você sempre deseja incentivar em outras pessoas.
fonte
Se você codificar em C, Objective-C ou C ++, poderá usar o CLang Static Analyzer para criticar sua fonte sem realmente executá-la.
Existem algumas ferramentas de depuração de memória disponíveis: ValGrind, Guard Malloc no Mac OS X, Cerca Elétrica no * NIX.
Alguns ambientes de desenvolvimento oferecem a opção de usar um alocador de memória de depuração, que faz coisas como preencher páginas recém-alocadas e páginas recém-liberadas com lixo, detectar a liberação de ponteiros não alocados e gravar alguns dados antes e depois de cada bloco de heap, com o depurador sendo chamado se o padrão conhecido desses dados mudar.
Um cara do Slashdot disse que ganhou muito valor com a nova linha de fonte de etapa única em um depurador. "É isso aí", ele disse. Eu nem sempre sigo o seu conselho, mas quando o recebi, foi muito útil para mim. Mesmo se você não tiver um caso de teste que estimule um caminho de código incomum, poderá modificar uma variável no depurador para seguir esses caminhos, digamos, alocando um pouco de memória e, em seguida, usando o depurador para definir seu novo ponteiro como NULL em vez de endereço de memória e, em seguida, percorrer o manipulador de falhas de alocação.
Use assertions - a macro assert () em C, C ++ e Objective-C. Se o seu idioma não fornecer uma função de afirmação, escreva um você mesmo.
Use afirmações liberalmente e deixe-as em seu código. Eu chamo assert () "O teste que continua testando". Eu os uso com mais frequência para verificar as pré-condições no ponto de entrada da maioria das minhas funções. Essa é uma parte da "Programação por contrato", incorporada à linguagem de programação Eiffel. A outra parte são pós-condições, ou seja, usando assert () nos pontos de retorno da função, mas acho que não obtenho tanta milhagem quanto as pré-condições.
Você também pode usar assert para verificar invariantes de classe. Embora nenhuma classe seja estritamente obrigada a ter nenhuma invariável, as classes mais sensatas projetadas as possuem. Uma classe invariável é uma condição que sempre é verdadeira, exceto dentro das funções de membro que podem temporariamente colocar seu objeto em um estado inconsistente. Tais funções sempre devem restaurar a consistência antes de retornar.
Assim, cada função membro poderia verificar a invariante ao entrar e sair, e a classe poderia definir uma função chamada CheckInvariant que qualquer outro código poderia chamar a qualquer momento.
Use uma ferramenta de cobertura de código para verificar quais linhas de sua fonte estão realmente sendo testadas e, em seguida, crie testes que estimulem as linhas não testadas. Por exemplo, você pode verificar os manipuladores com pouca memória executando seu aplicativo em uma VM configurada com pouca memória física e nenhum arquivo de troca ou um arquivo muito pequeno.
(Por alguma razão que eu nunca tive conhecimento, embora o BeOS pudesse ser executado sem um arquivo de troca, era altamente instável dessa forma. Dominic Giampaolo, que escreveu o sistema de arquivos BFS, me pediu para nunca executar o BeOS sem troca. Eu não veja por que isso importa, mas deve ter sido algum tipo de artefato de implementação.)
Você também deve testar a resposta do seu código a erros de E / S. Tente armazenar todos os seus arquivos em um compartilhamento de rede e desconecte o cabo de rede enquanto o aplicativo tem uma carga de trabalho alta. Da mesma forma, desconecte o cabo - ou desligue a sua conexão sem fio - se estiver se comunicando pela rede.
Uma coisa que acho particularmente irritante são sites que não possuem código Javascript robusto. As páginas do Facebook carregam dezenas de pequenos arquivos Javascript, mas se algum deles falhar no download, a página inteira é quebrada. Só precisa haver alguma maneira de fornecer alguma tolerância a falhas, digamos, repetindo um download ou fornecer algum tipo de fallback razoável quando alguns de seus scripts não foram baixados.
Tente matar seu aplicativo com o depurador ou com "kill -9" no * NIX enquanto ele estiver no meio da gravação de um arquivo grande e importante. Se o seu aplicativo estiver bem arquitetado, o arquivo inteiro será gravado ou não será gravado, ou talvez seja apenas parcialmente gravado, o que for gravado não será corrompido, com os dados salvos sendo completamente utilizáveis por o aplicativo após a leitura do arquivo.
os bancos de dados sempre têm E / S de disco tolerantes a falhas, mas quase nenhum outro tipo de aplicativo possui. Embora os sistemas de arquivos registrados no diário evitem a corrupção do sistema de arquivos em caso de falha de energia ou falha, eles não fazem nada para impedir a corrupção ou a perda de dados do usuário final. Essa é a responsabilidade dos aplicativos do usuário, mas quase nenhum outro banco de dados implementa tolerância a falhas.
fonte
Quando olho para testar meu código, geralmente passo por uma série de processos de pensamento:
A maneira mais fácil de encontrar isso é desenvolver meus testes junto com meu código. Assim que escrevi mesmo um fragmento de código, gosto de escrever um teste para ele. Tentar fazer todos os testes depois de codificar vários milhares de linhas de código com complexidade de código ciclomático não trivial é um pesadelo. Adicionar um ou mais dois testes após adicionar algumas linhas de código é realmente fácil.
BTW, apenas porque a empresa em que você trabalha e / ou seus colegas não realiza testes de unidade ou TDD, não significa que você não possa experimentá-los, a menos que sejam especificamente proibidos. Talvez usá-los para criar código robusto seja um bom exemplo para outros.
fonte
Além do conselho dado nas outras respostas, eu sugeriria o uso de ferramentas de análise estática (a Wikipedia possui uma lista de várias ferramentas de análise estática para vários idiomas ) para encontrar possíveis defeitos antes do início do teste, além de monitorar algumas métricas relacionadas a a testabilidade do código, como complexidade ciclomática , medidas de complexidade de Halstead e coesão e acoplamento (você pode medi-las com fan-in e fan-out).
Encontrar um código difícil de testar e facilitar o teste facilitará a criação de casos de teste. Além disso, a detecção precoce de defeitos agregará valor a todas as suas práticas de garantia de qualidade (que incluem testes). A partir daqui, familiarizar-se com as ferramentas de teste de unidade e as ferramentas de simulação facilitará a implementação de seus testes.
fonte
Você pode examinar o possível uso das Tabelas da Verdade para ajudá-lo a definir todos os caminhos possíveis no seu código. É impossível considerar todas as possibilidades em funções complexas, mas depois de estabelecer seu tratamento para todos os caminhos conhecidos, você pode estabelecer um tratamento para o caso else.
A maior parte dessa habilidade em particular é aprendida pela experiência. Depois de usar uma certa estrutura por um período significativo de tempo, você começa a ver os padrões e as marcas de comportamento que permitem examinar um pedaço de código e ver onde uma pequena alteração pode causar um erro grave. A única maneira de pensar em aumentar sua aptidão nisso é praticar.
fonte
Se, como você disse, não precisa de teste de unidade, não vejo uma abordagem melhor do que tentar quebrar seu próprio código manualmente.
Tente levar seu código ao limite . Por exemplo, tente passar variáveis para uma função que excede os limites do limite. Você tem uma função que supostamente filtra a entrada do usuário? Tente inserir diferentes combinações de caracteres.
Considere o ponto de vista do usuário . Tente ser um dos usuários que usará seu aplicativo ou biblioteca de funções.
fonte
Seus colegas devem ser verdadeiramente excepcionais para não seguir o TDD ou o teste de unidade e nunca gerar bugs; portanto, em algum nível, duvido que eles não estejam realizando nenhum teste de unidade.
Suponho que seus colegas estão realizando mais testes do que os permitidos, mas, como esse fato não é conhecido pela gerência, a organização sofre como resultado porque a gerência tem a impressão de que testes reais não estão sendo realizados e, portanto, o número de erros é baixo. o teste não é importante e o horário não será agendado para ele.
Converse com seus colegas e tente descobrir que tipo de teste de unidade eles estão fazendo e imite isso. Posteriormente, você pode criar protótipos de maneiras melhores de atributos de teste de unidade e TDD e apresentar lentamente esses conceitos à equipe para facilitar a adoção.
fonte
Você deve conseguir cobertura sobre o que escreve, mesmo que sua organização não tenha cobertura total. Como muitas coisas na programação, a experiência de fazer isso de novo e de novo é uma das melhores maneiras de ser eficiente nisso.
fonte
Além de todos os outros comentários, como você diz que seus colegas são bons em escrever testes que não são felizes, por que não pedir que eles se unam a você para escrever alguns testes.
A melhor maneira de aprender é ver como isso é feito e extrair o que você aprende disso.
fonte
Teste de caixa preta! Você deve criar suas classes / métodos com o teste em mente. Seus testes devem basear-se na especificação do software e devem ser claramente definidos no diagrama de sequência (por meio de casos de uso).
Agora, já que você pode não querer fazer desenvolvimento orientado a testes ...
Coloque validação de entrada em todos os dados recebidos; não confie em ninguém. A estrutura .net lança muitas exceções com base em argumentos inválidos, referências nulas e estados inválidos. Você já deve estar pensando em usar a validação de entrada na camada da interface do usuário, por isso é o mesmo truque no middleware.
Mas você realmente deveria fazer algum tipo de teste automatizado; esse material salva vidas.
fonte
Em minha experiência
A unidade de teste, se não for totalmente automática, é inútil. É mais como um chefe de cabelos pontudos poderia comprar. Por quê ?, porque a Unidade de Teste prometeu economizar tempo (e dinheiro), automatizando alguns processos de teste. Mas, algumas ferramentas da unidade de teste fazem o oposto, forçam os programadores a trabalhar de uma maneira estranha e forçam outros a criar testes de extensão excessiva. Na maioria das vezes, não economiza horas de trabalho, mas aumenta o tempo de mudança do controle de qualidade para o desenvolvedor.
UML é outro desperdício de tempo. um único quadro branco + caneta pode fazer o mesmo, mais barato e rapidamente.
BTW, como ser bom em codificação (e evitar bugs)?
a) atomicidade. Uma função que executa uma simples (ou algumas tarefas únicas). Como é fácil de entender, é fácil acompanhar e é fácil resolvê-lo.
b) Homologia. Se, por exemplo, você chamar um banco de dados usando um procedimento de armazenamento, faça o restante do código.
c) Identifique, reduza e isole o "código do criativo". A maior parte do código é basicamente copiar e colar. O código do criativo é o oposto, um código que é novo e atua como um protótipo, pode falhar. Esse código é propenso a erros de lógica, por isso é importante reduzi-lo, isolá-lo e identificá-lo.
d) Código "Thin ice", é o código que você sabe que é "incorreto" (ou potencialmente perigoso), mas ainda precisa, por exemplo, de código não seguro para um processo de várias tarefas. Evite se puder.
e) Evite o código da caixa preta, isso inclui o código que não é feito por você (por exemplo, framework) e a expressão regular. É fácil perder um bug com esse tipo de código. Por exemplo, trabalhei em um projeto usando o Jboss e encontrei não um erro, mas 10 no Jboss (usando a versão estável mais recente); era uma PITA encontrá-los. Evite especialmente o Hibernate, pois oculta a implementação, daí os erros.
f) adicione comentários no seu código.
g) entrada do usuário como fonte de bugs. identifique-o. Por exemplo, a injeção SQL é causada por uma entrada do usuário.
h) Identifique o elemento defeituoso da equipe e separe a tarefa atribuída. Alguns programadores são propensos a estragar o código.
i) Evite código desnecessário. Se, por exemplo, a classe precisar de Interface , use-a; caso contrário, evite adicionar código irrelevante.
a) eb) são fundamentais. Por exemplo, eu tive um problema com um sistema, quando cliquei em um botão (salvar) que não salvou o formulário. Então eu fiz uma lista de verificação:
E uma nota lateral
fonte
Um testador e um programador enfrentam o problema de diferentes ângulos, mas ambas as funções devem testar completamente a funcionalidade e encontrar bugs. Onde os papéis diferem está em foco. Um testador clássico vê o aplicativo apenas do lado de fora (por exemplo, caixa preta). Eles são especialistas nos requisitos funcionais do aplicativo. Espera-se que um programador seja especialista tanto nos requisitos funcionais quanto no código (mas tende a se concentrar mais no código).
(Depende da organização se se espera explicitamente que os programadores sejam especialistas em requisitos. Independentemente disso, a expectativa implícita existe - se você cria algo errado, você - e não a pessoa responsável - recebe a culpa.)
Essa dupla função de especialista está sobrecarregando a mente do programador e, exceto os mais experientes, pode diminuir a proficiência nos requisitos. Acho que devo mudar de marcha mentalmente para considerar os usuários do aplicativo. Aqui está o que me ajuda:
fonte
Eu acho que você quer trabalhar em duas frentes. Um é político, fazendo com que sua organização adote testes em algum nível (com a esperança de que, com o tempo, eles adotem mais). Converse com engenheiros de controle de qualidade fora do seu local de trabalho. Encontre listas de livros de controle de qualidade . Dê uma olhada nos artigos relevantes da Wikipedia . Familiarize-se com os princípios e práticas de controle de qualidade. Aprender essas coisas o preparará para fazer o caso mais convincente possível em sua organização. Existem bons departamentos de controle de qualidade e agregam um valor considerável às suas organizações.
Como desenvolvedor individual, adote estratégias para usar em seu próprio trabalho. Use o TDD você mesmo desenvolvendo códigos e testes. Mantenha os testes limpos e bem conservados. Se perguntado por que você está fazendo isso, pode dizer que está impedindo regressões e isso mantém seu processo de pensamento melhor organizado (os dois serão verdadeiros). Existe uma arte em escrever código testável , aprendê-lo. Seja um bom exemplo para seus colegas desenvolvedores.
Em parte, estou pregando para mim mesmo aqui, porque faço muito menos dessas coisas do que sei que deveria.
fonte