A veces, las funciones privadas son simplemente unidades internas de funcionalidad aún por extraer. Entonces, ¿por qué no probarlos?

9

A veces, las funciones privadas de un módulo o clase son simplemente unidades internas de funcionalidad aún por extraer, que podrían merecer sus propias pruebas. Entonces, ¿por qué no probarlos? Nos vamos a escribir pruebas para ellos más adelante si / cuando se extraen. Entonces, ¿por qué no escribir las pruebas ahora, cuando todavía forman parte del mismo archivo?

Demostrar:

ingrese la descripción de la imagen aquí

Primero escribí module_a. Ahora quiero escribir pruebas para ello. Me gustaría probar la función 'privada' _private_func. No entiendo por qué no escribiría una prueba para él, si más tarde podría refactorizarlo en su propio módulo interno de todos modos, y luego escribir pruebas para él.


Supongamos que tengo un módulo con las siguientes funciones (también podría ser una clase):

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

_do_stuffy _do_more_stuffson funciones 'privadas' del módulo.

Entiendo la idea de que solo debemos probar la interfaz pública, no los detalles de implementación. Sin embargo, aquí está la cosa:

_do_stuffy _do_more_stuffcontienen la mayoría de la funcionalidad del módulo. Cada uno de ellos podría ser una función pública de un módulo 'interno' diferente. Pero aún no han evolucionado y son lo suficientemente grandes como para extraerse para separar archivos.

Por lo tanto, probar estas funciones se siente bien porque son unidades importantes de funcionalidad. Si estuvieran en diferentes módulos como funciones públicas, los habríamos probado. Entonces, ¿por qué no probarlos cuando aún no están (o nunca) extraídos en un archivo diferente?

Aviv Cohn
fuente
2
"Los métodos privados son beneficiosos para las pruebas unitarias ..." "... seguir el método privado me brinda una mejora útil y confiable en las pruebas unitarias. Por el contrario, el debilitamiento de las limitaciones de acceso" para la capacidad de prueba "solo me da un aspecto oscuro y difícil de entender pieza de código de prueba, que además está en riesgo permanente de ser roto por cualquier refactorización menor; francamente, lo que obtengo se parece sospechosamente a una deuda técnica "
mosquito
3
"¿Debo probar funciones privadas?" No. Nunca, nunca, nunca.
David Arno
2
@DavidArno ¿Por qué? ¿Cuál es la alternativa a las pruebas internas? ¿Solo pruebas de integración? ¿O hacer más cosas públicas? (aunque en mi experiencia
pruebo
1
Si es lo suficientemente importante como para que sea necesario escribir pruebas para ello, entonces ya debería estar en un módulo separado. Si no es así, entonces prueba su comportamiento utilizando la API pública.
Vincent Savard el

Respuestas:

14

La necesidad de probar no es lo mismo que la necesidad de ser público.

El código no trivial necesita pruebas independientemente de la exposición. El comportamiento no público no necesita existir y mucho menos ser probado.

Estas opiniones en conflicto pueden llevarlo a querer hacer públicas todas las funciones o negarse a descomponer el código en una función a menos que sea pública.

Esta no es la respuesta. Esté dispuesto a crear funciones de ayuda privadas. Pruébelos a través de la interfaz pública que lo utiliza tanto como sea posible.

Si las pruebas a través de la interfaz pública no ejercen la función privada lo suficiente, la función privada intenta permitir demasiado.

La validación puede ayudar a reducir lo que permite la función privada. Si no puede pasar un valor nulo a través de la interfaz pública, aún puede lanzar una excepción si aparece de todos modos.

¿Por qué deberías? ¿Por qué probar lo que nunca verás? Porque las cosas cambian. Puede ser privado ahora pero público después. El código de llamada podría cambiar. El código que rechaza explícitamente nulo deja en claro el uso adecuado y el estado esperado.

Por supuesto, nulo podría estar bien. Es solo un ejemplo aquí. Pero si espera algo, es útil dejar en claro esa expectativa.

Es posible que ese no sea el tipo de prueba que tenía en mente, pero con suerte estará dispuesto a crear funciones de ayuda privadas cuando sea apropiado.

El deseo de probar es bueno, pero no debería ser la fuerza impulsora en el diseño de su API pública. Diseñe la API pública para que sea fácil de usar. Es probable que no lo sea si todas las funciones son públicas. La API debería ser algo que las personas puedan entender cómo usar sin sumergirse en el código. No dejes que esas personas se pregunten para qué sirve esta extraña función auxiliar.

Ocultar funciones de ayuda pública en un módulo interno es un intento de respetar la necesidad de una API limpia al tiempo que expone a los ayudantes para las pruebas. No diré que esto está mal. Es posible que esté dando el primer paso hacia una capa arquitectónica diferente. Pero, por favor, domine el arte de probar las funciones de ayuda privadas a través de las funciones públicas que las usan primero. De esa manera, no usarás demasiado esta solución.

naranja confitada
fuente
Se me ocurrió un enfoque, me gustaría escuchar su opinión: cada vez que me encuentre en una situación en la que quiera probar una función privada, comprobaré si puedo probarla suficientemente a través de una de las funciones públicas. (incluidos todos los casos límite, etc.). Si puedo, no escribiré una prueba para esta función específicamente, sino que solo la probaré probando las funciones públicas que la usan. Sin embargo, si siento que la función no se puede probar lo suficiente a través de la interfaz pública y merece una prueba propia, la extraeré a un módulo interno donde sea pública y escribiré pruebas para ella. ¿Qué piensas?
Aviv Cohn
Diré que estoy preguntando lo mismo que los otros chicos que respondieron aquí :) Me gustaría escuchar la opinión de todos.
Aviv Cohn
De nuevo, no te diré que no. Me preocupa que no hayas dicho nada sobre vigilar cómo eso afecta la usabilidad. La diferencia entre público y privado no es estructural. Es el uso. Si la diferencia entre público y privado es una puerta de entrada y una puerta de atrás, entonces su trabajo es construir un cobertizo en el patio trasero. Multa. Mientras la gente no se pierda allí.
candied_orange
1
Votó a favor de "Si las pruebas a través de la interfaz pública no ejercen la función privada lo suficiente, la función privada está tratando de permitir demasiado".
Kris Welsh el
7

