Oto kolejny przykład (przy użyciu Factorygirl na "tworzenie" metoda”i shared_examples_for)
dotyczą specyfikacji
#spec/support/concerns/commentable_spec
require 'spec_helper'
shared_examples_for 'commentable' do
let (:model) { create (described_class.to_s.underscore) }
let (:user) { create (:user) }
it 'has comments' do
expect { model.comments }.to_not raise_error
end
it 'comment method returns Comment object as association' do
model.comment(user, "description")
expect(model.comments.length).to eq(1)
end
it 'user can make multiple comments' do
model.comment(user, "description")
model.comment(user, "description")
expect(model.comments.length).to eq(2)
end
end
commentable koncern
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments, as: :commentable
end
def comment(user, description)
Comment.create(commentable_id: self.id,
commentable_type: self.class.name,
user_id: user.id,
description: description
)
end
end
i restraunt_spec mogą wyglądać mniej więcej tak (jestem n ot rspec guru, więc nie sądzę, że mój sposób pisania specyfikacji jest dobre - najważniejszą rzeczą jest na początku):
require 'rails_helper'
RSpec.describe Restraunt, type: :model do
it_behaves_like 'commentable'
describe 'with valid data' do
let (:restraunt) { create(:restraunt) }
it 'has valid factory' do
expect(restraunt).to be_valid
end
it 'has many comments' do
expect { restraunt.comments }.to_not raise_error
end
end
describe 'with invalid data' do
it 'is invalid without a name' do
restraunt = build(:restraunt, name: nil)
restraunt.save
expect(restraunt.errors[:name].length).to eq(1)
end
it 'is invalid without description' do
restraunt = build(:restraunt, description: nil)
restraunt.save
expect(restraunt.errors[:description].length).to eq(1)
end
it 'is invalid without location' do
restraunt = build(:restraunt, location: nil)
restraunt.save
expect(restraunt.errors[:location].length).to eq(1)
end
it 'does not allow duplicated name' do
restraunt = create(:restraunt, name: 'test_name')
restraunt2 = build(:restraunt, name: 'test_name')
restraunt2.save
expect(restraunt2.errors[:name].length).to eq(1)
end
end
end
To zdecydowanie najlepsza odpowiedź. Można być jednoznacznym w przypadku manekina ORAZ przetestować to samo API w specyfikacjach klasy macierzystej. To robi różnicę, gdy mamy do czynienia z dowolnymi "elastycznymi" API (czytaj: method_missing). Są tylko niektóre przypadki, o których nie można myśleć, dopóki nie zostaną użyte w "prawdziwej" (nie-niemuskiej) klasie, a udostępnione przykłady wykonają dobrą robotę wykonywania kodu w każdym niezbędnym kontekście. – winfred
To się rozpada, gdy moduł dodaje atrybuty dynamiczne. Załóżmy, że Twój moduł pozwala na metodę klasy: 'allow_upload: csv', która dodaje metody takie jak' csv_file_path' i 'csv_file_size'. Ale masz inny model, który wywołuje przesłany plik ': attachment'. Teraz twoja specyfikacja "działa jak upload" nie będzie działać, ponieważ dodaje się 'csv_file_path', a jedna ma' attachment_file_path'.Z tego powodu mam wrażenie, że w wielu przypadkach najlepiej będzie pasował do twoich potrzeb najlepiej, aby użyć klasy dummy, aby przetestować zachowanie modułu, jak w odpowiedzi @Martijna: – nzifnab
@ nzifnab, aby było jasne, moduł nie dodaje metody, klasa dziecko jest jawnie. Niezależnie od tego, czy wspólne przykłady są tutaj odpowiednie, to wywołanie oceny właściwe dla podstawy kodu. Jednak nadal można z nich korzystać w ten sposób. Możliwe jest przekazywanie im informacji, tak jak w rozmowie: 'it_behaves_like 'działa jak upload',: csv' –