¡Dar una buena acogida! Si se perdió la primera parte de nuestro viaje hasta el momento, es posible que desee regresar y ponerse al día primero..
Hasta ahora, hemos aplicado un proceso de desarrollo impulsado por pruebas para construir nuestra aplicación, junto con la utilización del popular marco de pruebas RSpec. A partir de aquí, vamos a investigar algunas otras características de RSpec, así como a utilizar la gema Pry para ayudarte a depurar y escribir tu código..
Tomemos un momento para revisar otras características de RSpec que no hemos necesitado en esta sencilla aplicación de ejemplo, pero puede que encuentre útil trabajar en su propio proyecto..
Imagina que escribes una prueba pero te interrumpen, o necesitas irte a una reunión y aún no has completado el código requerido para que la prueba sea aprobada.
Puede eliminar la prueba y volver a escribirla más tarde cuando pueda volver a su trabajo. O alternativamente, puedes comentar el código, pero eso es bastante feo y definitivamente no es bueno cuando usas un sistema de control de versiones..
Lo mejor que se puede hacer en esta situación es definir nuestra prueba como 'pendiente', de modo que cada vez que se ejecuten las pruebas, el marco de prueba ignorará la prueba. Para hacer esto necesitas usar el pendiente
palabra clave:
describa "algún método" hágalo "debería hacer algo" pendiente del final
Todos los buenos marcos de prueba le permiten ejecutar código antes y después de ejecutar cada prueba. RSpec no es diferente.
Nos proporciona antes de
y después
métodos que nos permiten configurar un estado específico para que se ejecute nuestra prueba, y luego limpiar ese estado después de que se haya ejecutado la prueba (esto es para que el estado no se filtre y afecte el resultado de pruebas posteriores).
describe "algún método" hacer antes (: cada uno) hacer # un código de configuración finalizar después de (: cada uno) hacer # un código desmontable terminar "debería hacer algo" pendiente de un extremo final
Ya hemos visto el describir
bloquear; Pero hay otro bloque que es funcionalmente equivalente llamado contexto
. Puedes usarlo en cualquier lugar que uses describir
.
La diferencia entre ellos es sutil pero importante: contexto
Nos permite definir un estado para nuestra prueba. Sin embargo, no explícitamente (en realidad no establecemos el estado definiendo un contexto
bloque: en cambio, es para facilitar la lectura, por lo que la intención del siguiente código es más clara).
Aquí hay un ejemplo:
describe "Algún método" hacer contexto "bloque proporcionado" hacerlo "cede al bloque" hacer pendiente extremo final contexto "no bloque proporcionado" hacerlo "llama a un método de reserva" hacer pendiente final extremo final
Podemos usar el talón
Método para crear una versión falsa de un objeto existente y hacer que devuelva un valor predeterminado.
Esto es útil para evitar que nuestras pruebas toquen las API de servicio en vivo y para guiar nuestras pruebas dando resultados predecibles de ciertas llamadas.
Imagina que tenemos una clase llamada Persona
y que esta clase tiene una hablar
método. Queremos probar que el método funciona como esperamos que lo haga. Para ello vamos a tentar el hablar
Método utilizando el siguiente código:
Describe a la persona hazlo "hablar ()" do bob = stub () bob.stub (: speak) .and_return ('hello') Person.any_instance.stub (: initialize) .and_return (bob) instance = Person.new expect ( instance.speak) .to eq ('hello') end end
En este ejemplo, decimos que 'cualquier instancia' de la Persona
la clase debe tener su inicializar
método aplastado por lo que devuelve el objeto mover
.
Te darás cuenta que mover
es en sí mismo un código auxiliar que se configura de modo que cada vez que el código intente ejecutar el código hablar
Método devolverá "hola".
Luego procedemos a crear un nuevo Persona
instancia y pasar la llamada de instancia.speak
en RSpec's esperar
sintaxis.
Le decimos a RSpec que estamos esperando que esa llamada dé como resultado la cadena "hola".
En los ejemplos anteriores hemos usado la característica RSpec y volver
para indicar lo que nuestro talón debe devolver cuando se llama.
Podemos indicar un valor de retorno diferente cada vez que se llama al apéndice especificando múltiples argumentos a la y volver
método:
obj = stub () obj.stub (: foo) .and_return (1, 2, 3) expect (obj.foo ()). hasta eq (1) expect (obj.foo ()). hasta eq (2) expect (obj.foo ()). a eq (3)
Los simulacros son similares a los stubs ya que estamos creando versiones falsas de nuestros objetos, pero en lugar de devolver un valor predefinido, orientamos más específicamente las rutas de nuestros objetos. debe tomar para que la prueba sea válida.
Para ello utilizamos el burlarse de
método:
describa Obj. "testing ()" do bob = mock () bob.should_receive (: testing) .with ('content') Obj.any_instance.stub (: initialize) .and_return (bob) instance = Obj.new instance. final de prueba ('algo de valor')
En el ejemplo anterior creamos una nueva Objeto
instancia y luego llamar a su pruebas
método.
Detrás de las escenas de ese código esperamos que el pruebas
Método a llamar con el valor 'contenido'
. Si no se llama con ese valor (que en el ejemplo anterior no lo es), entonces sabemos que parte de nuestro código no ha funcionado correctamente..
los tema
palabra clave se puede utilizar en un par de maneras diferentes. Todos los cuales están diseñados para reducir la duplicación de código.
Puedes usarlo implícitamente (nota nuestro eso
bloque no hace referencia tema
en absoluto):
describe Array describe "con 3 elementos" do asunto [1,2,3] it should_not be_empty end end end
Puedes usarlo explícitamente (nota nuestra eso
bloque se refiere a tema
directamente):
describe MyClass describe "inicialización" do asunto MyClass "crea una nueva instancia" do instance = subject.new expect (instance) .to be_a (MyClass) end end end end end
En lugar de hacer referencia constantemente a un tema dentro de su código y pasar valores diferentes para la creación de instancias, por ejemplo:
describe "Foo" do contexto "A" hazlo "Bar" do baz = Baz.new ('a') espera (baz.type) .to eq ('a') final contexto "B" hazlo "Bar" hacer baz = Baz.new ('b') esperar (baz.type). hasta eq ('b') terminar el contexto final "C" hacerlo "Bar" hacer baz = Baz.new ('c') esperar (baz .type) .to eq ('c') end end end end
En su lugar puedes usar tema
junto con dejar
Para reducir la duplicación:
describa "Person" do subject Person.new (name) # Person tiene un contexto del método get_name "Bob" do let (: name) 'Bob' su (: get_name) should == 'Bob' end context "Joe" no deje que (: nombre) 'Joe' es su (: obtener nombre) debería == 'Joe' contexto final "Smith" deja que (: nombre) 'Smith' es (: obtener nombre) debería == 'Smith' end end end
Hay muchas más funciones disponibles para RSpec, pero hemos analizado las más importantes que encontrará usando mucho al escribir pruebas utilizando RSpec..
Puede configurar RSpec para ejecutar sus pruebas en un orden aleatorio. Esto le permite asegurarse de que ninguna de sus pruebas dependa o dependa de las otras pruebas a su alrededor..
RSpec.configure do | config | config.order = final 'aleatorio'
Puede configurar esto a través del comando usando el --orden
bandera / opción Por ejemplo: rspec - orden aleatorio
.
Cuando usas el --orden aleatorio
La opción RSpec mostrará el número aleatorio que utilizó para inicializar el algoritmo. Puede usar este valor 'inicial' nuevamente cuando crea que ha descubierto un problema de dependencia dentro de sus pruebas. Una vez que haya solucionado lo que cree que es el problema, puede pasar el valor semilla a RSpec (por ejemplo, si la semilla fue 1234
luego ejecuta --orden aleatorio: 1234
) y usará esa misma semilla aleatoria para ver si puede replicar el error de dependencia original.
Usted ha visto que hemos agregado un conjunto específico de objetos de configuración dentro de nuestro Rakefile
. Pero puede configurar las opciones de configuración globalmente agregándolas a un .rspec
archivo dentro de su directorio de inicio.
Por ejemplo, dentro .rspec
:
--color - formato anidado
Ahora estamos listos para comenzar a investigar cómo podemos depurar nuestra aplicación y nuestro código de prueba usando la gema Pry.
Es importante entender que aunque Pry es realmente bueno para depurar su código, en realidad está pensado como una herramienta REPL de Ruby mejorada (para reemplazar irb
) y no estrictamente propósitos de depuración; así, por ejemplo, no hay funciones integradas como: paso a paso, paso o paso, etc. que normalmente encontrará en una herramienta diseñada para la depuración.
Pero como una herramienta de depuración, Pry es muy concentrado y delgado.
Volveremos a la depuración en un momento, pero primero revisemos cómo usaremos Pry inicialmente.
Con el fin de demostrar Pry, voy a agregar más código a mi aplicación de ejemplo (este código adicional no afecta nuestra prueba de ninguna manera)
class RSpecGreeter attr_accessor: test @@ class_property = "Soy una propiedad de clase" def greet binding.pry @instance_property = "Soy una propiedad de instancia" pubs privs "Hello RSpec!" end def pubs test_var = "Soy una variable de prueba" test_var end private def privs pone "I'm private" end end
Notará que hemos agregado algunos métodos adicionales, propiedades de instancia y clase. También hacemos llamadas a dos de los nuevos métodos que hemos agregado desde nuestro saludar
método.
Por último, notarás el uso de vinculante.pry
.
vinculante.pry
Un punto de ruptura es un lugar dentro de su código donde la ejecución se detendrá.
Puede tener múltiples puntos de interrupción establecidos dentro de su código y puede crearlos usando vinculante.pry
.
Cuando ejecute su código, notará que el terminal se detendrá y lo ubicará dentro del código de su aplicación en el lugar exacto donde se colocó su enlace..
A continuación se muestra un ejemplo de cómo podría verse ...
8: def greet => 9: binding.pry 10: pubs 11: privs 12: "Hello RSpec!" 13: fin
Desde este punto, Pry tiene acceso al ámbito local, por lo que puede usar Pry como lo haría en irb
y comience a escribir, por ejemplo, variables para ver qué valores tienen.
Puede ejecutar el salida
comando para salir de Pry y para que su código continúe ejecutándose.
Dónde estoy
Cuando se usa mucho vinculante.pry
puntos de ruptura puede ser difícil de entender en qué parte de la aplicación se encuentra.
Para obtener un mejor contexto de dónde se encuentra en cualquier momento, puede utilizar el Dónde estoy
mando.
Cuando se ejecuta solo verá algo similar a cuando usó vinculante.pry
(Verá la línea en la que se estableció el punto de quiebre y un par de líneas arriba y abajo). La diferencia es que si pasas un argumento numérico extra como dónde son 5
verá cinco líneas adicionales encima de donde vinculante.pry
fue puesto. Podría solicitar ver 100 líneas alrededor del punto de quiebre actual, por ejemplo..
Este comando puede ayudarte a orientarte dentro del archivo actual.
wtf
los wtf
comando significa "what the f ***" y proporciona un seguimiento completo de la pila para la excepción más reciente que se ha lanzado. Puede ayudarlo a comprender los pasos que conducen al error que ocurrió.
ls
los ls
comando muestra qué métodos y propiedades están disponibles para Pry.
Cuando lo ejecutes te mostrará algo como ...
RSpecGreeter # métodos: greet pubs test test = variables de clase: @@ class_property locals: _ __ _dir_ _ex_ _file_ _in_ _out_ _pry_
En el ejemplo anterior, podemos ver que tenemos cuatro métodos públicos (recuerde que actualizamos nuestro código para incluir algunos métodos adicionales y luego prueba
y prueba =
fueron creados cuando se usa Ruby attr_accessor
mano corta).
También muestra otras variables de clase y locales a las que Pry puede acceder..
Otra cosa útil que puedes hacer es grep (buscar) los resultados solo para lo que te interesa. Necesitarás tener un entendimiento de las expresiones regulares, pero puede ser una técnica útil. Aquí hay un ejemplo…
ls -p -G ^ p => RSpecGreeter # métodos: privs
En el ejemplo anterior estamos usando el -pag
y -sol
opciones / banderas que indican a Pry que solo queremos ver métodos públicos y privados y usamos la expresión regular ^ p
(lo que significa emparejar cualquier cosa que comience con pag
) Como nuestro patrón de búsqueda para filtrar los resultados..
Corriendo ls - ayuda
También te mostrará todas las opciones disponibles..
discos compactos
Puede cambiar el alcance actual usando el discos compactos
mando.
En nuestro ejemplo si corremos cd… / pubs
Nos llevará al resultado de esa llamada al método..
Si ahora corremos Dónde estoy
verá que se mostrará Dentro de "Soy una variable de prueba"
.
Si corremos yo
entonces verás que obtenemos "Soy una variable de prueba"
devuelto.
Si corremos clase propia
ya veremos Cuerda
devuelto.
Puedes subir la cadena de alcance usando discos compactos…
o puede volver al nivel superior del alcance usando discos compactos /
.
Nota: podríamos añadir otra. vinculante.pry
dentro de pubs
Método y luego nuestro alcance estaría dentro de ese método en lugar del resultado del método.
anidando
Considere el ejemplo anterior de correr pubs cd
. Si corremos el anidando
comando obtendremos una vista de nivel superior en la cantidad de contextos / niveles que Pry tiene actualmente:
Estado de anidación: - 0. # (nivel superior de palanca) 1. "Soy una variable de prueba"
Desde alli podemos correr salida
para volver al contexto anterior (por ejemplo, dentro de saludar
método)
Corriendo salida
nuevamente significará que estamos cerrando el último contexto que Pry tiene y así Pry termina y nuestro código continúa ejecutándose.
método de búsqueda
Si no está seguro de dónde encontrar un método en particular, puede utilizar el método de búsqueda
comando para mostrarle todos los archivos dentro de su base de código que tienen un método que coincide con lo que está buscando:
find-method priv => Kernel Kernel # private_methods Module Module # private_instance_methods Module # private_constant Module # private_method_defined? Módulo # private_class_method Módulo # private RSpecGreeter RSpecGreeter # privs
También puedes usar el -do
Opción / bandera para buscar el contenido de los archivos en su lugar:
find-method -c greet => RSpecGreeter RSpecGreeter: def greet RSpecGreeter # privs: greet
siguiente
, paso
, continuar
Aunque las técnicas anteriores son útiles, no es realmente 'depuración' en el mismo sentido que lo que probablemente estás acostumbrado.
Para la mayoría de los desarrolladores, su editor o navegador les proporcionará una herramienta de depuración incorporada que les permite recorrer su código línea por línea y seguir la ruta que el código toma hasta su finalización..
Como Pry está desarrollado para ser usado como REPL, no quiere decir que no sea útil para la depuración..
Una solución ingenua sería establecer múltiples vinculante.pry
declaraciones a través de un método y uso ctrl-d para moverse a través de cada conjunto de puntos de ruptura. Pero eso todavía no es lo suficientemente bueno..
Para la depuración paso a paso puede cargar la gema pry-nav ...
fuente "https://rubygems.org" gema 'rspec' grupo: desarrollo hacer gema 'guardia' gema 'guardia-rspec' gema '#' Agrega pasos de depuración a Pry # continuar, paso, siguiente gema 'pry-remoto' gema 'pry-nav' final
Esta gema extiende Pry para que entienda los siguientes comandos:
Siguiente
(pasar a la siguiente línea)Paso
(Mueve a la siguiente línea y si es un método, muévete a ese método)Continuar
(Ignora cualquier otro punto de ruptura en este archivo)Como beneficio adicional, integremos nuestras pruebas con el servicio en línea de CI (integración continua) Travis-CI.
El principio de CI es cometer / presionar temprano y con frecuencia para evitar conflictos entre su código y la rama maestra. Cuando lo haga (en este caso nos comprometemos con GitHub), entonces eso debería iniciar una 'compilación' en su servidor de CI que ejecuta las pruebas relevantes para garantizar que todo funcione como debería..
Ahora, con TDD como su principal metodología de desarrollo, es menos probable que incurra en errores cada vez que empuja debido a que sus pruebas son una parte integral de su flujo de trabajo de desarrollo y, por lo tanto, antes de que empiece, ya estará al tanto de los errores o las regresiones. Pero esto no lo protege necesariamente de los errores que se producen en las pruebas de integración (donde todos los códigos de varios sistemas se ejecutan juntos para garantizar que el sistema "en su conjunto" funcione correctamente).
En cualquier caso, el código nunca debe enviarse directamente a su servidor de producción en vivo; siempre debe enviarse primero a un servidor de CI para ayudar a detectar cualquier error potencial que surja de las diferencias entre su entorno de desarrollo y el entorno de producción.
Muchas empresas tienen más entornos para que pase su código antes de que llegue al servidor de producción en vivo.
Por ejemplo, en BBC News tenemos:
Aunque cada entorno debe ser idéntico en su configuración, el propósito es implementar diferentes tipos de pruebas para garantizar que se detecten y solucionen tantos errores antes de que el código llegue a "vivo"..
Travis CI es un servicio de integración continua alojado para la comunidad de código abierto. Está integrado con GitHub y ofrece soporte de primera clase para múltiples idiomas.
Lo que esto significa es que Travis-CI ofrece servicios gratuitos de IC para proyectos de código abierto y también tiene un modelo pagado para las empresas y organizaciones que desean mantener en privado su integración de CI..
Usaremos el modelo gratuito de código abierto en nuestro repositorio GitHub de ejemplo.
El proceso es este:
.travis.yml
Archivo dentro del directorio raíz de su proyecto y confírmelo en su repositorio GitHubEl paso final es el más importante (creando un .travis.yml
archivo) ya que esto determina los ajustes de configuración de Travis-CI para que sepa cómo manejar la ejecución de las pruebas para su proyecto.
Echemos un vistazo a la .travis.yml
Archivo que estamos usando para nuestro repositorio GitHub de ejemplo:
idioma: ruby caché: bundler rvm: - 2.0.0 - 1.9.3 script: 'bundle exec rake spec' bundler_args: --sin las ramas de desarrollo: solo: - notificaciones maestras: correo electrónico: - [email protected]
Vamos a desglosar pieza por pieza ...
Primero especificamos qué idioma estamos usando en nuestro proyecto. En este caso estamos usando Ruby: idioma: rubí
.
Debido a que ejecutar Bundler puede ser un poco lento y sabemos que nuestras dependencias no van a cambiar, que a menudo podemos optar por almacenar las dependencias en caché, así que configuramos caché: bundler
.
Travis-CI usa RVM (Ruby Version Manager) para instalar Rubies en sus servidores. Así que necesitamos especificar contra qué versiones de Ruby queremos ejecutar nuestras pruebas. En este caso hemos elegido 2.0
y 1.9.3
que son dos versiones populares de Ruby (técnicamente nuestra aplicación usa Ruby 2, pero también es bueno saber que nuestro código pasa en otras versiones de Ruby):
rvm: - 2.0.0 - 1.9.3
Para ejecutar nuestras pruebas sabemos que podemos usar el comando rastrillo
o rastrillo espec
. Travis-CI por defecto ejecuta el comando rastrillo
pero debido a cómo se instalan las gemas en Travis-CI usando Bundler, necesitamos cambiar el comando predeterminado: script: 'bundle exec rake spec'
. Si no hiciéramos esto, entonces Travis-CI tendría un problema para localizar el rspec / core / rake_task
archivo que se especifica dentro de nuestra Rakefile
.
Nota: si tiene algún problema con Travis-CI, puede unirse al canal #travis en IRC freenode para obtener ayuda para responder cualquier pregunta que pueda tener. Ahí es donde descubrí la solución a mi problema con Travis-CI al no poder ejecutar mis pruebas usando su valor predeterminado rastrillo
comando y la sugerencia de sobrescribir el valor predeterminado con paquete de rastrillo ejecutivo
resuelto ese problema.
Luego, debido a que solo estamos interesados en ejecutar nuestras pruebas, podemos pasar argumentos adicionales a Travis-CI para filtrar gemas que no queremos molestar en la instalación. Así que para nosotros queremos excluir la instalación de gemas agrupadas como desarrollo: bundler_args: --sin desarrollo
(Esto significa que estamos excluyendo gemas que solo se usan realmente para el desarrollo y la depuración como Pry y Guard).
Es importante tener en cuenta que originalmente estaba cargando Pry dentro de nuestro spec_helper.rb
expediente. Esto causó un problema al ejecutar el código en Travis-CI, ahora que estaba excluyendo las gemas de "desarrollo". Así que tuve que modificar el código así:
requiere 'palanca' si ENV ['APP_ENV'] == 'depurar'
Puedes ver que ahora la gema de Pry es solo exigir
'ed si una variable de entorno de APP_ENV
está configurado para depurar. De esta manera podemos evitar que Travis-CI arroje cualquier error. Esto significa que cuando ejecute su código localmente tendría que establecer la variable de entorno si desea depurar su código usando Pry. A continuación se muestra cómo se podría hacer esto en una línea:
APP_ENV = debug && ruby lib / example.rb
Hubo otros dos cambios que hice y eso fue para nuestra Gemfile
. Uno fue para aclarar qué gemas eran necesarias para las pruebas y cuáles para el desarrollo, y el otro fue explícitamente requerido por Travis-CI:
fuente grupo "https://rubygems.org": prueba hacer gema 'rake' gema 'rspec' grupo final: desarrollo hacer gema 'guardia' gema 'guard-rspec' gema 'pry' # Agrega pasos de depuración a Pry # continuar, paso, siguiente gema 'pry-remote' gema 'pry-nav' final
Viendo lo anterior actualizado Gemfile
Podemos ver que hemos movido la gema RSpec a una nueva prueba
grupo, por lo que ahora debería ser más claro qué propósito tiene cada gema. También hemos añadido un nuevo gema 'rastrillo'
. La documentación de Travis-CI indica que esto debía especificarse explícitamente.
La siguiente sección es opcional y le permite a la lista blanca (o lista negra) ciertas ramas en su repositorio. Por lo tanto, de manera predeterminada, Travis-CI ejecutará pruebas en todas sus sucursales, a menos que le indique lo contrario. En este ejemplo, le decimos que solo queremos que se ejecute contra nuestra dominar
rama:
ramas: solo: - maestro
Podríamos decirle que ejecute cada rama 'excepto' una rama en particular, así:
ramas: excepto: - some_branch_I_dont_want_run
La sección final le dice a Travis-CI a dónde enviar notificaciones cuando una compilación falla o tiene éxito:
notificaciones: email: - [email protected]
Puede especificar múltiples direcciones de correo electrónico si lo desea:
notificaciones: correo electrónico: - [email protected] - [email protected] - [email protected]
Puede ser más específico y especificar qué quiere que suceda en caso de fracaso o éxito (por ejemplo, más personas solo estarán interesadas si las pruebas fallan en lugar de recibir un correo electrónico cada vez que pasan):
notificaciones: correo electrónico: destinatarios: - [email protected] on_failure: change on_success: never
El ejemplo anterior muestra que el destinatario nunca recibirá un correo electrónico si las pruebas pasan, pero se le notificará si el estado de falla cambia (el valor predeterminado para ambos es siempre
lo que significa que siempre se le notificará independientemente del resultado del estado).
Nota: cuando especifique explícitamente un en el fracaso
o on_success
necesita mover las direcciones de correo electrónico dentro de recipientes
llave.
Este es el final de nuestra mirada de dos partes en RSpec, TDD y Pry.
En la primera parte, logramos escribir nuestra aplicación utilizando el proceso TDD y el marco de prueba RSpec. En esta segunda mitad también hemos utilizado Pry para mostrar cómo podemos depurar más fácilmente una aplicación Ruby en ejecución. Finalmente, pudimos configurar nuestras pruebas para que funcionen como parte de un servidor de integración continua utilizando el popular servicio Travis-CI..
Esperemos que esto le haya dado suficiente sabor, por lo que le interesa investigar cada una de estas técnicas.