So testen Sie ein Problem in Rails

104

In Anbetracht , dass ich eine habe PersonableSorge in meinem Rails 4 - Anwendung , die eine hat full_nameMethode, wie würde ich mich über diese mit RSpec testen?

Anliegen / personable.rb

module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end
Kyle Decot
quelle
Welches Test-Framework verwenden Sie? Denken Sie auch daran, dass Personable nur ein normales Ruby-Modul ist. Testen Sie es so, wie Sie jedes andere Mixin testen würden.
Lee Jarvis
Wurde nicht ActiveSupport::Concernaus Rails genommen? Ich dachte, es ist eine Weile her.
Russell
@ LeeJarvis Ich benutze Rspec zusammen mit FactoryGirl
Kyle Decot
4
@ Russell Ich stimme zu. Das heißt, ich würde niemandem bei seinen Fragen nicht helfen, nur weil er einer Rails-Methode folgte, um etwas zu tun, mit dem ich nicht einverstanden war. Jedenfalls entgeht dies dem Thema dieser Frage :-)
Lee Jarvis

Antworten:

176

Die Methode, die Sie gefunden haben, funktioniert sicherlich, um ein wenig Funktionalität zu testen, scheint aber ziemlich fragil zu sein - Ihre Dummy-Klasse (eigentlich nur eine Structin Ihrer Lösung) kann sich wie eine echte Klasse verhalten oder nicht, die includeIhr Anliegen ist. Wenn Sie versuchen, Modellprobleme zu testen, können Sie die Gültigkeit von Objekten nicht testen oder ActiveRecord-Rückrufe aufrufen, es sei denn, Sie richten die Datenbank entsprechend ein (da Ihre Dummy-Klasse keine Datenbanktabellenunterstützung hat es). Darüber hinaus möchten Sie nicht nur das Problem testen, sondern auch das Verhalten des Unternehmens in Ihren Modellspezifikationen testen.

Warum also nicht zwei Fliegen mit einer Klappe schlagen? Mithilfe der gemeinsam genutzten Beispielgruppen von RSpec können Sie Ihre Bedenken anhand der tatsächlichen Klassen testen, die sie verwenden (z. B. Modelle), und Sie können sie überall dort testen, wo sie verwendet werden. Und Sie müssen die Tests nur einmal schreiben und sie dann einfach in jede Modellspezifikation aufnehmen, die Ihr Anliegen verwendet. In Ihrem Fall könnte dies ungefähr so ​​aussehen:

# 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

Die Vorteile dieses Ansatzes werden noch deutlicher, wenn Sie anfangen, Dinge in Ihrem Anliegen zu tun, wie das Aufrufen von AR-Rückrufen, bei denen weniger als ein AR-Objekt einfach nicht funktioniert.

Josh Leitzel
quelle
2
Ein Nachteil davon ist, dass es langsamer wird parallel_tests. Ich denke, es ist besser, separate Tests zu haben, als shared_examples_forund zu verwenden it_behaves_like.
Artem Kalinchuk
5
@ArtemKalinchuk Ich bin mir nicht sicher, ob das stimmt. Per github.com/grosser/parallel_tests/issues/168 parallel_tests basieren sie auf Dateien. Gemeinsame Beispiele sollten dies also nicht verlangsamen. Ich würde auch argumentieren, dass richtig gruppierte gemeinsame Verhaltensweisen die Testgeschwindigkeit übertrumpfen.
Aaron K
8
Stellen Sie sicher, dass Sie das concernsVerzeichnis in Ihrem spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871
Ziggy
1
Ich konnte nichts darüber finden, das Anliegenverzeichnis in diesen Link aufzunehmen. Könnten Sie bitte klarstellen, wie dies gemacht wird? Ich kann meinen RSpec-Test nicht dazu bringen, das Modul in einem meiner Probleme zu erkennen.
Jake Smith
4
_specFügen Sie nicht den Dateinamen hinzu , der shared_examples_for enthält (in diesem Fall personable_spec.rb), da sonst eine irreführende Warnmeldung angezeigt wird - github.com/rspec/rspec-core/issues/828 .
Lalu
62

Als Antwort auf die Kommentare, die ich erhalten habe, habe ich Folgendes getan (wenn jemand Verbesserungen hat, kann er diese gerne posten) :

spec / Anliegen / 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
quelle
1
Ja, dies wird andere Tests unterbrechen, wenn sie eine echte Klasse namens testen Person. Ich werde bearbeiten, um zu beheben.
Russell
Das funktioniert nicht. Es gibt mir den Fehler:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
Kyle Decot
Versuchen Sie, Personable einzuschließen, anstatt es zu erweitern. Ich werde die Antwort aktualisieren.
Russell
Funktioniert jetzt super. Vielen Dank, dass Sie mich in die richtige Richtung weisen und mir helfen, @Russell
Kyle Decot
Funktioniert
Edward
7

Ein anderer Gedanke ist, das Juwel with_model zu verwenden, um solche Dinge zu testen. Ich wollte selbst ein Problem testen und hatte gesehen, wie das Juwel pg_search dies tat . Es scheint viel besser zu sein als das Testen einzelner Modelle, da sich diese möglicherweise ändern, und es ist schön, die Dinge zu definieren, die Sie in Ihrer Spezifikation benötigen.

Lobati
quelle