Programmierung

Oct 28, 2020

Halten Sie Ihre Tests DRY - ein wenig über den gemeinsamen Kontext in RSpec

Halten Sie Ihre Tests DRY - ein wenig über den gemeinsamen Kontext in RSpec

Halten Sie Ihre Tests DRY - ein wenig über den gemeinsamen Kontext in RSpec

Konrad Badzioch

Konrad Badzioch

Senior Backend-Entwickler

Violette Blume
Violette Blume
Violette Blume

Als Entwickler bin ich immer bestrebt, meine Tests zu verbessern, um sie sauber und angenehm zu halten. Shared Context war die perfekte Lösung für die Wiederverwendung wiederholbarer Variablen in Tests.

Als Entwickler versuche ich ständig, meinen Code zu verbessern. Auch in Bezug auf die Tests. Für mich ist es wichtig, Tests sauber zu halten, damit das Hinzufügen neuer Tests ein Vergnügen und kein Schuften ist.

Das erste Mal stolperte ich über den geteilten Kontext, als ich nach etwas suchte, das mir helfen kann, einige ständig wiederkehrende Variablen in Tests zu extrahieren. Der geteilte Kontext war genau das, wonach ich gesucht hatte. Ich werde Ihnen zeigen, wie man ihn verwendet und welche Vorteile das hat.

Was testen wir?

Angenommen, wir müssen ein paar Policy-Objekte schreiben. Sie können wie das folgende aussehen:

# lib/policies/user_settings_policy.rb

class UserSettingsPolicy
  def can_show?
    user.admin? || user.id == resource.id
  end

  private

  attr_reader :user, :resource

  def initialize(user, resource)
    @user = user
    @resource = resource
  end
end

Wir haben irgendwo User definiert und UserSettings Klassen, deren Instanzen durch user_id verbunden sind. Diese können ActiveRecord-Objekten und einer Datenbankbeziehung ähneln oder können Objekte sein, die auf Ereignissen basieren. Danach können wir mit dem Schreiben von Tests beginnen. In diesem Beispiel werden first_name, last_name und email zur Erstellung eines Benutzerobjekts benötigt.

Um das Richtlinienobjekt zu testen, benötigen wir eine:n Admin-Benutzer:in und eine:n Benutzer:in, der/die Eigentümer:in einer Ressource ist. Offensichtlich ebenfalls so die Ressource.

# spec/policies/user_settings_policy_spec.rb

RSpec.describe UserSettingsPolicy do
  let(:user) do
    Models::User.new(
      id: SecureRandom.uuid,
      first_name: 'Nick',
      last_name: 'Owen',
      email: 'nick.owen@exam.com'
    )
  end
  let(:admin) do
    Models::User.new(
      id: SecureRandom.uuid,
      first_name: 'Jack',
      last_name: 'Jones',
      email: 'jones@exam.com',
      role: :admin
    )
  end
  let (:resource) { Models::UserSettings.new(user_id: user.id) }

  describe '#can_show?' do
    context 'when a user has an admin role' do
      subject { described_class.new(admin, resource) }

      it 'returns true' do
        expect(subject.can_show?).to be_truthy
      end
    end

    context 'when a user has a user role' do
      subject { described_class.new(user, resource) }

      ...
    end
  end
end

Sie müssen zugeben, dass diese lets etwas Platz eingenommen haben.

Ok, wir können uns um das Testen des nächsten Richtlinienobjekts kümmern. Hmmmm... das bedeutet, dass wir den Benutzer und den Admin in der nachfolgenden Spezifikationsdatei deklarieren müssen. Das ist nicht der richtige Weg.

Es ist Zeit für den gemeinsamen Kontext

Ich habe das shared Verzeichnis innerhalb des spec Katalogs erstellt. Innerhalb dieses Verzeichnisses habe ich die users Datei erstellt. Ich habe sie so benannt, um den Inhalt der Datei genau anzugeben.$

# spec/shared/users.rb

