Cómo probar una preocupación en Rails

104

Dado que tengo una Personableinquietud en mi aplicación Rails 4 que tiene un full_namemétodo, ¿cómo haría para probar esto usando RSpec?

preocupaciones / personable.rb

module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end
Kyle Decot
fuente
¿Qué marco de prueba estás usando? También recuerde que Personable es solo un módulo Ruby normal. Pruébelo como lo haría con cualquier otro mixin.
Lee Jarvis
¿No se ActiveSupport::Concernha sacado de Rails? Pensé que se había ido hace un rato.
Russell
@LeeJarvis Estoy usando Rspec junto con FactoryGirl
Kyle Decot
4
@Russell Estoy de acuerdo. Dicho esto, no ayudaría a nadie con sus preguntas solo porque estuvieran siguiendo una forma de Rails de hacer algo con lo que no estoy de acuerdo. De todos modos, esto es una especie de escapar del tema de esta pregunta :-)
Lee Jarvis

Respuestas:

176

El método que encontró ciertamente funcionará para probar un poco de funcionalidad, pero parece bastante frágil: su clase ficticia (en realidad, solo una Struct en su solución) puede o no comportarse como una clase real que includees de su interés. Además, si está tratando de probar las preocupaciones del modelo, no podrá hacer cosas como probar la validez de los objetos o invocar devoluciones de llamada de ActiveRecord a menos que configure la base de datos en consecuencia (porque su clase ficticia no tendrá un respaldo de tabla de base de datos eso). Además, querrá no solo probar la preocupación, sino también probar el comportamiento de la preocupación dentro de las especificaciones de su modelo.

Entonces, ¿por qué no matar dos pájaros de un tiro? Al usar los grupos de ejemplo compartidos de RSpec , puede probar sus inquietudes con las clases reales que los usan (por ejemplo, modelos) y podrá probarlos en todos los lugares donde se usan. Y solo tiene que escribir las pruebas una vez y luego incluirlas en cualquier especificación del modelo que utilice su preocupación. En su caso, esto podría verse así:

# app/models/concerns/personable.rb
module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end

# spec/concerns/personable_spec.rb
require 'spec_helper'

shared_examples_for "personable" do
  let(:model) { described_class } # the class that includes the concern

  it "has a full name" do
    person = FactoryBot.build(model.to_s.underscore.to_sym, first_name: "Stewart", last_name: "Home")
    expect(person.full_name).to eq("Stewart Home")
  end
end

# spec/models/master_spec.rb
require 'spec_helper'
require Rails.root.join "spec/concerns/personable_spec.rb"

describe Master do
  it_behaves_like "personable"
end

# spec/models/apprentice_spec.rb
require 'spec_helper'

describe Apprentice do
  it_behaves_like "personable"
end

Las ventajas de este enfoque se vuelven aún más obvias cuando comienza a hacer cosas que le preocupan, como invocar devoluciones de llamada de AR, donde nada menos que un objeto AR simplemente no funcionará.

Josh Leitzel
fuente
2
Una desventaja de esto es que se ralentizará parallel_tests. Creo que será mejor tener pruebas separadas en lugar de usar shared_examples_fory it_behaves_like.
Artem Kalinchuk
6
@ArtemKalinchuk No estoy seguro de que eso sea cierto, según github.com/grosser/parallel_tests/issues/168 parallel_tests se basan en cada archivo, por lo que los ejemplos compartidos no deberían ralentizarlo. También diría que los comportamientos compartidos agrupados de forma adecuada superan la velocidad de prueba.
Aaron K
8
Asegúrese de incluir el concernsdirectorio en su spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871
Ziggy
1
No pude encontrar nada sobre la inclusión del directorio de preocupaciones en ese enlace. ¿Podría aclarar cómo se hace esto? No puedo hacer que mi prueba RSpec reconozca el módulo en una de mis preocupaciones.
Jake Smith
4
No agregue _specal nombre de archivo que contiene shared_examples_for (personable_spec.rb en este caso), de lo contrario, recibirá un mensaje de advertencia engañoso: github.com/rspec/rspec-core/issues/828 .
Lalu
62

En respuesta a los comentarios que recibí, esto es lo que terminé haciendo (si alguien tiene mejoras, no dude en publicarlas) :

spec / preocupaciones / personable_spec.rb

require 'spec_helper'

describe Personable do
  let(:test_class) { Struct.new(:first_name, :last_name) { include Personable } }
  let(:personable) { test_class.new("Stewart", "Home") }

  it "has a full_name" do
    expect(personable.full_name).to eq("#{personable.first_name} #{personable.last_name}")
  end
end
Kyle Decot
fuente
1
Sí, esto romperá otras pruebas si pasan a probar una clase real llamada Person. Editaré para arreglar.
Russell
Esto no funciona. Me da el error:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
Kyle Decot
Intente incluir Personable en lugar de ampliarlo. Actualizaré la respuesta.
Russell
Funciona muy bien ahora. Gracias por señalarme en la dirección correcta y ayudarme a refactorizar @Russell
Kyle Decot
Funciona muy bien y se ve bien
Edward
7

Otro pensamiento es usar la gema with_model para probar cosas como esta. Estaba buscando probar una preocupación por mí mismo y había visto la gema pg_search haciendo esto . Parece mucho mejor que probar en modelos individuales, ya que pueden cambiar, y es bueno definir las cosas que necesitará en su especificación.

lobati
fuente