Anonyme Namespaces machen Code nicht testbar

13

Hier ist ein typischer C ++ - Code:

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

Es sieht aus wie ein anständiger idiomatischer C ++ - Code - eine Klasse, eine Hilfsfunktion, matchdie von den Methoden Fooeiniger Konstanten für diese Hilfsfunktion verwendet wird.

Und dann möchte ich Tests schreiben.
Es wäre völlig logisch, einen separaten Komponententest zu schreiben match, da dies nicht trivial ist.
Es befindet sich jedoch in einem anonymen Namespace.
Natürlich kann ich einen Test schreiben, der anrufen würde Foo::f(). Allerdings ist es kein guter Test, wenn er Fooschwer und kompliziert ist. Ein solcher Test kann den Testteilnehmer jedoch nicht von anderen Faktoren isolieren, die nichts damit zu tun haben.

Also muss ich umziehen matchund alles andere aus dem anonymen Namensraum raus.

Frage: Wozu sollten Funktionen und Konstanten in den anonymen Namespace eingefügt werden, wenn sie dadurch in Tests unbrauchbar werden?

Abyx
quelle
3
@ BЈовић Code erneut lesen - der anonyme Namespace ist in foo.cpp, nicht der Header! OP scheint recht gut zu verstehen, dass Sie in einem Header keine Namespaces setzen sollten.
amon
Eine Idee beim Unit-Testen von C ++ - Code in einem unbenannten Namespace ... aber der "Punkt" ist, dass die Kapselung gut ist. Immerhin ist es das gleiche Problem, das Sie mit privaten Member-Funktionen haben: Sie können nicht unauffällig getestet werden, aber Sie möchten nicht aufgeben, Informationen für Unit-Tests zu verbergen (z . B. stackoverflow.com/a/3676680/3235496 ).
Manlio
1
Ihr Fall unterscheidet sich konzeptionell nicht sehr vom Fall des Testens (oder Nicht-Testens) privater Methoden. Wenn Sie also hier bei Programmierern nach "unit test private" suchen, erhalten Sie viele Antworten, die Sie direkt auf Ihren Fall anwenden können.
Doc Brown
@ DocBrown Ich frage nicht, wie man Funktionen in anon testet. Namespaces. Ich frage "warum Code in anon. Namespaces setzen?" (siehe den Text am Ende der Frage). Beschuldigen Sie Ixrec, den Titel in etwas anderes zu ändern.
Abyx
1
@ Byx: In diesen anderen Antworten, die ich oben erwähnt habe, werden Sie einen großen Konsens vieler Experten finden, dass es eine wirklich schlechte Idee ist, private Methoden zu testen, und dass der Missbrauch des friendSchlüsselworts für diesen Zweck nicht empfohlen wird. Kombinieren Sie dies mit Ihrer Annahme Wenn eine Einschränkung für eine Methode zu einer Situation führt, in der Sie sie nicht mehr direkt testen können, bedeutet dies, dass private Methoden nicht nützlich waren.
Doc Brown

Antworten:

7

Wenn Sie die Details der privaten Implementierung einem Komponententest unterziehen möchten, weichen Sie für unbenannte Namespaces auf die gleiche Weise aus wie für private (oder geschützte) Klassenmitglieder:

Einbruch und Party.

Während für Klassen, die Sie missbrauchen friend, für unbenannte Namespaces der #include-Mechanismus missbraucht wird , werden Sie nicht einmal gezwungen, den Code zu ändern.
Jetzt, da sich Ihr Testcode (oder besser gesagt, nur etwas, um alles freizulegen) in derselben TU befindet, gibt es kein Problem mehr.

Ein Wort zur Vorsicht: Wenn Sie die Implementierungsdetails testen, wird Ihr Test abgebrochen, wenn sich diese ändern. Stellen Sie wirklich sicher, dass Sie nur die Implementierungsdetails testen, die ohnehin lecken, oder akzeptieren Sie, dass Ihr Test ungewöhnlich kurzlebig ist.

Deduplizierer
quelle
6

Die Funktion in Ihrem Beispiel sieht ziemlich komplex aus, und es ist möglicherweise besser, sie zum Zweck des Komponententests in die Kopfzeile zu verschieben.

Was nützt es, Funktionen und Konstanten in den anonymen Namespace zu setzen, wenn sie dadurch in Tests unbrauchbar werden?

Um sie vom Rest der Welt zu isolieren. Und Sie können nicht nur Funktionen und Konstanten in den anonymen Namespace eingeben, sondern auch Typen.

