Ruby para principiantes Pruebas con Rspec

Ruby es uno de los idiomas más populares utilizados en la web. Estamos ejecutando una sesión aquí en Nettuts + que le presentará a Ruby, así como a los excelentes marcos y herramientas que acompañan el desarrollo de Ruby. En este episodio, aprenderá cómo probar su código Ruby con Rspec, una de las mejores bibliotecas de pruebas del sector..


Prefiero un Screencast?

Parecer familiar?

Si has leído mi reciente tutorial en JasmineJS, probablemente notarás varias similitudes en Rspec. En realidad, las similitudes están en Jasmine: Jasmine se creó con Rspec en mente. Vamos a ver cómo se puede usar Rspec para hacer TDD en Ruby. En este tutorial, crearemos algunas clases de Ruby ideadas para familiarizarnos con la sintaxis de Rspec. Sin embargo, la próxima? Ruby para novatos? ¿El episodio incluirá el uso de Rspec junto con otras bibliotecas para probar las aplicaciones web? así que estad atentos!


Configurando

Es bastante fácil de instalar Rspec. Abre la línea de comando y ejecuta esto:

gema de instalar rspec

Así de fácil.

Ahora, vamos a configurar un pequeño proyecto. Vamos a crear dos clases: Libro y Biblioteca. Nuestro Libro Los objetos simplemente almacenarán un título, autor y categoría. Nuestro Biblioteca El objeto almacenará una lista de libros, los guardará en un archivo y nos permitirá obtenerlos por categoría..

Así es como debería verse el directorio de tu proyecto:

Ponemos las especificaciones (o especificaciones) en un especulación carpeta; Tenemos un archivo de especificaciones para cada clase. Observe la spec_helper.rb expediente. Para que nuestras especificaciones funcionen, necesitamos exigir Las clases de Ruby que estamos probando. Eso es lo que estamos haciendo dentro del spec_helper expediente:

require_relative '? / biblioteca 'require_relative'? / libro 'require' yaml '

(Conoces a require_relative ¿todavía? ¿No? Bien, require_relative es como exigir, excepto que en lugar de buscar tu camino de Ruby, busca relativo al directorio actual.)

Es posible que no esté familiarizado con el módulo YAML; YAML es una base de datos de texto simple que utilizaremos para almacenar datos. Verás cómo funciona, y hablaremos más sobre esto más adelante..

Entonces, ahora que estamos todos configurados, comencemos con algunas especificaciones!


los Libro Clase

Empecemos con las pruebas para el Libro clase.

requiere 'spec_helper' describe Book do end

Así es como empezamos: con una describir bloquear. Nuestro parámetro para describir explica lo que estamos probando: esto podría ser una cadena, pero en nuestro caso estamos usando el nombre de clase.

Entonces, ¿qué vamos a poner dentro de este describir bloquear?

antes: cada do @book = Book.new "Título", "Autor",: final de categoría

Comenzaremos haciendo una llamada a antes de; pasamos el simbolo :cada Para especificar que queremos que este código se ejecute antes de cada prueba (también podríamos hacer :todos para ejecutarlo una vez antes de todas las pruebas). ¿Qué estamos haciendo exactamente antes de cada prueba? Estamos creando una instancia de Libro. Observe cómo lo estamos convirtiendo en una variable de instancia, anteponiendo el nombre de la variable con @. Necesitamos hacer esto para que nuestra variable sea accesible desde nuestras pruebas. De lo contrario, solo obtendremos una variable local que solo es buena dentro de antes de ¿bloquear? que no es bueno en absoluto.

Seguir adelante,

describe "#new" do it "toma tres parámetros y devuelve un objeto Book" do @ book.should be_an_instance_of Book end end

Aquí está nuestra primera prueba. Estamos usando un anidado describir bloque aquí para decir que estamos describiendo las acciones de un método específico. Notarás que he usado la cadena? #New ?; Es una convención en Ruby para hablar, refiérase a métodos de instancia como este: ClassName # methodName Ya que tenemos el nombre de la clase en nuestro nivel superior describir, estamos poniendo el nombre del método aquí.

Nuestra prueba simplemente confirma que efectivamente nos hemos hecho un objeto de Libro..