RSpec.shared_context :users do
  let(:user_id) { SecureRandom.uuid }
  let(:user) do
    Models::User.new(
      id: user_id,
      first_name: 'Nick',
      last_name: 'Owen',
      email: 'nick.owen@exam.com'
    )
  end

  let(:admin_id) { SecureRandom.uuid }
  let(:admin) do
    Models::User.new(
      id: admin_id,
      first_name: 'Jack',
      last_name: 'Jones',
      email: 'jones@exam.com',
      role: :admin
    )
  end
end

Der Inhalt dieser Datei enthält lets, die wir zuvor in der Policy-Spec-Datei gesehen haben. Der einzige Unterschied ist, dass ich IDs in separate Variablen extrahiert habe, weil ich nicht jedes Mal [user.id](http://user.id) verwenden möchte. Es gibt noch eine Sache zu tun, wir müssen den geteilten Kontext in der spec_helper.rb Datei laden, etwa so:

Dir[File.expand_path('shared/**/*.rb', __dir__)].each { |f| require f

Die Zeile oben lädt alle .rb Dateien aus dem shared Verzeichnis.

Damit können wir anfangen den gemeinsamen Kontext zu benutzen, was ziemlich einfach ist. Werfen wir einen Blick auf die Spec-Datei. Jetzt ist es klarer, oder?

# spec/policies/user_settings_policy_spec.rb

RSpec.describe UserSettingsPolicy do
  include_context :users

  let (:resource) { Models::UserSettings.new(user_id: user_id) }

  describe '#can_show?' do
    context 'when a user has an admin role' do
      subject { described_class.new(admin, resource) }

      it 'returns true' do
        expect(subject.can_show?).to be_truthy
      end
    end

    context 'when a user has a user role' do
      subject { described_class.new(user, resource) }

      ...
    end
  end
end

Wie Sie feststellen konnten, müssen wir den Kontext mit include_context :users einschliessen. Danach können wir alles verwenden, was wir in unserer Kontextdatei definiert haben. Jetzt können wir ein weiteres Policy-Objekt und Tests dafür schreiben, ohne die Benutzerdeklarationen zu duplizieren.

Zusammenfassung

Der gemeinsame Kontext ist ein guter Ort, um Variablen zu platzieren, die wir in mehr als einer Spec-Datei verwenden. Bei Bedarf können Sie auch Mocks in Ihrem Kontext ablegen. Manchmal ist es schwer, einen gemeinsamen Kontext zu schreiben, der keine unbenutzten Variablen enthält, aber keine Sorge, unbenutzte Variablen sind lets und sie werden initialisiert, wenn man sie in einer Testdatei benutzt. Trotzdem empfehle ich Ihnen, kleine gemeinsame Kontexte zu schreiben, um einfach nur das einzubinden, was Sie brauchen, und die richtige Benennung der Kontextdateien beizubehalten.

✍️

ÜBER DEN AUTOR

Konrad Badzioch

Konrad Badzioch

Senior Backend-Entwickler

Programmieren ist mein Beruf sowie mein Hobby. Ausserdem liebe ich Fantasy-Bücher, Radfahren und viele, viele andere Dinge. Ein produktiver Tag macht mich glücklich und gibt mir Energie für den nächsten Tag.

Sie haben eine Projektidee? Lassen Sie uns darüber reden und sie zum Leben erwecken.

Ihre hochqualifizierten Spezialisten sind da. Kontaktieren Sie uns, um zu sehen, was wir gemeinsam tun können.

Dariusz Michalski

Dariusz Michalski, CEO

dariusz@useo.pl

Konrad Pochodaj

Konrad Pochodaj, CGO

konrad@useo.pl

Sie haben eine Projektidee? Lassen Sie uns darüber reden und sie zum Leben erwecken.

Ihre hochqualifizierten Spezialisten sind da. Kontaktieren Sie uns, um zu sehen, was wir gemeinsam tun können.

Dariusz Michalski

Dariusz Michalski, CEO

dariusz@useo.pl

Konrad Pochodaj

Konrad Pochodaj, CGO

konrad@useo.pl

Sie haben eine Projektidee? Lassen Sie uns darüber reden und sie zum Leben erwecken.

Ihre hochqualifizierten Spezialisten sind da. Kontaktieren Sie uns, um zu sehen, was wir gemeinsam tun können.