[RUBY] Stellen Sie RSpec auf DRY

Es ist ein Memorandum.

Verweise Everyday Rails-Einführung in das Testen von Schienen mit RSpec https://leanpub.com/everydayrailsrspec-jp

Support-Modul

Dies ist eine Methode zur Beseitigung von Codeduplizierungen und zur Standardisierung der Verarbeitung (Anmeldeverarbeitung usw.). Wenn beispielsweise fünf Tests den Anmeldevorgang im Voraus ausführen und die Anmeldeschaltfläche geändert wird, sind fünf Änderungen erforderlich. Die Aufteilung in ein einzelnes Modul verbessert die Lesbarkeit und reduziert Änderungen auf ein Minimum.

Versuchen Sie, den folgenden Anmeldevorgang in Module aufzuteilen

#Anmeldebildschirm abrufen
visit root_path
#Klicken Sie auf Anmelden
click_link "Sign in"
#Geben Sie die E-Mail-Adresse des Benutzers in das Textfeld E-Mail ein
fill_in "Email", with: user.email 
#Geben Sie das Passwort wie oben ein
fill_in "Password", with: user.password
#Klicken Sie auf die Schaltfläche Anmelden
click_button "Log in"

Angenommen, der Code in den in diesem Modul beschriebenen Methoden ist über den gesamten Test verteilt.

Um diesen Code zu trennen, erstellen Sie spec / suprot / login_support.rb und modularisieren Sie ihn.

login_support.rb


module LoginSupport
 def sign_in_as(user)
    visit root_path
    click_link "Sign in"
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    click_button "Log in"
 end
#Lesen Sie die gesamte RSpec
RSpec.configure do |config|
  config.include LoginSupport
end
↓
#Wenn Sie es für jeden Test explizit lesen möchten, in der Spezifikationsdatei, die Sie lesen möchten
include LoginSupport
#Wenn ja, wird es gelesen.

Jetzt können Sie die Methoden (Hilfsmethoden) im Modul aufrufen. Vergleichen wir vor und nach dem Umschreiben. Everyday Rails - Leihen Sie sich den Code aus, um mit dem Testen von Rails mit RSpec zu beginnen.

Vor dem Austausch

projects_spec.rb


require 'rails_helper'

RSpec.feature "Projects", type: :feature do
  scenario "user creates a new project" do
    user = FactoryBot.create(:user)

  #von hier
    visit root_path
    click_link "Sign in"
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    click_button "Log in"
  #Ersetzen Sie bis hierher durch die Hilfsmethode

    expect {
      click_link "New Project"
      fill_in "Name", with: "Test Project"
      fill_in "Description", with: "Trying out Capybara"
      click_button "Create Project"

      expect(page).to have_content "Project was successfully created"
      expect(page).to have_content "Test Project"
      expect(page).to have_content "Owner: #{user.name}"
    }.to change(user.projects, :count).by(1)
  end
end

Nach dem Austausch

projects_spec.rb


require 'rails_helper'

RSpec.feature "Projects", type: :feature do
  scenario "user creates a new project" do
    user = FactoryBot.create(:user)

  #von hier

    sign_in_as(user)
  #Oder
  sign_in_as user
   #Aber ok

  #Ich habe das Obige durch eine Hilfsmethode ersetzt

    expect {
      click_link "New Project"
      fill_in "Name", with: "Test Project"
      fill_in "Description", with: "Trying out Capybara"
      click_button "Create Project"

      expect(page).to have_content "Project was successfully created"
      expect(page).to have_content "Test Project"
      expect(page).to have_content "Owner: #{user.name}"
    }.to change(user.projects, :count).by(1)
  end
end

Das Wichtigste dabei ist, den Methodennamen leicht verständlich zu machen. Es ist wichtig, einen Namen im Auge zu behalten, den Sie leicht verstehen können, da Sie den Vorgang nicht unter dem Namen der Methode verstehen können. Wenn Sie auf die Originaldatei verweisen, wird der Test nur unpraktisch.