Wenn es Ihre Einheitentests jedoch sehr komplex macht, machen Sie es falsch. In diesem Fall gehört die Funktion nicht dorthin. Dann ist es Zeit für ein kleines Refactoring, um das Testen zu vereinfachen.

Daher sollten in anonymen Namespaces nur sehr einfache Funktionen verwendet werden, manchmal Konstanten und Typen (einschließlich typedefs), die in dieser Übersetzungseinheit verwendet werden.

BЈовић
quelle
5

Es wäre absolut logisch, einen separaten Komponententest für das Spiel zu schreiben, da dies nicht trivial ist.

Der Code, für den Sie gezeigt haben, matchist ein ziemlich trivialer 1-Liner ohne schwierige Randfälle, oder ist das wie ein vereinfachtes Beispiel? Wie auch immer, ich gehe davon aus, dass es vereinfacht ist ...

Frage: Wozu sollten Funktionen und Konstanten in den anonymen Namespace eingefügt werden, wenn sie dadurch in Tests unbrauchbar werden?

Diese Frage wollte mich dazu bringen, hier einzuspringen, da Deduplicator bereits eine sehr gute Möglichkeit zum Einbruch und Durchstieg zeigte #include Tricks einzudringen . Der Wortlaut hier klingt jedoch so, als würde man jedes einzelne interne Implementierungsdetail von allem testen. Dies ist eine Art universelles Endziel, wenn es weit davon entfernt ist.

Das Ziel selbst von Unit-Tests besteht nicht immer darin, jede kleine granulare interne Mikro-Funktionseinheit zu testen. Dieselbe Frage gilt für statische Dateibereichsfunktionen in C. Sie können die Beantwortung der Frage sogar erschweren, indem Sie fragen, warum Entwickler pimplsin C ++ verwenden, was beides friendship und #includeTricks in der White Box erfordern würde. z.B

Aus einer Art pragmatischer Perspektive mag es grob klingen, aber matchmit einigen Randfällen, die dazu führen, dass es auslöst, möglicherweise nicht richtig implementiert werden. Wenn jedoch die einzige äußere Klasse, Foodie Zugriff darauf hat, matches möglicherweise nicht in einer Weise verwenden kann, die auf diese Randfälle stößt Foo, matchist dies für die Richtigkeit dieser Randfälle, die niemals angetroffen werden, außer wenn Foosich an welchem ​​Punkt etwas ändert, irrelevant Die Tests von Foowerden fehlschlagen und wir werden es sofort wissen.

Eine obsessivere Denkweise, die jedes einzelne interne Implementierungsdetail testen möchte (möglicherweise eine unternehmenskritische Software, z. B.), möchte vielleicht einbrechen und feiern, aber viele Leute denken nicht unbedingt, dass dies die beste Idee ist, da sie das schaffen würde spröde Tests vorstellbar. YMMV. Aber ich wollte mich nur mit dem Wortlaut dieser Frage befassen, der so klingt, als ob diese Art von überfeinkörniger Testbarkeit auf interner Detailebene ein Endziel sein sollte, wenn sich auch die strengste Einstellung zum Testen von Einheiten hier etwas entspannen könnte und vermeiden Sie es, die Einbauten jeder Klasse zu durchleuchten.

Warum definieren die Benutzer Funktionen in anonymen Namespaces in C ++ oder als statische Funktionen im Dateibereich mit interner Verknüpfung in C, die vor der Außenwelt verborgen sind? Und das war es vor allem: Sie vor der Außenwelt zu verstecken. Dies hat eine Reihe von Effekten, von der Verkürzung der Kompilierungszeiten bis zur Reduzierung der Komplexität (auf was anderswo nicht zugegriffen werden kann, kann an anderer Stelle keine Probleme verursachen) und so weiter. Wahrscheinlich ist die Überprüfbarkeit von privaten / internen Implementierungsdetails nicht das Wichtigste, wenn die Leute dies erledigen, um beispielsweise die Erstellungszeiten zu verkürzen und unnötige Komplexität vor der Außenwelt zu verbergen.


quelle
Ich wünschte, ich könnte das zehnmal verbessern. Als Tangens im Zusammenhang mit unternehmenskritischer Software mit hoher Zuverlässigkeit geht es Ihnen viel mehr um die Richtigkeit von Details. Dieser idiomatische Stil für C ++ ist dafür nicht geeignet. Ich würde keine Funktionen der Sprache wie anonyme Namespaces oder proxyartige Muster verwenden. Ich würde meine Grenze grob mit [unterstützten] User-Space-Headern und [nicht unterstützten] Developer-Space-Headern steuern.
David