Namespaces anônimos tornam o código não testável

13

Aqui está um código C ++ típico:

foo.hpp

#pragma once

class Foo {
public:
  void f();
  void g();
  ...
};

foo.cpp

#include "foo.hpp"

namespace {
    const int kUpperX = 111;
    const int kAlternativeX = 222;

    bool match(int x) {
      return x < kUpperX || x == kAlternativeX;
    }
} // namespace

void Foo::f() {
  ...
  if (match(x)) return;
  ...

Parece um código C ++ idiomático decente - uma classe, uma função auxiliar matchque é usada pelos métodos de Foo, algumas constantes para essa função auxiliar.

E então eu quero escrever testes.
Seria perfeitamente lógico escrever um teste de unidade separado match, porque é bastante não trivial.
Mas ele reside em um espaço para nome anônimo.
Claro que posso escrever um teste que chamaria Foo::f(). No entanto, não será um bom teste se Foofor pesado e complicado; esse teste não isolará o testado de outros fatores não relacionados.

Então eu tenho que mudar matche todo o resto do espaço para nome anônimo.

Pergunta: qual é o sentido de colocar funções e constantes no espaço para nome anônimo, se as torna inutilizáveis ​​nos testes?

Abyx
fonte
3
@ Leia novamente o código - o espaço para nome anônimo está foo.cpp, não o cabeçalho! O OP parece entender muito bem que você não deve colocar nenhum espaço para nome em um cabeçalho.
amon
Alguma idéia no código C ++ de teste de unidade em um espaço para nome sem nome ... mas o "ponto" é que o encapsulamento é bom. Afinal, é o mesmo problema que você tem com as funções de membro privadas: elas não podem ser testadas de maneira não invasiva, mas você não deseja abrir mão de informações ocultas para testes de unidade (por exemplo, stackoverflow.com/a/3676680/3235496 ).
manlio
1
Seu caso não é conceitualmente muito diferente do caso de testar (ou não testar) métodos privados. Portanto, quando você pesquisar "teste de unidade particular" aqui em Programadores, receberá muitas respostas que poderá aplicar diretamente ao seu caso.
Doc Brown
@ DocBrown Eu não estou perguntando como testar funções em anon. namespaces. Estou perguntando "por que colocar código em espaço de nome não.?" (veja o texto no final da pergunta). Culpe o Ixrec por mudar o título para algo diferente.
Abyx
1
@Abyx: nessas outras respostas que mencionei acima, você encontrará um grande consenso de muitos especialistas aqui de que é uma péssima ideia testar métodos privados e que friendnão é recomendável abusar da palavra - chave para esse fim. que, se uma restrição para um método leva a uma situação em que você não pode mais testá-lo diretamente, isso implicaria que métodos privados não eram úteis.
Doc Brown

Respostas:

7

Se você quiser testar os detalhes da implementação privada, faça o mesmo tipo de esquiva para espaços de nome não nomeados que para membros da classe privados (ou protegidos):

Entre e festeje.

Enquanto nas classes que você abusa friend, nos namespaces não nomeados, você abusa do #includemecanismo, que nem o força a alterar o código.
Agora que seu código de teste (ou melhor, apenas algo para expor tudo) está na mesma TU, não há problema.

Uma palavra de cautela: Se você testar os detalhes da implementação, seu teste será interrompido se eles mudarem. Certifique-se de testar apenas os detalhes da implementação que vazarão de qualquer maneira ou aceite que seu teste é extraordinariamente efêmero.

Desduplicador
fonte
6

A função no seu exemplo parece bastante complexa e pode ser melhor movê-la para o cabeçalho, para fins de teste de unidade.

qual é o sentido de colocar funções e constantes no espaço para nome anônimo, se as torna inutilizáveis ​​nos testes?

Para torná-los isolados do resto do mundo. E não são apenas as funções e constantes que você pode colocar no espaço para nome anônimo - também é para tipos.

No entanto, se tornar seus testes de unidade muito complexos, você estará fazendo errado. Nesse caso, a função não pertence a isso. Chegou a hora de um pouco de refatoração para simplificar os testes.

Portanto, no espaço para nome anônimo, devem haver apenas funções muito simples, às vezes constantes e tipos (incluindo typedefs) usados ​​nessa unidade de tradução.

BЈовић
fonte
5

Seria perfeitamente lógico escrever um teste de unidade separado para correspondência, porque é bastante não trivial.

O código que você mostrou matché um liner 1 bastante trivial, sem casos extremos complicados, ou é como um exemplo simplificado? Enfim, eu vou assumir que é simplificado ...

Pergunta: qual é o sentido de colocar funções e constantes no espaço para nome anônimo, se as torna inutilizáveis ​​nos testes?

Esta pergunta é o que queria me fazer entrar aqui, já que o Deduplicator já mostrava uma maneira perfeitamente boa de invadir e obter acesso através de #includetruques. Mas a redação aqui faz parecer que testar todos os detalhes internos de implementação de tudo é algum tipo de objetivo final universal, quando está longe disso.

O objetivo dos testes unitários nem sempre é testar todas as micro-unidades granulares internas de funcionalidade. A mesma pergunta se aplica a funções estáticas de escopo de arquivo em C. Você pode até tornar a pergunta mais difícil de responder, perguntando por que os desenvolvedores usam pimplsem C ++ que exigiria tanto truques friendshipquanto #includetruques na caixa branca, negociando facilmente a testabilidade dos detalhes da implementação para melhorar os tempos de compilação, por exemplo

De um tipo de perspectiva pragmática, pode parecer grosseiro, mas matchpode não ser implementado corretamente com alguns casos extremos que o fazem tropeçar. No entanto, se a única classe externa Foo, que tem acesso, matchnão pode usá-la de maneira a encontrar esses casos extremos, então é bastante irrelevante a correção de Fooque matchesses casos extremos nunca serão encontrados, a menos Fooque sejam alterados, nesse momento os testes Foofalharão e saberemos imediatamente.

Uma mentalidade mais obsessiva, ansiosa por testar todos os detalhes de implementação interna (talvez um software de missão crítica, por exemplo) pode querer entrar e festejar, mas muitas pessoas não acham necessariamente que essa é a melhor ideia, pois isso criaria o testes mais frágeis que se possa imaginar. YMMV. Mas eu só queria abordar a redação desta pergunta, que faz parecer que esse tipo de testabilidade de nível interno de detalhes ultrafinos deve ser um objetivo final, quando mesmo a mentalidade mais rigorosa de teste de unidade pode relaxar um pouco aqui e evite radiografar os internos de todas as classes.

Então, por que as pessoas definem funções em namespaces anônimos em C ++ ou como funções estáticas no escopo de arquivos com ligação interna em C, ocultas do mundo externo? E é isso principalmente: escondê-los do mundo exterior. Isso tem vários efeitos, da redução do tempo de compilação à redução da complexidade (o que não pode ser acessado em outro lugar não pode causar problemas em outro lugar) e assim por diante. Provavelmente, a testabilidade dos detalhes de implementação privada / interna não é a primeira coisa em que as pessoas pensam, por exemplo, reduzindo o tempo de construção e ocultando a complexidade desnecessária do mundo exterior.


fonte
Eu gostaria de poder votar isso dez vezes. Como tangente relacionada ao software de missão crítica e alta garantia, você está muito mais preocupado com a correção dos detalhes. Esse estilo idiomático para C ++ não é adequado para isso. Eu não usaria recursos do idioma como namespaces anônimos ou padrões de proxy. Eu controlaria meus limites de maneira grosseira com cabeçalhos de espaço de usuário [suportados] e cabeçalhos de espaço de desenvolvedor [não suportados].
David