Verzögertes Laden mit let

In einigen Fällen werden die für den Test erforderlichen Daten vor dem Test eingerichtet (Datenerstellungsprozess, z. B. Benutzererstellung), bevor Beschreibung oder Kontext ausgeführt werden. Da before jedes Mal ausgeführt wird, wenn eine Beschreibung oder ein Kontext ausgeführt wird, können die folgenden Probleme auftreten.

Es ist das verzögerte Lesen durch let, das dies klärt. Da let nur ausgeführt wird, wenn es aufgerufen wird, können die oben genannten Probleme gelöst werden. Vergleichen wir vor und nach dem Umschreiben. Everyday Rails - Leihen Sie sich den Code aus, um mit dem Testen von Rails mit RSpec zu beginnen.

unbenutzt lassen

note_spec.rb


require 'rails_helper'

RSpec.describe Note, type: :model do
  before do
    #Benutzer erstellt
    @user = User.create(
      first_name: "Joe",
      last_name:  "Tester",
      email:      "[email protected]",
      password:   "dottle-nouveau-pavilion-tights-furze",
    )
    #Projekterstellung
    @project = @user.projects.create(
      name: "Test Project",
    )
  end

  it "Muss gültig sein, wenn Benutzer, Projekte, Nachrichten vorhanden sind" do
    note = Note.new(
      message: "This is a sample note.",
      user: @user,
      project: @project,
    )
    expect(note).to be_valid
  end

  it "Wenn keine Nachricht vorhanden ist, befindet sie sich in einem ungültigen Zustand" do
    note = Note.new(message: nil)
    note.valid?
    expect(note.errors[:message]).to include("can't be blank")
  end

  describe "Suchen Sie nach Nachrichten, die der Zeichenfolge entsprechen" do
    before do
      @note1 = @project.notes.create(
        message: "This is the first note.",
        user: @user,
      )
      @note2 = @project.notes.create(
        message: "This is the second note.",
        user: @user,
      )
      @note3 = @project.notes.create(
        message: "First, preheat the oven.",
        user: @user,
      )
    end

    context "Bei der Suche nach passenden Daten" do
      it "Rückgabe von Notizen, die mit der Suchzeichenfolge übereinstimmen" do
        expect(Note.search("first")).to include(@note1, @note3)
      end
    end

    context "Wenn keine übereinstimmenden Daten gefunden werden" do
      it "Rückgabe einer leeren Sammlung" do
        expect(Note.search("message")).to be_empty
      end
    end
  end
end

benutze let

note_spec.rb


require 'rails_helper'

RSpec.describe Note, type: :model do
  #benutze let statt vorher
  let(:user) { FactoryBot.create(:user) }
  let(:project) { FactoryBot.create(:project, owner: user) }

  it "Muss gültig sein, wenn Benutzer, Projekte, Nachrichten vorhanden sind" do
    note = Note.new(
      message: "This is a sample note.",
      user: user,
      project: project,
    )
    expect(note).to be_valid
  end

  it "Wenn keine Nachricht vorhanden ist, befindet sie sich in einem ungültigen Zustand" do
    note = Note.new(message: nil)
    note.valid?
    expect(note.errors[:message]).to include("can't be blank")
  end

  describe "Suchen Sie nach Nachrichten, die der Zeichenfolge entsprechen" do
    #benutze let statt vorher
    let(:note1) {
      FactoryBot.create(:note,
        project: project,
        user: user,
        message: "This is the first note."
      )
    }

    let(:note2) {
      FactoryBot.create(:note,
        project: project,
        user: user,
        message: "This is the second note."
      )
    }

    let(:note3) {
      FactoryBot.create(:note,
        project: project,
        user: user,
        message: "First, preheat the oven."
      )
    }

    context "Bei der Suche nach passenden Daten" do
      it "Rückgabe von Notizen, die mit der Suchzeichenfolge übereinstimmen" do
        expect(Note.search("first")).to include(note1, note3)
      end
    end

    context "Wenn keine übereinstimmenden Daten gefunden werden" do
      it "Rückgabe einer leeren Sammlung" do
        expect(Note.search("message")).to be_empty
      end
    end
  end
