Às vezes, funções privadas são simplesmente unidades internas de funcionalidade ainda a serem extraídas. Então, por que não testá-los?

9

Às vezes, funções privadas de um módulo ou classe são simplesmente unidades internas de funcionalidade ainda a serem extraídas, que podem merecer seus próprios testes. Então, por que não testá-los? Nós vai escrever testes para eles mais tarde, se / quando eles são extraídos. Então, por que não escrever os testes agora, quando eles ainda fazem parte do mesmo arquivo?

Para demonstrar:

insira a descrição da imagem aqui

Primeiro eu escrevi module_a. Agora eu quero escrever testes para isso. Eu gostaria de testar a função 'privada' _private_func. Eu não entendo por que não escreveria um teste para ele, se mais tarde eu poderia refatorá-lo para seu próprio módulo interno e, em seguida, escrever testes para ele.


Suponha que eu tenha um módulo com as seguintes funções (também pode ser uma classe):

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

_do_stuffe _do_more_stuffsão funções 'privadas' do módulo.

Entendo a ideia de que devemos testar apenas a interface pública, não os detalhes da implementação. No entanto, aqui está a coisa:

_do_stuffe _do_more_stuffcontém a maioria das funcionalidades do módulo. Cada um deles poderia ser uma função pública de um módulo 'interno' diferente. Mas eles ainda não foram desenvolvidos e são grandes o suficiente para serem extraídos para separar arquivos.

Portanto, testar essas funções parece certo porque são unidades importantes de funcionalidade. Se eles estivessem em módulos diferentes como funções públicas, nós os teríamos testado. Então, por que não testá-los quando ainda não foram (ou nunca) extraídos para um arquivo diferente?

Aviv Cohn
fonte
2
"Métodos particulares são benéficos para o teste de unidade ..." "... aderir ao método privado me traz um aprimoramento útil e confiável nos testes de unidade. Por outro lado, o enfraquecimento das limitações de acesso" para testabilidade "só me dá uma visão obscura e difícil de entender parte do código de teste, que também corre o risco permanente de ser quebrado por qualquer refatoração menor; francamente, o que recebo parece suspeito como dívida técnica "
gnat
3
"Devo testar funções privadas?" Não. Nunca, nunca, nunca.
David Arno
2
@DavidArno Why? Qual é a alternativa aos testes internos? Apenas testes de integração? Ou tornar mais coisas públicas? (embora na minha experiência eu testar principalmente métodos públicos em classes internas, métodos não privadas)
CodesInChaos
11
Se é importante o suficiente para que seja necessário escrever testes para ele, ele já deve estar em um módulo separado. Caso contrário, teste seu comportamento usando a API pública.
18716 Vincent Savard

Respostas:

14

A necessidade de testar não é a mesma que ser pública.

Código não trivial precisa ser testado independentemente da exposição. O comportamento não público não precisa existir e muito menos ser testado.

Essas visões conflitantes podem levar você a querer tornar pública todas as funções ou se recusar a incluir o código em uma função, a menos que seja pública.

Esta não é a resposta. Esteja disposto a criar funções auxiliares privadas. Teste-os através da interface pública que o utiliza o máximo possível.

Se o teste através da interface pública não exercer suficientemente a função privada, a função privada está tentando permitir muito.

A validação pode ajudar a restringir o que a função privada permite. Se você não conseguir passar um nulo pela interface pública, ainda poderá lançar uma exceção, se houver, de qualquer maneira.

Por que você deveria? Por que testar o que você nunca verá? Porque as coisas mudam. Pode ser privado agora, mas ser público mais tarde. O código de chamada pode mudar. O código que rejeita explicitamente nulo torna claro o uso adequado e o estado esperado.

Claro que nulo poderia estar bem. É apenas um exemplo aqui. Mas se você espera algo, é útil deixar essa expectativa clara.

Esse pode não ser o tipo de teste que você tinha em mente, mas espero que esteja disposto a criar funções auxiliares privadas, quando apropriado.

O desejo de testar é bom, mas não deve ser a força motriz no design da sua API pública. Projete a API pública para ser fácil de usar. Provavelmente não será se todas as funções forem públicas. A API deve ser algo que as pessoas possam entender como usar sem mergulhar no código. Não deixe essas pessoas se perguntando para que serve essa função auxiliar estranha.

Ocultar funções auxiliares públicas em um módulo interno é uma tentativa de respeitar a necessidade de uma API limpa enquanto expõe auxiliares para teste. Não direi que isso está errado. Você pode estar dando o primeiro passo em direção a uma camada arquitetural diferente. Mas, por favor, domine a arte de testar funções auxiliares privadas através das funções públicas que as utilizam primeiro. Dessa forma, você não usará mais essa solução alternativa.