Note la gramática que usamos aquí: objeto.debe_algo. El noventa y nueve por ciento de tus pruebas tomarán esta forma: tienes un objeto y comienzas llamando debería o no debería en el objeto. Luego, le pasas a ese objeto la llamada a otra función. En este caso eso es be_an_instance_of (el cual toma Libro como su único parámetro). En conjunto, esto hace una prueba perfectamente legible. Esta muy claro que @libro debe ser una instancia de la clase Libro. Entonces, vamos a ejecutarlo.

Abre tu terminal, discos compactos en el directorio del proyecto, y ejecute especificación rspec. los especulación es la carpeta en la que rspec Encontrará las pruebas. Debería ver una salida que dice algo acerca de? Constante no inicializada Objeto :: Libro ?; esto solo significa que no hay Libro clase. Vamos a arreglar eso.

De acuerdo con TDD, solo queremos escribir suficiente código para solucionar este problema. En el libro.rb archivo, eso sería este:

clase final de libro

Vuelva a ejecutar la prueba (especificación rspec), y encontrarás que está pasando bien. No tenemos un inicializar método, así que llamando Ruby # nuevo no tiene efecto en este momento. Pero, podemos crear Libro objetos (aunque huecos). Normalmente, seguiríamos este proceso durante el resto de nuestro desarrollo: escribir una prueba (o algunas pruebas relacionadas), verla fallar, hacerla pasar, refactorizar, repetir. Sin embargo, para este tutorial, solo te mostraré las pruebas y el código, y los discutiremos.

Así que, más pruebas para Libro:

describe "#title" hazlo "devuelve el título correcto" do @ book.title.should eql "Título" fin describe "#author" hazlo "devuelve el autor correcto" do @ book.author.should eql "Autor" end end describe "#category" do it "devuelve la categoría correcta" do @ book.category.should eql: category end end

Debería haber todo muy en serio para ti. Pero note como estamos comparando en la prueba: con eql. Hay tres formas de probar la igualdad con Rspec: usando el operador == o el método eql ambos regresan cierto Si los dos objetos tienen el mismo contenido. Por ejemplo, ambas son cadenas o símbolos que dicen lo mismo. Entonces hay igual, lo que solo devuelve verdadero en los dos objetos es real y verdaderamente igual, lo que significa que son el mismo objeto en la memoria. En nuestro caso, eql (o ==) es lo que queremos.

Estos fallarán, así que aquí está el código para Libro para hacerlos pasar:

class Book attr_accessor: title,: author,: category def initialize title, author, category @title = title @author = author @category = category end end

Movámonos a Biblioteca!


Especificando el Biblioteca clase

Este será un poco más complicado. Vamos a empezar con esto:

requiere 'spec_helper' describe "Objeto de la biblioteca" do antes: all do lib_obj = [Book.new ("JavaScript: The Good Parts", "Douglas Crockford",: development), Book.new ("Designing with Web Standards", " Jeffrey Zeldman ",: diseño), Libro.nuevo (" No me hagas pensar "," Steve Krug ",: usabilidad), Libro.nuevo (" Patrones de JavaScript "," Stoyan Stefanov ",: desarrollo), Libro. nuevo ("Diseño web responsivo", "Ethan Marcotte",: diseño)] Archivo. abrir "books.yml", "w" do | f | f.write YAML :: dump lib_obj end end before: each do @lib = Library.new "books.yml" end end

Todo esto está configurado: estamos usando dos antes de bloques: uno para :cada y uno para :todos. En el antes de todo Bloque, creamos una serie de libros. Entonces abrimos el archivo? Books.yml? (en modo? w? rite) y uso YAML para volcar la matriz en el archivo.

Sendero corto de conejos para explicar YAML un poco mejor: YAML es, según el sitio, un estándar de serialización de datos amigable para todos los lenguajes de programación. Es como una base de datos basada en texto, algo así como JSON. Estamos importando YAML en nuestro spec_helper.rb. los YAML módulo tiene dos métodos principales que utilizará: tugurio, que da salida a los datos serializados como una cadena. Entonces, carga toma la cadena de datos y la cubre de nuevo a objetos Ruby.