end

Bei der vorherigen Verwendung wurden Instanzvariablen verwendet, um den Bereich im Test zu überschreiten. In let wird dies jedoch durch Variablen beschrieben. Nachdem der Test bestanden wurde, tritt hier ein Problem auf. Fügen Sie Expect (Note.count) .zu Gleichung 3 zum letzten "Zurückgeben einer leeren Sammlung" hinzu.

note_spec.rb


    context "Wenn keine übereinstimmenden Daten gefunden werden" do
      it "Rückgabe einer leeren Sammlung" do
        expect(Note.search("message")).to be_empty
     #Fügen Sie diesen Satz hinzu
     expect(Note.count).to eq 3
      end
    end
  end
end

Die obige Beschreibung besteht den Test nicht. Der Grund ist einfach: Lassen Sie uns faul lesen.

Kurz gesagt, es werden keine Daten erstellt, wenn sie nicht aufgerufen werden.

Mit Expect (Note.count) .zu Gleichung 3 geht der Test zur DB, um die Daten zu finden. Natürlich ist die DB leer, weil let nicht aufgerufen wird. Da die Definition der Erwartung jedoch lautet, dass die Anzahl der Daten in der Notiz gleich 3 ist, die Anzahl der Daten 0 ist, der Test nicht bestanden wird und ein Fehler auftritt.

Eine Möglichkeit, dies zu lösen, besteht darin, einfach let aufzurufen. Ein Beispiel mit let! Wird unten gezeigt.

note_spec.rb


   #lassen statt lassen!verwenden
    let!(:note1) {
      FactoryBot.create(:note,
        project: project,
        user: user,
        message: "This is the first note."
      )
    }

    let!(:note2) {
      FactoryBot.create(:note,
        project: project,
        user: user,
        message: "This is the second note."
      )
    }

    let!(:note3) {
      FactoryBot.create(:note,
        project: project,
        user: user,
        message: "First, preheat the oven."
      )
    }

   #====Kürzung=====

    context "Wenn keine übereinstimmenden Daten gefunden werden" do
      it "Rückgabe einer leeren Sammlung" do
        expect(Note.search("message")).to be_empty
     #Fügen Sie diesen Satz hinzu
     expect(Note.count).to eq 3
      end
    end
  end
end

Dies wird den Test bestehen. Lassen Sie! Preloads, während let delay liest. Das Bild ist das gleiche wie zuvor, und für jeden Bewertungsblock werden Daten erstellt.

Dies bedeutet, dass die oben genannten Probleme nicht behoben wurden. In diesem Fall hängt es von der Situation ab, ob Sie es vorher verwenden oder lassen! Aus der Sicht meines Lernens habe ich das Gefühl, dass es in Ordnung ist, vorher zu verwenden und richtig zu lassen.

Als Ergebnis im Grunde lassen! Ich möchte in die Richtung gehen, nicht zu benutzen. Ich würde es gerne vorher belassen, wenn ein Vorladen erforderlich ist, anstatt Fehlinterpretationen oder Pocha-Fehler zu machen, da es nur einen subtilen Unterschied zwischen let und let gibt!

shared_context

Mit shared_context können Sie das erforderliche Setup mit mehreren Testdateien durchführen. Die Verwendung ähnelt dem Support-Modul. Vergleichen wir vor und nach dem Umschreiben. Everyday Rails - Leihen Sie sich den Code aus, um mit dem Testen von Rails mit RSpec zu beginnen.

tasks_controller.rb


require 'rails_helper'

