Aquí hay un código típico de C ++:
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 un código de C ++ idiomático decente: una clase, una función auxiliar matchque se usa mediante los métodos de Foo, algunas constantes para esa función auxiliar.
Y luego quiero escribir pruebas.
Sería perfectamente lógico escribir una prueba unitaria separada para match, porque es bastante no trivial.
Pero reside en un espacio de nombres anónimo.
Por supuesto que puedo escribir una prueba que llamaría Foo::f(). Sin embargo, no será una buena prueba si Fooes pesada y complicada, dicha prueba no aislará al examinado de otros factores no relacionados.
Así que tengo que moverme matchy todo lo demás fuera del espacio de nombres anónimo.
Pregunta: ¿cuál es el punto de poner funciones y constantes en el espacio de nombres anónimo, si los hace inutilizables en las pruebas?
fuente

foo.cpp, no el encabezado! OP parece entender bastante bien que no debe colocar espacios de nombres anon en un encabezado.friendno se recomienda abusar de la palabra clave para ese propósito. Combine eso con su suposición que si una restricción para un método lleva a una situación en la que ya no puede probarlo directamente, eso implicaría que los métodos privados no serían útiles.Respuestas:
Si desea probar unitariamente los detalles privados de implementación, haga el mismo tipo de esquiva para espacios de nombres sin nombre que para miembros de clase privados (o protegidos):
Entra y festeja.
Mientras que para las clases que abusa
friend, para los espacios de nombres sin nombre, abusa del#includemecanismo, que ni siquiera lo obliga a cambiar el código.Ahora que su código de prueba (o mejor solo algo para exponerlo todo) está en la misma TU, no hay problema.
Una palabra de precaución: si prueba los detalles de implementación, su prueba se romperá si cambian. Asegúrese de probar solo los detalles de implementación que se filtrarán de todos modos, o acepte que su prueba es inusualmente efímera.
fuente
La función en su ejemplo parece bastante compleja, y puede ser mejor moverla al encabezado, con el propósito de realizar pruebas unitarias.
Para aislarlos del resto del mundo. Y no solo las funciones y las constantes se pueden colocar en el espacio de nombres anónimo, sino también los tipos.
Sin embargo, si hace que las pruebas de su unidad sean muy complejas, entonces lo está haciendo mal. En tal caso, la función no pertenece allí. Entonces es hora de un poco de refactorización para simplificar las pruebas.
Por lo tanto, en el espacio de nombres anónimo solo deben ir funciones muy simples, a veces constantes y tipos (incluidos typedefs) utilizados en esa unidad de traducción.
fuente
El código que mostraste
matches un 1-liner bastante trivial sin ningún caso de borde complicado, ¿o es un ejemplo simplificado? De todos modos, supondré que está simplificado ...Esta pregunta es lo que quería hacerme saltar aquí, ya que Deduplicator ya mostró una forma perfectamente buena de entrar y obtener acceso a través del
#includeengaño. Pero la redacción aquí hace que parezca que probar cada detalle de implementación interna de todo es una especie de objetivo final universal, cuando está lejos de serlo.El objetivo de las pruebas unitarias uniformes no siempre es probar cada pequeña microunidad interna granular de funcionalidad. La misma pregunta se aplica a las funciones estáticas de alcance de archivo en C. Incluso puede hacer que la pregunta sea más difícil de responder preguntando por qué los desarrolladores usan
pimplsen C ++, lo que requeriría tantofriendshipy#includetrucos para la caja blanca, intercambiando la facilidad de prueba de los detalles de implementación para tiempos de compilación mejorados p.ejDesde un punto de vista pragmático, puede sonar asqueroso, pero
matchpuede que no se implemente correctamente con algunos casos extremos que hacen que se dispare. Sin embargo, si la única clase externaFoo, a la que tiene accesomatch, no puede usarla de una manera que encuentre esos casos límite, entonces es bastante irrelevante para la corrección deFooquematchtiene estos casos extremos que nunca se encontrarán a menosFooque haya cambios, en ese punto las pruebas deFoofallarán y lo sabremos de inmediato.Una mentalidad más obsesiva y ansiosa por probar cada detalle de implementación interna (tal vez un software de misión crítica, por ejemplo) podría querer entrar y divertirse, pero mucha gente no necesariamente piensa que esa es la mejor idea, ya que crearía el La mayoría de las pruebas frágiles imaginables. YMMV. Pero solo quería abordar la redacción de esta pregunta que hace que parezca que este tipo de comprobabilidad de nivel de detalle interno súper fino debe ser un objetivo final, cuando incluso la mentalidad de prueba de unidad más rigurosa podría relajarse un poco aquí y evite radiografiar los elementos internos de cada clase.
Entonces, ¿por qué las personas definen funciones en espacios de nombres anónimos en C ++ o como funciones estáticas de alcance de archivo con enlaces internos en C, ocultos del mundo exterior? Y eso es todo: esconderlos del mundo exterior. Eso tiene una serie de efectos desde reducir los tiempos de compilación hasta reducir la complejidad (a lo que no se puede acceder en otro lugar no puede causar problemas en otros lugares) y así sucesivamente. Probablemente la capacidad de prueba de los detalles de implementación privados / internos no es lo más importante en la mente de las personas cuando lo hacen, por ejemplo, reduciendo los tiempos de construcción y ocultando la complejidad innecesaria del mundo exterior.
fuente