Blob Blame History Raw
<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="unit-testing" xml:lang="pt-BR">

  <info>
    <link type="guide" xref="index#general-guidelines"/>

    <credit type="author copyright">
      <name>Philip Withnall</name>
      <email its:translate="no">philip.withnall@collabora.co.uk</email>
      <years>2015</years>
    </credit>

    <include xmlns="http://www.w3.org/2001/XInclude" href="cc-by-sa-3-0.xml"/>

    <desc>Projete software para ser testado e escreva testes de unidades para ele</desc>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Rafael Fontenelle</mal:name>
      <mal:email>rafaelff@gnome.org</mal:email>
      <mal:years>2017</mal:years>
    </mal:credit>
  </info>

  <title>Teste de unidade</title>

  <synopsis>
    <title>Resumo</title>

    <p>Teste de unidade deve ser o método principal para testar o conjunto de código escrito porque um teste de unidade pode ser escrito uma vez e executado muitas outras — testes manuais têm que ser planejados uma vez e, então, executado manualmente cada vez.</p>

    <p>O desenvolvimento de testes de unidade se inicia com o design de API e arquitetura do código a ser testado: o código deve ser projetado para ser facilmente testável, do contrário provavelmente ele será muito difícil de testar.</p>

    <list>
      <item><p>Escreva testes de unidade para serem tão pequenos quanto possível, mas não menores. (<link xref="#writing-unit-tests"/>)</p></item>
      <item><p>Use ferramentas de cobertura de código para escrever testes para obter alta cobertura de código. (<link xref="#writing-unit-tests"/>)</p></item>
      <item><p>Execute todos os testes de unidade sob Valgrind para verificar ocorrências vazamentos e outros problemas. (<link xref="#leak-checking"/>)</p></item>
      <item><p>Use as ferramentas apropriadas para automaticamente gerar testes de unidade onde for possível. (<link xref="#test-generation"/>)</p></item>
      <item><p>Projete o código para ser testável desde o início. (<link xref="#writing-testable-code"/>)</p></item>
    </list>
  </synopsis>

  <section id="writing-unit-tests">
    <title>Escrevendo testes de unidade</title>

    <p>Testes de unidade devem ser escritos em conjunto com a análise das <link xref="tooling#gcov-and-lcov">informações de cobertura de código obtidas por executar os testes</link>. Geralmente isso significa escrever um conjunto inicial de testes de unidade, executando-os para obter dados de cobertura e, então, retrabalhar e expandi-los para aumentar os níveis de cobertura de código. A cobertura deve primeiro ser aumentada se certificando de que todas as funções estejam cobertas (pelo menos em parte) e, então, se certificando de que todas as linhas de código estejam cobertas. Ao cobrir as funções primeiro, problemas de API que vão evitar testes efetivos serão localizados rapidamente. Essas geralmente se manifestam como funções internas que não podem ser facilmente chamadas a partir de testes de unidade. Em geral, deve-se ter como meta os níveis de cobertura em cerca de 90%; não teste apenas os casos cobertos pelos requerimentos do projeto, teste tudo.</p>

    <p>Assim como <link xref="version-control">git commits</link>, os testes de unidade devem ser “tão pequenos quanto possível, mas não menores”, testando especificamente uma única API ou um único comportamento. Cada caso de teste deve ser capaz de executar individualmente, sem depender do estado de outros casos de teste. Isso é importante para permitir depuração de falha em um teste, sem ter que também passar por todos os outros códigos de teste. Isso significa que a falha de um teste pode ser facilmente rastreada a uma API específica, em vez de uma mensagem genérica “testes de unidade falharam em algum lugar”.</p>

    <p>O GLib oferece suporte a testes de unidade com seu <link href="https://developer.gnome.org/glib/stable/glib-Testing.html">framework GTest</link>, permitindo que os testes sejam organizados em grupos e hierarquias. Isso significa que os grupos de testes relacionados podem ser executados juntos para melhorar a depuração também, executando o binário de teste com o argumento <cmd>-p</cmd>: <cmd>./test-suite-nome -p /caminho/para/grupo/de/teste</cmd>.</p>
  </section>

  <section id="installed-tests">
    <title>Testes instalados</title>

    <p>Todos os testes de unidade devem ser instalados para todo o sistema, seguindo o <link href="https://wiki.gnome.org/Initiatives/GnomeGoals/InstalledTests">padrão de testes instalados</link>.</p>

    <p>Ao instalar os testes de unidade, integração contínua (CI) é facilitada, já que os testes para um projeto podem ser reexecutados após alterações em outros projetos no ambiente de CI, de forma a testar as interfaces entre módulos. Isso é útil para um conjunto altamente encaixado de projetos como o GNOME.</p>

    <p>Para adicionar suporte para testes instalados, adicione o seguinte ao <file>configure.ac</file>:</p>
    <code># Testes instalados
AC_ARG_ENABLE([modular_tests],
              AS_HELP_STRING([--disable-modular-tests],
                             [Disable build of test programs (default: no)]),,
              [enable_modular_tests=yes])
AC_ARG_ENABLE([installed_tests],
              AS_HELP_STRING([--enable-installed-tests],
                             [Install test programs (default: no)]),,
              [enable_installed_tests=no])
AM_CONDITIONAL([BUILD_MODULAR_TESTS],
               [test "$enable_modular_tests" = "yes" ||
                test "$enable_installed_tests" = "yes"])
AM_CONDITIONAL([BUILDOPT_INSTALL_TESTS],[test "$enable_installed_tests" = "yes"])</code>

    <p>Então, em <file>tests/Makefile.am</file>:</p>
    <code>insttestdir = $(libexecdir)/installed-tests/[project]

all_test_programs = \
	test-program1 \
	test-program2 \
	test-program3 \
	$(NULL)