RSpec.describe TasksController, type: :controller do
  #Fassen Sie das Setup hier zusammen
  let(:user) { FactoryBot.create(:user) }
  let(:project) { FactoryBot.create(:project, owner: user) }
  let(:task) { project.tasks.create!(name: "Task task") }

  describe "#show" do
    it "responds with JSON formatted output" do
      sign_in user
      get :show, format: :json,
        params: { project_id: project.id, id: task.id }
      expect(response.content_type).to eq "application/json"
    end
  end

  describe "#create" do
    it "responds with JSON formatted output" do
      new_task = { name: "New test task" }
      sign_in user
      post :create, format: :json,
        params: { project_id: project.id, task: new_task }
      expect(response.content_type).to eq "application/json"
    end

    it "adds a new task to the project" do
      new_task = { name: "New test task" }
      sign_in user
      expect {
        post :create, format: :json,
          params: { project_id: project.id, task: new_task }
      }.to change(project.tasks, :count).by(1)
    end

    it "requires authentication" do
      new_task = { name: "New test task" }
      # Don't sign in this time ...
      expect {
        post :create, format: :json,
          params: { project_id: project.id, task: new_task }
      }.to_not change(project.tasks, :count)
      expect(response).to_not be_success
    end
  end
end

Erstellen Sie eine separate Datei und kombinieren Sie die 3 Zeilen von let mit shared_context Erstellen Sie spec / support / context / project_setup.rb

project_setup.rb


RSpec.shared_context "project setup" do
  let(:user) { FactoryBot.create(:user) }
  let(:project) { FactoryBot.create(:project, owner: user) }
  let(:task) { project.tasks.create!(name: "Task task") }
end

Gehen Sie zurück zu task_controller.rb und schreiben Sie den let-Teil neu.

tasks_controller.rb


require 'rails_helper'

RSpec.describe TasksController, type: :controller do
  #Löschen lassen und projizieren_setup.Include rb.
  include_context "project setup"

#====Unten weggelassen=====

Jetzt haben Sie mehrere Testdateien, um die erforderlichen Setups in einer zu kombinieren.

Zusammenfassung

・ Support-Modul Sie können gängige Verarbeitungen wie Hilfsmethoden in einer separaten Datei ausschneiden und bei Bedarf einfügen. · Lassen Es ist möglich, Daten zu erstellen, die zum Testen verwendet werden sollen. Auswirkungen auf die Prüfung durch verzögertes Laden Es ist zu erwarten, dass sich die Leistung verbessert. ・ Shared_context Es ist möglich, das zum Testen verwendete Setup zusammenzustellen. Schneiden Sie es in eine andere Datei aus und verwenden Sie es als Include.

Vielen Dank, dass Sie bis zum Ende bei uns bleiben. Wenn Sie Anregungen oder Ratschläge haben, würde ich mich freuen, wenn Sie einen Kommentar abgeben könnten.

Recommended Posts

Stellen Sie RSpec auf DRY
[RSpec] Testen von Fehlermeldungen, die unabhängig von Shoulda-Matchers festgelegt wurden
So legen Sie Nginx von Docker fest
So legen Sie Java-Konstanten fest
Dinge, die nach der Installation von RubyMine festgelegt werden müssen
So stellen Sie Spring Boot + PostgreSQL ein
So setzen Sie Lombok in Eclipse
to_ ○
Setzen Sie das Spring Security-Authentifizierungsergebnis auf JSON
Einfache Möglichkeit, iOS-App-Symbole festzulegen
[Hinweis] Erste Schritte mit Rspec
So richten Sie kapt ein und verwenden es
[Java] So stellen Sie die Datums- und Uhrzeit auf 00:00:00 ein
So setzen Sie JAVA_HOME mit dem Appassembler-Maven-Plugin von Maven
Benötigt für iOS 14? So legen Sie NSUserTrackingUsageDescription fest