Les espaces de noms anonymes rendent le code non testable

13

Voici un code C ++ typique:

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;
  ...

Il ressemble à un code C ++ idiomatique décent - une classe, une fonction d'aide matchqui est utilisée par les méthodes de Foo, quelques constantes pour cette fonction d'aide.

Et puis je veux écrire des tests.
Il serait parfaitement logique d'écrire un test unitaire séparé match, car il n'est pas banal.
Mais il réside dans un espace de noms anonyme.
Bien sûr, je peux écrire un test qui appellerait Foo::f(). Cependant, ce ne sera pas un bon test s'il Fooest lourd et compliqué, un tel test n'isolera pas le candidat des autres facteurs non liés.

Je dois donc déplacer matchet tout le reste hors de l'espace de noms anonyme.

Question: quel est l'intérêt de mettre des fonctions et des constantes dans l'espace de noms anonyme, si cela les rend inutilisables dans les tests?

Abyx
la source
3
@ BЈовић relire le code - l'espace de noms anonyme est dedans foo.cpp, pas l'en-tête! OP semble comprendre très bien que vous ne devriez pas mettre des espaces de noms dans un en-tête.
amon
Une idée dans le test unitaire du code C ++ dans un espace de noms sans nom ... mais le "point" est que l'encapsulation est bonne. Après tout, c'est le même problème que vous avez avec les fonctions membres privées: elles ne peuvent pas être testées de manière non intrusive, mais vous ne voulez pas abandonner les informations qui se cachent pour les tests unitaires (par exemple stackoverflow.com/a/3676680/3235496 ).
manlio
1
Votre cas n'est pas conceptuellement très différent du cas de tester (ou de ne pas tester) des méthodes privées. Ainsi, lorsque vous recherchez "test unitaire privé" ici sur les programmeurs, vous obtiendrez de nombreuses réponses que vous pouvez appliquer directement à votre cas.
Doc Brown
@DocBrown Je ne demande pas comment tester les fonctions dans anon. espaces de noms. Je demande "pourquoi mettre du code dans des espaces de noms anon." (voir le texte à la fin de la question). Blâmez Ixrec d'avoir changé le titre en quelque chose de différent.
Abyx
1
@Abyx: dans ces autres réponses que j'ai mentionnées ci-dessus, vous trouverez un grand consensus de nombreux experts ici que c'est une très mauvaise idée de tester des méthodes privées, et qu'il friendn'est pas recommandé d' abuser du mot - clé à cette fin.Combinez cela avec votre hypothèse que si une restriction pour une méthode conduit à une situation où vous ne pouvez plus la tester directement, cela impliquerait que les méthodes privées n'étaient pas utiles.
Doc Brown

Réponses:

7

Si vous souhaitez tester de manière unitaire les détails d'implémentation privés, vous effectuez le même type d'esquive pour les espaces de noms sans nom que pour les membres de classe privés (ou protégés):

Entrez et faites la fête.

Alors que pour les classes que vous abusez friend, pour les espaces de noms sans nom, vous #includeabusez du mécanisme, qui ne vous force même pas à changer le code.
Maintenant que votre code de test (ou mieux seulement quelque chose pour tout exposer) est dans le même TU, il n'y a plus de problème.

Un mot d'avertissement: si vous testez les détails de l'implémentation, votre test sera interrompu si ceux-ci changent. Assurez-vous vraiment de ne tester que les détails d'implémentation qui fuiront de toute façon, ou acceptez que votre test est inhabituellement éphémère.

Déduplicateur
la source
6

La fonction dans votre exemple semble assez complexe, et il peut être préférable de la déplacer vers l'en-tête, à des fins de test unitaire.

quel est l'intérêt de mettre des fonctions et des constantes dans l'espace de noms anonyme, si cela les rend inutilisables dans les tests?

Pour les isoler du reste du monde. Et ce ne sont pas seulement les fonctions et les constantes que vous pouvez mettre dans l'espace de noms anonyme - c'est aussi pour les types.