Respuesta corta: no

Respuesta más larga: sí, pero a través de la 'API' pública de su clase

La idea general de los miembros privados de una clase es que representan una funcionalidad que debería ser invisible fuera de la 'unidad' de código, por grande que sea la definición de esa unidad. En el código orientado a objetos, esa unidad a menudo termina siendo una clase.

Debe tener su clase diseñada de modo que sea posible invocar toda la funcionalidad privada a través de varias combinaciones de estado de entrada. Si encuentra que no hay una forma relativamente directa de hacer esto, probablemente sea una pista de que su diseño necesita más atención.


Después de aclarar la pregunta, esto es solo una cuestión de semántica. Si el código en cuestión puede funcionar como una unidad independiente separada, y se está probando como si fuera un código público, no puedo ver ningún beneficio de no moverlo a un módulo independiente. En la actualidad, solo sirve para confundir a los futuros desarrolladores (incluido usted, dentro de 6 meses), en cuanto a por qué el código aparentemente público está oculto dentro de otro módulo.

richzilla
fuente
Hola, gracias por tu respuesta :) Vuelve a leer la pregunta que he editado para aclarar.
Aviv Cohn el
Se me ocurrió un enfoque, me gustaría escuchar su opinión: cada vez que me encuentre en una situación en la que quiera probar una función privada, comprobaré si puedo probarla suficientemente a través de una de las funciones públicas. (incluidos todos los casos límite, etc.). Si puedo, no escribiré una prueba para esta función específicamente, sino que solo la probaré probando las funciones públicas que la usan. Sin embargo, si siento que la función no se puede probar lo suficiente a través de la interfaz pública y merece una prueba propia, la extraeré a un módulo interno donde sea pública y escribiré pruebas para ella. ¿Qué piensas?
Aviv Cohn
Diré que estoy preguntando lo mismo que los otros chicos que respondieron aquí :) Me gustaría escuchar la opinión de todos.
Aviv Cohn
5

El objetivo de las funciones privadas es que son detalles de implementación ocultos que se pueden cambiar a voluntad, sin cambiar la API pública. Para su código de ejemplo:

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

Si tiene una serie de pruebas que solo usa public_func, entonces si la reescribe para:

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

entonces, siempre y cuando el resultado de retorno para un valor particular de apermanezca igual, entonces todas sus pruebas serán buenas. Si el resultado devuelto cambia, una prueba fallará, destacando el hecho de que algo se rompió.

Todo esto es algo bueno: API pública estática; funcionamiento interno bien encapsulado; y pruebas robustas.

Sin embargo, si hubiera escrito pruebas para _do_stuffo _do_more_stuffy luego hubiera realizado el cambio anterior, ahora tendría un montón de pruebas rotas, no porque la funcionalidad cambiara, sino porque la implementación de esa funcionalidad cambió. Esas pruebas necesitarían reescribirse para funcionar con las nuevas funciones, pero una vez que estén funcionando, todo lo que sabría es que esas pruebas funcionaron con las nuevas funciones. Habría perdido las pruebas originales y, por lo tanto, no sabría si el comportamiento de public_funcha cambiado y, por lo tanto, sus pruebas serían básicamente inútiles.

Esto es algo malo: una API cambiante; funcionamiento interno expuesto estrechamente acoplado a las pruebas; y pruebas frágiles que cambian tan pronto como se realizan cambios en la implementación.

Entonces no, no pruebe las funciones privadas. Siempre.

David Arno
fuente
Hola David, gracias por tu respuesta. Vuelva a leer mi pregunta, he editado para aclarar.
Aviv Cohn el
Meh No hay nada de malo en probar las funciones internas. Personalmente, no escribo una función no trivial de ningún tipo a menos que pueda cubrirla con un par de pruebas unitarias, por lo que prohibir las pruebas en funciones internas me impediría escribir funciones internas, robándome una técnica muy útil. Las API pueden y cambian; cuando lo hacen, debes cambiar las pruebas. Refactorizar una función interna (y su prueba) no interrumpe las pruebas de la función externa; Ese es el punto de tener esas pruebas. Mal consejo en general.
Robert Harvey
Lo que realmente estás discutiendo es que no deberías tener funciones privadas.
Robert Harvey
1
@AvivCohn Son lo suficientemente grandes como para justificar pruebas, en cuyo caso son lo suficientemente grandes como para obtener su propio archivo; o son lo suficientemente pequeños como para que no necesite probarlos por separado. Entonces, ¿cuál es?
Doval
3
@RobertHarvey No, hace que el argumento "divida las clases grandes en componentes sueltos si es necesario". Si realmente no son públicos, probablemente sea un buen caso de uso para la visibilidad privada del paquete.
Doval