Entonces, hemos creado este archivo con algunos datos. antes de :cada prueba, vamos a crear una Biblioteca Objeto, pasándole el nombre del archivo YAML. Ahora veamos las pruebas:

describe "#new" do context "sin parámetros" do it "no tiene libros" do lib = Library.new lib.should have (0) .books end end end "con un parámetro de archivo yaml" do it "tiene cinco libros "do @ lib.should tiene (5) .books end end end end it" devuelve todos los libros en una categoría dada "do @ lib.get_books_in_category (: development) .length.should == 2 end it" acepta libros nuevos "do @ lib.add_book (Book.new ("Diseñar para la Web", "Mark Boulton",: diseño)) @ lib.get_book ("Diseñar para la Web"). be_an_instance_of Book end it "guarda la biblioteca" do books = @ lib.books.map | book | book.title @ lib.save lib2 = Library.new "books.yml" books2 = lib2.books.map | book | book.title books.should eql books2 end

Comenzamos con un interior describir bloque especialmente para el Biblioteca # nuevo método. Estamos introduciendo otro bloque aquí: contexto Esto nos permite especificar un contexto para las pruebas dentro de él, o especificar diferentes resultados para diferentes situaciones. En nuestro ejemplo, tenemos dos contextos diferentes: ¿sin parámetros? y? con un parámetro de archivo yaml ?; estos muestran los dos comportamientos para el uso Biblioteca # nuevo.

Además, note los comparadores de prueba que estamos usando en estas dos pruebas: lib.should tiene (0) .books y @ lib.should tiene (5) .books. La otra forma de escribir esto sería lib.books.length.should == 5, pero esto no es tan legible. Sin embargo, muestra que necesitamos tener un libros propiedad que es una serie de los libros que tenemos.

Luego, tenemos otras tres pruebas para probar la funcionalidad de obtener libros por categoría, agregar un libro a la biblioteca y guardar la biblioteca. Todos estos están fallando, así que escribamos la clase ahora.

clase Biblioteca attr_accessor: books def initialize lib_file = false @lib_file = lib_file @books = @lib_file? YAML :: load (File.read (@lib_file)): [] end def get_books_in_category category @ books.select do | book | book.category == categoría end end def final add_book book @ books.push book end def get_book title @ books.select do | book | book.title == title end.first end def save lib_file = false @lib_file = lib_file || @lib_file || "library.yml" File.open @lib_file, "w" do | f | f.write YAML :: dump @ books end end end end

Podríamos escribir más pruebas y agregar muchas otras funciones a esto Biblioteca clase, pero nos detendremos allí. Ahora corriendo especificación rspec, Verás que todas las pruebas pasan..

Sin embargo, esto no nos da mucha información sobre las pruebas. Si desea ver más, use el parámetro de formato anidado: especificación rspec - formato anidado. Verás esto:


Unos últimos últimos

Antes de terminar, déjame mostrarte un par de otros competidores

  • obj. debería ser_true, obj.debe ser_fundido, obj.debe ser_nil, obj.should_empty - Los tres primeros de estos podrían hacerse por == verdadero, etc. be_empty será verdad si obj.empty? es verdad.
  • obj.debe existir - ¿Todavía existe este objeto??
  • obj.should have_at_most (n) .items, object.should have_at_least (n) .items - me gusta tener, pero pasará si hay más o menos que norte artículos, respectivamente.
  • obj. debería incluir (a [, b ,?]) - son uno o más elementos en una matriz?
  • obj.should match (string_or_regex) - el objeto coincide con la cadena o expresiones regulares?
  • obj.should raise_exception (error) - ¿Este método genera un error cuando se llama?
  • obj.should respond_to (method_name) - ¿Este objeto tiene este método? Puede tomar más de un nombre de método, ya sea en cadenas o símbolos.

Querer aprender más?

Rspec es uno de los mejores marcos para pruebas en Ruby, y hay mucho que puedes hacer con él. Para obtener más información, visite el sitio web de Rspec. También está el libro The Rspec, que enseña más que solo Rspec: se trata de TDD y BDD en Ruby. Lo estoy leyendo ahora, y es extremadamente minucioso y profundo..

Bueno, eso es todo para esta lección! La próxima vez, veremos cómo podemos usar Rspec para probar las interfaces en una aplicación web.