candied_orange
fonte
Eu propus uma abordagem, gostaria de ouvir sua opinião: sempre que estou em uma situação em que quero testar uma função privada, verifico se consigo testá-la suficientemente através de uma das funções públicas (incluindo todos os casos de borda, etc). Se eu puder, não escreverei um teste para esta função especificamente, mas somente testarei testando as funções públicas que a utilizam. No entanto, se eu sentir que a função não pode ser testada suficientemente por meio da interface pública e merecer um teste próprio, vou extraí-la para um módulo interno em que seu público e escrever testes para ela. O que você acha?
Aviv Cohn
Vou dizer que estou perguntando a mesma coisa para os outros caras que responderam aqui :) Gostaria de ouvir a opinião de todos.
Aviv Cohn
Mais uma vez, não vou dizer não. Estou preocupado que você não tenha dito nada sobre como isso afeta a usabilidade. A diferença entre público e privado não é estrutural. É uso. Se a diferença entre público e privado é uma porta da frente e uma porta dos fundos, então a sua solução é construir um galpão no quintal. Bem. Contanto que as pessoas não se percam lá atrás.
Candied_orange 11/11
11
Promovido para "Se o teste através da interface pública não exercer suficientemente a função privada, a função privada está tentando permitir muito".
Kris Welsh #
7

Resposta curta: Não

Resposta mais longa: Sim, mas através da 'API' pública da sua classe

A idéia dos membros privados de uma classe é que eles representem funcionalidades que devem ser invisíveis fora da 'unidade' do código, por maior que você queira definir essa unidade. No código orientado a objetos, essa unidade geralmente acaba sendo uma classe.

Você deve ter sua classe projetada de forma que seja possível invocar toda a funcionalidade privada por meio de várias combinações de estado de entrada. Se você achar que não existe uma maneira relativamente direta de fazer isso, provavelmente é uma sugestão de que seu design precisa de mais atenção.


Após o esclarecimento da questão, isso é apenas uma questão de semântica. Se o código em questão puder operar como uma unidade autônoma separada e estiver sendo testado como se fosse um código público, não vejo nenhum benefício em não movê-lo para um módulo autônomo. No momento, serve apenas para confundir futuros desenvolvedores (inclusive você, em seis meses), por que o código aparentemente público está oculto dentro de outro módulo.

richzilla
fonte
Olá, obrigado pela sua resposta :) Por favor, releia a pergunta que editei para esclarecer.
Aviv Cohn
Eu propus uma abordagem, gostaria de ouvir sua opinião: sempre que estou em uma situação em que quero testar uma função privada, verifico se consigo testá-la suficientemente através de uma das funções públicas (incluindo todos os casos de borda, etc). Se eu puder, não escreverei um teste para esta função especificamente, mas somente testarei testando as funções públicas que a utilizam. No entanto, se eu sentir que a função não pode ser testada suficientemente por meio da interface pública e merecer um teste próprio, vou extraí-la para um módulo interno em que seu público e escrever testes para ela. O que você acha?
Aviv Cohn
Vou dizer que estou perguntando a mesma coisa para os outros caras que responderam aqui :) Gostaria de ouvir a opinião de todos.
Aviv Cohn
5

O ponto principal das funções privadas é que elas são detalhes ocultos da implementação que podem ser alterados à vontade, sem alterar a API pública. Para o seu código de exemplo:

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

Se você tiver uma série de testes que apenas usam public_func, se você a reescrever para:

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

então, desde que o resultado de retorno para um valor específico apermaneça o mesmo, todos os seus testes serão bons. Se o resultado do retorno mudar, um teste falhará, destacando o fato de que algo ocorreu.

Tudo isso é bom: API pública estática; funcionamento interno bem encapsulado; e testes robustos.

No entanto, se você tivesse escrito testes para _do_stuffou _do_more_stufffeito a alteração acima, agora terá vários testes quebrados, não porque a funcionalidade mudou, mas porque a implementação dessa funcionalidade mudou. Esses testes precisariam ser reescritos para funcionar com as novas funções, mas, com eles funcionando, tudo que você saberia é que esses testes funcionavam com as novas funções. Você teria perdido os testes originais e, portanto, não saberia se o comportamento public_funchavia mudado; portanto, os testes seriam basicamente inúteis.

Isso é ruim: uma API em mudança; trabalhos internos expostos firmemente acoplados a testes; e testes frágeis que mudam assim que são feitas mudanças na implementação.

Portanto, não, não teste funções privadas. Sempre.

David Arno
fonte
Olá David, obrigado pela sua resposta. Releia minha pergunta, editei para esclarecer.
Aviv Cohn
Meh. Não há nada de errado em testar funções internas. Pessoalmente, não escrevo uma função não trivial de qualquer tipo, a menos que eu possa cobri-la com alguns testes de unidade; portanto, banir o teste de funções internas me impediria de escrever funções internas, roubando-me uma técnica muito útil. As APIs podem e mudam; quando o fizerem, você deve alterar os testes. Refatorar uma função interna (e seu teste) não quebra os testes da função externa; esse é o objetivo de fazer esses testes. Maus conselhos em geral.
9788 Robert Downey
O que você realmente está argumentando é que não deve ter funções privadas.
Robert Harvey
11
@AvivCohn Eles são grandes o suficiente para justificar o teste; nesse caso, são grandes o suficiente para obter seu próprio arquivo; ou eles são pequenos o suficiente para que você não precise testá-los separadamente. Então qual é?
Doval
3
@RobertHarvey Não, faz com que o argumento "divida grandes classes em componentes fracamente acoplados, se necessário". Se eles realmente não são públicos, provavelmente é um bom caso de uso para visibilidade do pacote privado.
Doval