Come testare una preoccupazione in Rails


104

Dato che ho un Personableproblema nella mia applicazione Rails 4 che ha un full_namemetodo, come dovrei testarlo usando RSpec?

preoccupazioni / personable.rb

module Personable
  extend ActiveSupport::Concern

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

Quale framework di test stai usando? Ricorda anche che Personable è solo un normale modulo Ruby. Provalo proprio come faresti con qualsiasi altro mixin.
Lee Jarvis

Non è ActiveSupport::Concernstato tolto da Rails? Pensavo fosse successo poco fa.
Russell

@LeeJarvis sto usando Rspec con FactoryGirl
Kyle Decot


4
@Russell sono d'accordo. Detto questo, non aiuterei qualcuno con le loro domande solo perché stavano seguendo un modo Rails-y di fare qualcosa con cui non ero d'accordo. Comunque questo è un po 'sfuggire all'argomento di questa domanda :-)
Lee Jarvis

Risposte:


176

Il metodo che hai trovato funzionerà sicuramente per testare un po 'di funzionalità ma sembra piuttosto fragile: la tua classe fittizia (in realtà solo una Structnella tua soluzione) può o meno comportarsi come una vera classe che includeti interessa. Inoltre, se stai cercando di testare i problemi del modello, non sarai in grado di fare cose come testare la validità degli oggetti o richiamare i callback di ActiveRecord a meno che tu non configuri il database di conseguenza (perché la tua classe fittizia non avrà un supporto per la tabella del database esso). Inoltre, vorrai non solo testare la preoccupazione, ma anche testare il comportamento della preoccupazione all'interno delle specifiche del tuo modello.

Allora perché non prendere due piccioni con una fava? Utilizzando i gruppi di esempio condivisi di RSpec , puoi testare le tue preoccupazioni rispetto alle classi effettive che li utilizzano (ad esempio, i modelli) e sarai in grado di testarli ovunque vengano utilizzati. E devi solo scrivere i test una volta e poi includerli in qualsiasi specifica del modello che utilizza la tua preoccupazione. Nel tuo caso, potrebbe essere simile a questo:

# 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

I vantaggi di questo approccio diventano ancora più evidenti quando inizi a fare cose nella tua preoccupazione come invocare callback AR, dove qualsiasi cosa meno di un oggetto AR semplicemente non andrà bene.


2
Uno svantaggio di questo è che rallenterà parallel_tests. Penso che sarà meglio avere test separati invece di usare shared_examples_fore it_behaves_like.
Artem Kalinchuk

6
@ArtemKalinchuk Non sono sicuro che sia vero, per github.com/grosser/parallel_tests/issues/168 parallel_tests sono basati per file, quindi gli esempi condivisi non dovrebbero rallentarlo. Direi anche che comportamenti condivisi correttamente raggruppati, trionfi sulla velocità di test.
Aaron K

8
Assicurati di includere la concernsdirectory nel tuo spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871
Ziggy

1
Non sono riuscito a trovare nulla sull'inclusione della directory delle preoccupazioni in quel collegamento. Potresti chiarire come si fa? Non riesco a far sì che il mio test RSpec riconosca il modulo in una delle mie preoccupazioni.
Jake Smith

4
Non aggiungere _specal nome del file che contiene shared_examples_for (personable_spec.rb in questo caso), altrimenti riceverai un messaggio di avviso fuorviante: github.com/rspec/rspec-core/issues/828 .
Lalu

62

In risposta ai commenti che ho ricevuto, ecco cosa ho finito per fare (se qualcuno ha miglioramenti non esitate a pubblicarli) :

spec / preoccupazioni / 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

1
Sì, questo interromperà altri test se capita di testare una vera classe chiamata Person. Modificherò per correggere.
Russell

Questo non funziona. Mi dà l'errore:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
Kyle Decot

Prova a includere Personable piuttosto che estenderlo. Aggiornerò la risposta.
Russell

Funziona alla grande ora. Grazie per avermi indicato la giusta direzione e avermi aiutato a refactoring @Russell
Kyle Decot

Funziona alla grande e sembra carino
Edward

7

Un altro pensiero è usare la gemma with_model per testare cose come questa. Stavo cercando di testare una preoccupazione me stesso e avevo visto la gemma pg_search farlo . Sembra molto meglio che testare su singoli modelli, poiché questi potrebbero cambiare, ed è bello definire le cose di cui avrai bisogno nelle tue specifiche.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.