Rspec, Rails: özel kontrol yöntemleri nasıl test edilir?


125

Denetleyicim var:

class AccountController < ApplicationController
  def index
  end

  private
  def current_account
    @current_account ||= current_user.account
  end
end

Özel yöntem current_accountrspec ile nasıl test edilir?

Not: Rspec2 ve Ruby on Rails 3 kullanıyorum


8
Bu, sorunuzu yanıtlamaz, ancak özel yöntemlerin test edilmemesi gerekir. Testleriniz yalnızca gerçek şeyi - genel API'nizi önemsemelidir . Herkese açık yöntemleriniz işe yararsa, aradıkları özel yöntemler de işe yarar.
Samy Dindane

77
Katılmıyorum. Kodunuzdaki yeterince karmaşık herhangi bir özelliği test etmenin değeri vardır.
whitehat101

11
Ben de katılmıyorum. Genel API'niz çalışıyorsa, yalnızca özel yöntemlerinizin beklendiği gibi çalıştığını varsayabilirsiniz. Ama özellikleriniz tesadüfen geçiyor olabilir.
Rimian

4
Özel yöntemin test edilmesi gerekiyorsa, özel yöntemi test edilebilir yeni bir sınıfa çıkarmak daha iyi olacaktır.
Kris

10
@RonLugge Haklısın. Daha fazla geriye bakış ve deneyimle, üç yıllık yorumuma katılmıyorum. :)
Samy Dindane

Yanıtlar:


196

#İnstance_eval kullanın

@controller = AccountController.new
@controller.instance_eval{ current_account }   # invoke the private method
@controller.instance_eval{ @current_account }.should eql ... # check the value of the instance variable

94
İsterseniz şunu da söyleyebilirsiniz: @ controller.send (: current_account).
Confusion

13
Ruby, send ile özel yöntemleri çağırmanıza izin verir, ancak bu yapmanız gerektiği anlamına gelmez. Özel yöntemlerin test edilmesi, genel arabirimin bu yöntemlerle test edilmesiyle yapılır. Bu yaklaşım işe yarayacak, ancak ideal değil. Yöntemin denetleyiciye dahil edilen bir modülde olması daha iyi olurdu. Daha sonra kontrolörden bağımsız olarak da test edilebilir.
Brian Hogan

9
Bu yanıt teknik olarak soruyu yanıtlasa da, testte en iyi uygulamaları ihlal ettiği için olumsuz oy kullanıyorum. Özel yöntemler test edilmemelidir ve Ruby size yöntem görünürlüğünü atlatma yeteneği verdiği için, onu kötüye kullanmanız gerektiği anlamına gelmez.
Srdjan Pejic

24
Srdjan Pejic, özel yöntemlerin neden test edilmemesi gerektiğini açıklar mısınız?
John Bachir

35
Sanırım yanlış olan veya soruya cevap vermeyen cevapları olumsuz değerlendiriyorsunuz. Bu cevap doğrudur ve olumsuz oy verilmemelidir. Özel yöntemleri test etme uygulamasına katılmıyorsanız, bunu iyi bilgi olduğu için yorumlara ekleyin (pek çok kişinin yaptığı gibi) ve sonra insanlar bu yorumu yükseltebilir; bu da, önemli ölçüde geçerli bir cevabı gereksiz yere olumsuz oylamadan yine de gösterir.
traday

37

Gönderme yöntemini kullanıyorum. Örneğin:

event.send(:private_method).should == 2

Çünkü "gönder" özel yöntemleri çağırabilir


Özel yöntem içindeki örnek değişkenlerini kullanarak nasıl test edersiniz .send?
12

23

Current_account yöntemi nerede kullanılıyor? Hangi amaca hizmet ediyor?

Genel olarak, özel yöntemleri test etmezsiniz, bunun yerine özel olanı çağıran yöntemleri test edersiniz.


5
İdeal olarak kişi her yöntemi test etmelidir. Hem subject.send hem de subject.instance_eval'i rspec'te çok başarılı bir şekilde kullandım
David W. Keith

7
@Pullets Ben katılmıyorum, benim orijinal cevabımın dediği gibi, özel olanları çağıracak olan API'nin genel yöntemlerini test etmelisiniz. Yalnızca sizin görebileceğiniz özel yöntemleri değil, sağladığınız API'yi test etmelisiniz.
Ryan Bigg

5
@Ryan Bigg'e katılıyorum. Özel yöntemleri test etmiyorsunuz. Bu, söz konusu yöntemin uygulanmasını yeniden düzenleme veya değiştirme yeteneğinizi ortadan kaldırır, bu değişiklik kodunuzun genel bölümlerini etkilemese bile. Otomatik testler yazarken lütfen en iyi uygulamaları okuyun.
Srdjan Pejic

