Nomenclatura de pacote adequada para teste com a linguagem Go

102

Eu vi várias estratégias diferentes de nomenclatura de pacotes de teste no Go e queria saber quais são os prós e os contras de cada uma e qual devo usar.

Estratégia 1:

Nome do arquivo: github.com/user/myfunc.go

package myfunc

Nome do arquivo de teste: github.com/user/myfunc_test.go

package myfunc

Veja bzip2 para um exemplo.

Estratégia 2:

Nome do arquivo: github.com/user/myfunc.go

package myfunc

Nome do arquivo de teste: github.com/user/myfunc_test.go

package myfunc_test

import (
    "github.com/user/myfunc"
)

Veja fio para um exemplo.

Estratégia 3:

Nome do arquivo: github.com/user/myfunc.go

package myfunc

Nome do arquivo de teste: github.com/user/myfunc_test.go

package myfunc_test

import (
    . "myfunc"
)

Veja strings para um exemplo.

A biblioteca padrão Go parece usar uma mistura das estratégias 1 e 2. Qual das três devo usar? É uma dor anexar package *_testaos meus pacotes de teste, pois significa que não posso testar os métodos privados do meu pacote, mas talvez haja uma vantagem oculta da qual não estou ciente?

Dan
fonte
9
Essa pergunta só vai resultar em opiniões divergentes, mas vou acrescentar a minha. Você não deve precisar testar seus métodos privados. Você deseja testar a interface do seu pacote que outros desenvolvedores irão consumir. Se os testes falharem, você sabe que seus métodos privados precisam ser examinados.
Brenden de
2
O exemplo [wire] ( github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go ) para a Estratégia 2, agora também é um exemplo de Estratégia 1 ...
durp

Respostas:

130

A diferença fundamental entre as três estratégias que você listou é se o código de teste está ou não no mesmo pacote que o código em teste. A decisão de usar package myfuncou package myfunc_testno arquivo de teste depende se você deseja realizar um teste de caixa branca ou caixa preta .

Não há nada de errado em usar os dois métodos em um projeto. Por exemplo, você poderia ter myfunc_whitebox_test.goe myfunx_blackbox_test.go.

Comparação de pacote de código de teste

  • Teste de caixa preta: Use package myfunc_test, que garantirá que você está usando apenas os identificadores exportados .
  • Teste de caixa branca: use package myfuncpara que você tenha acesso aos identificadores não exportados. Bom para testes de unidade que requerem acesso a variáveis, funções e métodos não exportados.

Comparação de estratégias listadas em questão

  • Estratégia 1: O arquivo myfunc_test.gousa package myfunc- Neste caso, o código de teste em myfunc_test.goestará no mesmo pacote que o código sendo testado myfunc.go, que está myfuncneste exemplo.
  • Estratégia 2: O arquivo myfunc_test.gousa package myfunc_test- Neste caso, o código de teste em myfunc_test.go"será compilado como um pacote separado e, em seguida, vinculado e executado com o binário de teste principal." [Fonte: linhas 58-59 no código-fonte test.go ]
  • Estratégia 3: O arquivo myfunc_test.gousa, package myfunc_testmas importa, myfuncusando a notação de ponto - Esta é uma variante da Estratégia 2, mas usa a notação de ponto para importar myfunc.
Matthew Rankin
fonte
1
Deve-se observar que o uso da Estratégia 1 também manterá os arquivos com _test.goseparados do pacote sendo testado (o mesmo comportamento da Estratégia 2). Isso não parece estar documentado por github.com/golang/go/issues/15315
Kevin Deenanauth
Eu vi um pacote forte usar a Estratégia 3, mas não consigo entender qual é o ponto?
PickBoy
1
Fiz um fork de um pacote e fiz alterações, e agora meus testes estão todos tentando importar o repositório original em vez do meu pacote com fork. Com a Estratégia 3, não preciso alterar o "github.com/original/link" para "github.com/my/fork", porque está apenas referenciando '.' em vez de.
nmarley
1
@KevinDeenanauth Isso me surpreendeu. Pensei ter encontrado uma armadilha quando acabei de encontrar um _test.gocom um _testnome que não é de pacote e contém um func init()que muda alguma variável global de pacote para teste. Eu estava errado.
Zyl de
1
@nmarley, o .não resolve o problema do garfo. Não é uma importação relativa. Ele apenas importa os identificadores "para o pacote atual".
qaisjp
19

Depende do escopo de seus testes. Os testes de alto nível (integração, aceitação, etc ...) provavelmente devem ser colocados em um pacote separado para garantir que você está usando o pacote por meio da API exportada.

Se você tiver um pacote grande com muitos componentes internos que precisam ser testados, use o mesmo pacote para seus testes. Mas isso não é um convite para que seus testes acessem qualquer parte do estado privado. Isso tornaria a refatoração um pesadelo. Quando escrevo structs em go , geralmente estou implementando interfaces. São esses métodos de interface que invoco em meus testes, nem todos os métodos / funções auxiliares individualmente.

mdwhatcott
fonte
13

Você deve usar a estratégia 1 sempre que possível. Você pode usar o foo_testnome do pacote especial para evitar ciclos de importação, mas ele está lá principalmente para que a biblioteca padrão possa ser testada com o mesmo mecanismo. Por exemplo, stringsnão pode ser testado com a estratégia 1, pois o testingpacote depende de strings. Como você disse, com a estratégia 2 ou 3 você não tem acesso aos identificadores privados do pacote, então geralmente é melhor não usá-lo a menos que seja necessário.

guelfey
fonte
10
Como não ter acesso a identificadores privados em testes não é uma virtude?
jub0bs de
3
de acordo com as boas práticas de teste, você não testa detalhes de implementação interna para um artefato de código; fazendo filho, é um código de cheiro
Gerardo Lima
0

Uma observação importante que gostaria de acrescentar sobre os comentários import .do Golang CodeReview :

O import .formulário pode ser útil em testes que, devido às dependências circulares , não podem fazer parte do pacote que está sendo testado:

package foo_test

import (
    "bar/testutil" // also imports "foo"
    . "foo"
)

Nesse caso, o arquivo de teste não pode estar no pacote foo porque ele usa bar/testutil, que importa foo. Portanto, usamos o 'import.' formulário para permitir que o arquivo finja ser parte do pacote foo, embora não seja.

Exceto neste caso, não useimport . em seus programas. Isso torna os programas muito mais difíceis de ler porque não está claro se um nome como Quux é um identificador de nível superior no pacote atual ou em um pacote importado.

Eric
fonte