¿Cuál es la mejor manera de probar los métodos protegidos y privados en Ruby, utilizando el Test::Unit
marco estándar de Ruby ?
Estoy seguro de que alguien hablará y afirmará dogmáticamente que "solo debe realizar una prueba unitaria de los métodos públicos; si necesita una prueba unitaria, no debería ser un método protegido o privado", pero no estoy realmente interesado en debatir eso. Tengo varios métodos que están protegidos o privados por razones buenas y válidas, estos métodos privados / protegidos son moderadamente complejos, y los métodos públicos en la clase dependen de que estos métodos protegidos / privados funcionen correctamente, por lo tanto, necesito una forma de probar Los métodos protegidos / privados.
Una cosa más ... Generalmente pongo todos los métodos para una clase dada en un archivo, y las pruebas unitarias para esa clase en otro archivo. Idealmente, me gustaría toda la magia para implementar esta funcionalidad de "prueba unitaria de métodos protegidos y privados" en el archivo de prueba unitaria, no en el archivo fuente principal, para mantener el archivo fuente principal lo más simple y directo posible.
fuente
Respuestas:
Puede omitir la encapsulación con el método de envío:
Esta es una 'característica' de Ruby. :)
Hubo un debate interno durante el desarrollo de Ruby 1.9 que consideró
send
respetar la privacidad esend!
ignorarla, pero al final nada cambió en Ruby 1.9. Ignora los comentarios a continuación discutiendosend!
y rompiendo cosas.fuente
send!
lugar, que fue revocada hace mucho tiempo,send/__send__
puede llamar a los métodos de toda la visibilidad - redmine.ruby-lang.org/repositories/revision/1?rev=13824public_send
(documentación aquí ) si desea respetar la privacidad. Creo que eso es nuevo para Ruby 1.9.Aquí hay una manera fácil si usa RSpec:
fuente
Simplemente vuelva a abrir la clase en su archivo de prueba y redefina el método o los métodos como públicos. No tiene que redefinir las agallas del método en sí, solo pase el símbolo en la
public
llamada.Si su clase original se define así:
En su archivo de prueba, simplemente haga algo como esto:
Puede pasar varios símbolos a
public
si desea exponer métodos más privados.fuente
instance_eval()
podría ayudar:Puede usarlo para acceder a métodos privados y variables de instancia directamente.
También podría considerar el uso
send()
, que también le dará acceso a métodos privados y protegidos (como sugirió James Baker)Alternativamente, puede modificar la metaclase de su objeto de prueba para hacer públicos los métodos privados / protegidos solo para ese objeto.
Esto le permitirá llamar a estos métodos sin afectar a otros objetos de esa clase. Puede volver a abrir la clase dentro de su directorio de prueba y hacerlos públicos para todas las instancias dentro de su código de prueba, pero eso podría afectar su prueba de la interfaz pública.
fuente
Una forma de hacerlo en el pasado es:
fuente
También podría refactorizarlos en un nuevo objeto en el que esos métodos son públicos y delegarlos en privado en la clase original. Esto le permitirá probar los métodos sin metaruby mágico en sus especificaciones mientras los mantiene privados.
¿Cuáles son esas razones válidas? Otros lenguajes de OOP pueden escapar sin métodos privados (smalltalk viene a la mente, donde los métodos privados solo existen como una convención).
fuente
Similar a la respuesta de @ WillSargent, esto es lo que he usado en un
describe
bloque para el caso especial de probar algunos validadores protegidos sin necesidad de pasar por el pesado proceso de crearlos / actualizarlos con FactoryGirl (y podría usar deprivate_instance_methods
manera similar):fuente
Para hacer públicos todos los métodos protegidos y privados para la clase descrita, puede agregar lo siguiente a su spec_helper.rb y no tener que tocar ninguno de sus archivos de especificaciones.
fuente
Puede "volver a abrir" la clase y proporcionar un nuevo método que delegue al privado:
fuente
Probablemente me inclinaría hacia el uso de instance_eval (). Sin embargo, antes de saber sobre instance_eval (), creaba una clase derivada en mi archivo de prueba unitaria. Luego establecería los métodos privados para que sean públicos.
En el ejemplo a continuación, el método build_year_range es privado en la clase PublicationSearch :: ISIQuery. Derivar una nueva clase solo con fines de prueba me permite establecer uno o varios métodos para que sean públicos y, por lo tanto, directamente comprobables. Del mismo modo, la clase derivada expone una variable de instancia llamada 'resultado' que anteriormente no estaba expuesta.
En mi prueba unitaria, tengo un caso de prueba que crea una instancia de la clase MockISIQuery y prueba directamente el método build_year_range ().
fuente
En Test :: Unit framework puede escribir,
Aquí "nombre_método" es un método privado.
y mientras llama a este método puede escribir,
fuente
Aquí hay una adición general a la clase que uso. Es un poco más escopeta que solo hacer público el método que está probando, pero en la mayoría de los casos no importa, y es mucho más legible.
El uso de métodos de envío / acceso protegidos / privados se divide en 1.9, por lo que no es una solución recomendada.
fuente
Para corregir la respuesta superior anterior: en Ruby 1.9.1, es Object # send el que envía todos los mensajes, y Object # public_send que respeta la privacidad.
fuente
En lugar de obj.send, puede usar un método singleton. Son 3 líneas más de código en su clase de prueba y no requiere cambios en el código real para ser probado.
En los casos de prueba, puede usarlos
my_private_method_publicly
cuando quieramy_private_method
.http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html
obj.send
para métodos privados fue reemplazado porsend!
en 1.9, pero luegosend!
fue eliminado nuevamente. Entoncesobj.send
funciona perfectamente bien.fuente
Sé que llego tarde a la fiesta, pero no pruebes métodos privados ... No puedo pensar en una razón para hacerlo. Un método de acceso público está utilizando ese método privado en alguna parte, pruebe el método público y la variedad de escenarios que causarían el uso de ese método privado. Algo entra, algo sale. Probar métodos privados es un gran no-no, y hace que sea mucho más difícil refactorizar su código más adelante. Son privados por una razón.
fuente
Para hacer esto:
Puede implementar esto en su archivo test_helper:
fuente
Disrespect
aPrivacyViolator
(: P) e hice que eldisrespect_privacy
método edite temporalmente el enlace del bloque, para recordar el objeto objetivo al objeto contenedor, pero solo por la duración de la cuadra. De esa manera no necesita usar un parámetro de bloque, simplemente puede continuar haciendo referencia al objeto con el mismo nombre.