if BUILD_MODULAR_TESTS
TESTS = $(all_test_programs)
noinst_PROGRAMS = $(TESTS)
endif

if BUILDOPT_INSTALL_TESTS
insttest_PROGRAMS = $(all_test_programs)

testmetadir = $(datadir)/installed-tests/[project]
testmeta_DATA = $(all_test_programs:=.test)

testdatadir = $(insttestdir)
testdata_DATA = $(test_files)

testdata_SCRIPTS = $(test_script_files)
endif

EXTRA_DIST = $(test_files)

%.test: % Makefile
	$(AM_V_GEN) (echo '[Test]' &gt; $@.tmp; \
	echo 'Type=session' &gt;&gt; $@.tmp; \
	echo 'Exec=$(insttestdir)/$&lt;' &gt;&gt; $@.tmp; \
	mv $@.tmp $@)</code>
  </section>

  <section id="leak-checking">
    <title>Verificação de vazamento</title>

    <p>Assim que os testes de unidade com alta cobertura de código forem escritos, eles podem ser executados sob várias ferramentas de análise dinâmica, tal como <link xref="tooling#valgrind">Valgrind</link> para verificar por vazamentos, erros de threading, problemas de alocação, etc. por toda a base de código. Quanto maior a cobertura de código dos testes unitários, mais confiança haverá nos resultados do Valgrind. Veja <link xref="tooling"/> para mais informações, incluindo instruções sobre integração de sistema de compilação.</p>

    <p>Mais importante de tudo é que isso significa que os testes de unidade não devem eles mesmos resultar em vazamento de memória ou de outros recursos e, de forma similar, não devem ter qualquer problemas de threading. Qualquer um desses problemas seria efetivamente um falso positivo na análise do código projeto. (Falsos positivos que precisam ser resolvidos corrigindo os testes de unidade.)</p>
  </section>

  <section id="test-generation">
    <title>Geração de testes</title>

    <p>Certos tipos de códigos são bem repetitivos e exigem muitos testes de unidade para obter uma boa cobertura de código; mas são apropriados para <link href="http://en.wikipedia.org/wiki/Test_data_generation">geração de dados de teste</link>, na qual uma ferramenta é usada para automaticamente gerar vetores de teste para o código. Isso pode reduzir drasticamente o tempo necessário para escrever testes de unidade, para código nesses domínio em específico.</p>

    <section id="json">
      <title>JSON</title>

      <p>Um exemplo de um domínio acessível para geração de dados de teste é análise, na qual exige-se que os dados a serem analisados sigam um esquema estrito — esse é o caso para documentos XML e JSON. Para JSON, uma ferramenta como <link href="http://people.collabora.com/~pwith/walbottle/">Walbottle</link> pode ser usado para gerar vetores de teste para todos tipos de entrada válida e inválida de acordo com o esquema.</p>

      <p>Todo tipo de documento JSON deve ter um <link href="http://json-schema.org/">JSON Schema</link> definido para ele, que pode então ser passado para Walbottle para gerar vetores de teste:</p>
      <code mime="application/x-shellscript">
json-schema-generate --valid-only schema.json
json-schema-generate --invalid-only schema.json</code>

      <p>Esses vetores de teste podem, então, ser passados para o código sob teste em seus testes de unidade. As instâncias JSON geradas por <cmd>--valid-only</cmd> deve ser aceita; aqueles de <cmd>--invalid-only</cmd> deve ser rejeitado.</p>
    </section>
  </section>

  <section id="writing-testable-code">
    <title>Escrevendo código testável</title>

    <p>O código deve ser escrito com testabilidade em mente desde o estágio de design, já que isso afeta o design de API e arquitetura em formas fundamentais. Alguns princípios chaves:</p>
    <list>
      <item><p>Não use um estado global. Objetos “sigleton” são geralmente uma má ideia, pois eles não podem ser instanciados separadamente ou controlados nos testes de unidade.</p></item>
      <item><p>Separe o uso de estado externo, tal como banco de dados, conectividade ou sistema de arquivos. Os testes de unidade podem, então, substituir os acessos a estado externo com objetos simulados (mock). Uma abordagem comum a isso é usar injeção de dependência para passar um objeto interfaceador de sistema de arquivos para o código sob teste. Por exemplo, uma classe não deve carregar um banco de dados global (de uma localização fixa no sistema de arquivos) porque os testes de unidade poderiam acabar sobrescrevendo a cópia do sistema em execução do banco de dados, e nunca poderia ser executado em paralelo. A eles deve ser passado um objeto que fornece uma interface para o banco de dados: em um sistema de produção, isso seria um interfaceador magro em volta da API de banco de dados; para teste, seria um objeto simulado que verifica as requisições fornecidas a ele e retorna respostas codificadas para vários testes.</p></item>
      <item><p>Exponha funções utilitárias onde geralmente elas podem ser úteis.</p></item>
      <item><p>Divida projetos em coleções de bibliotecas pequenas e privadas, que são então vinculadas com uma quantidade mínima de código colante no executável global.</p></item>
    </list>
  </section>

  <section id="external-links">
    <title>Links externos</title>

    <p>O tópico de estabilidade de API é coberto nos seguintes artigos (em inglês):</p>
    <list>
      <item><p><link href="http://msdn.microsoft.com/en-us/magazine/dd263069.aspx">Design por testabilidade</link></p></item>
      <item><p><link href="http://en.wikipedia.org/wiki/Software_testability">testabilidade de software</link></p></item>
      <item><p><link href="http://en.wikipedia.org/wiki/Dependency_injection">Injeção de dependência</link></p></item>
      <item><p><link href="http://c2.com/cgi/wiki?SoftwareDesignForTesting">Design de software para teste</link></p></item>
    </list>
  </section>
</page>