4
Hmm, belki bir şey kaçırıyorumdur. Yazdığım sınıfların herkese açık olanlardan çok daha fazla özel yöntemi vardı. Yalnızca genel API aracılığıyla test etmek, test ettikleri kodu yansıtmayan yüzlerce testin bir listesi ile sonuçlanır.
David W. Keith

9
Benim anlayışıma göre, birim testlerinde gerçek taneciklik elde etmek istiyorsanız özel yöntemler de test edilmelidir. Yeniden kodlamak istiyorsanız, birim testi de buna göre yeniden düzenlenmelidir. Bu, yeni kodunuzun da beklendiği gibi çalışmasını sağlar.
Indika K

7

Sen gerektiğini değil doğrudan özel yöntemler test, yapabilirler ve kamu yöntemlerle kod egzersiz yoluyla dolaylı olarak test edilmelidir.

Bu, testlerinizi değiştirmek zorunda kalmadan kodunuzun iç kısımlarını değiştirmenize olanak tanır.


4

Herkese açık olarak sizi özel veya korumalı yöntemler yapabilirsiniz:

MyClass.send(:public, *MyClass.protected_instance_methods) 
MyClass.send(:public, *MyClass.private_instance_methods)

Bu kodu, sınıf adınızı değiştirerek test sınıfınıza yerleştirin. Varsa ad alanını ekleyin.


3
require 'spec_helper'

describe AdminsController do 
  it "-current_account should return correct value" do
    class AccountController
      def test_current_account
        current_account           
      end
    end

    account_constroller = AccountController.new
    account_controller.test_current_account.should be_correct             

   end
end

1

Özel yöntemleri test eden birim, uygulamanın davranışıyla çok bağlam dışı görünüyor.

Önce arama kodunu yazıyor musunuz? Bu kod, örneğinizde çağrılmamıştır.

Davranış şudur: başka bir nesneden bir nesnenin yüklenmesini istiyorsunuz.

context "When I am logged in"
  let(:user) { create(:user) }
  before { login_as user }

  context "with an account"
    let(:account) { create(:account) }
    before { user.update_attribute :account_id, account.id }

    context "viewing the list of accounts" do
      before { get :index }

      it "should load the current users account" do
        assigns(:current_account).should == account
      end
    end
  end
end

Açıklamaya çalışmanız gereken davranıştan testi neden bağlam dışında yazmak istiyorsunuz?

Bu kod birçok yerde kullanılıyor mu? Daha genel bir yaklaşıma mı ihtiyacınız var?

https://www.relishapp.com/rspec/rspec-rails/v/2-8/docs/controller-specs/anonymous-controller


1

Özel yöntemleri bir bağlam içinde geçici olarak herkese açık hale getirmek için rspec-context-private gem'i kullanın .

gem 'rspec-context-private'

Projenize paylaşılan bir bağlam ekleyerek çalışır.

RSpec.shared_context 'private', private: true do

  before :all do
    described_class.class_eval do
      @original_private_instance_methods = private_instance_methods
      public *@original_private_instance_methods
    end
  end

  after :all do
    described_class.class_eval do
      private *@original_private_instance_methods
    end
  end

end

Daha sonra, :privatebir describebloğa meta veri olarak aktarırsanız , özel yöntemler bu bağlam içinde genel olacaktır.

describe AccountController, :private do
  it 'can test private methods' do
    expect{subject.current_account}.not_to raise_error
  end
end

0

Özel bir işlevi test etmeniz gerekiyorsa, özel olanı çağıran genel bir yöntem oluşturun.


3
Bunun birim test kodunuzda yapılması gerektiğini kastettiğinizi varsayıyorum. .İnstance_eval ve .send'in tek bir kod satırında yaptığı şey budur. (Ve daha kısa olan aynı etkiye sahipken kim daha uzun testler yazmak ister?)
David W. Keith

3
iç çekiş, bu bir ray kontrolörü. Yöntemin özel olması gerekir. Asıl soruyu okuduğunuz için teşekkürler.
Michael Johnston

Özel yöntemi her zaman bir hizmet nesnesindeki genel bir yönteme soyutlayabilir ve bu şekilde başvurabilirsiniz. Bu şekilde, yalnızca genel yöntemleri test edebilir ve yine de kodunuzu KURU tutabilirsiniz.
Jason

0

Bunun biraz hile olduğunu biliyorum, ancak yöntemlerin rspec tarafından test edilebilmesini ancak prod'da görünmemesini istiyorsanız işe yarıyor.

class Foo
  def public_method
    #some stuff
  end

  eval('private') unless Rails.env == 'test'

  def testable_private_method
    # You can test me if you set RAILS_ENV=test
  end 
end

Şimdi koşabildiğin zaman böyle spesifik olursun:

RAILS_ENV=test bundle exec rspec spec/foo_spec.rb 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.