Cependant, si cela rend vos tests unitaires très complexes, vous vous trompez. Dans ce cas, la fonction n'y appartient pas. Ensuite, il est temps pour une petite refactorisation de simplifier les tests.

Ainsi, dans un espace de noms anonyme, seules les fonctions très simples, parfois les constantes et les types (y compris les typedefs) utilisés dans cette unité de traduction, doivent être utilisées.

BЈовић
la source
5

Il serait parfaitement logique d'écrire un test unitaire séparé pour la correspondance, car il n'est pas banal.

Le code que vous avez montré matchest un 1-liner assez trivial sans cas de bord délicat, ou est-ce comme un exemple simplifié? Quoi qu'il en soit, je suppose que c'est simplifié ...

Question: quel est l'intérêt de mettre des fonctions et des constantes dans l'espace de noms anonyme, si cela les rend inutilisables dans les tests?

Cette question est ce qui voulait me faire sauter ici, car Deduplicator a déjà montré un excellent moyen de pénétrer et d'accéder à la #includeruse. Mais le libellé ici donne l'impression que tester chaque détail d'implémentation interne de tout est une sorte d'objectif final universel, quand il est loin de là.

L'objectif d'un test unitaire uniforme n'est pas toujours de tester chaque petite micro-unité interne granulaire de fonctionnalité. La même question s'applique aux fonctions statiques de portée de fichier en C. Vous pouvez même rendre la question plus difficile à répondre en demandant pourquoi les développeurs utilisent pimplsen C ++ qui nécessiteraient les deux friendship et la #includeruse à la boîte blanche, échangeant une testabilité facile des détails d'implémentation pour des temps de compilation améliorés, par exemple

Dans une sorte de perspective pragmatique, cela peut sembler grossier mais matchpeut ne pas être correctement mis en œuvre avec certains cas de bord qui le font se déclencher. Cependant, si la seule classe externe Foo, qui a accès à, matchne peut pas l'utiliser d'une manière qui rencontre ces cas marginaux, alors c'est peu pertinent pour l'exactitude de Foocela matcha ces cas limites qui ne seront jamais rencontrés à moins de Foochangements, à quel point les tests Fooéchoueront et nous le saurons immédiatement.

Un état d'esprit plus obsessionnel désireux de tester chaque détail d'implémentation interne (peut-être un logiciel essentiel à la mission, par exemple) pourrait vouloir s'introduire et faire la fête, mais beaucoup de gens ne pensent pas nécessairement que c'est la meilleure idée, car cela créerait le tests les plus fragiles imaginables. YMMV. Mais je voulais juste aborder le libellé de cette question qui donne l'impression que ce genre de testabilité au niveau de détail interne ultra fin devrait être un objectif final, alors que même l'état d'esprit des tests unitaires les plus rigoureux pourrait se détendre un peu ici et éviter de radiographier les internes de chaque classe.

Alors pourquoi les gens définissent-ils des fonctions dans des espaces de noms anonymes en C ++ ou en tant que fonctions statiques de portée de fichier avec une liaison interne en C, cachées du monde extérieur? Et c'est surtout ça: les cacher du monde extérieur. Cela a un certain nombre d'effets, de la réduction des temps de compilation à la réduction de la complexité (ce qui n'est pas accessible ailleurs ne peut pas causer de problèmes ailleurs) et ainsi de suite. La testabilité des détails d'implémentation privés / internes n'est probablement pas la chose la plus importante dans l'esprit des gens lorsqu'ils le font, par exemple, en réduisant les temps de construction et en cachant la complexité inutile du monde extérieur.


la source
J'aimerais pouvoir voter dix fois. En tant que tangente liée aux logiciels critiques à haute mission, vous vous souciez beaucoup plus de l'exactitude des détails. Ce style idiomatique pour C ++ ne convient pas à cela. Je n'utiliserais pas les fonctionnalités de la langue comme les espaces de noms anonymes ou les modèles de type proxy. Je contrôlerais grossièrement mes limites avec des en-têtes d'espace utilisateur [pris en charge] et des en-têtes d'espace de développeur [non pris en charge].
David