Rspec, Rails: làm thế nào để kiểm tra các phương thức riêng của bộ điều khiển?


125

Tôi có bộ điều khiển:

class AccountController < ApplicationController
  def index
  end

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

Làm thế nào để kiểm tra phương pháp riêng current_accountvới rspec?

PS Tôi sử dụng Rspec2 và Ruby trên Rails 3


8
Điều này không trả lời câu hỏi của bạn, nhưng các phương pháp riêng tư không được thử nghiệm. Các thử nghiệm của bạn chỉ nên quan tâm đến thực tế - API công khai của bạn. Nếu phương thức công khai của bạn hoạt động, thì phương thức riêng tư mà họ gọi cũng hoạt động.
Samy Dindane

77
Tôi không đồng ý. Có giá trị trong việc kiểm tra bất kỳ tính năng đủ phức tạp nào trong mã của bạn.
Whitehat101

11
Tôi cũng không đồng ý. Nếu API công khai của bạn hoạt động, bạn chỉ có thể giả sử các phương thức riêng tư của bạn đang hoạt động như mong đợi. Nhưng thông số kỹ thuật của bạn có thể được thông qua ngẫu nhiên.
Rimian

4
Sẽ là tốt hơn để trích xuất phương thức riêng tư vào một lớp mới có thể kiểm tra được, nếu phương thức riêng cần thử nghiệm.
Kris

10
@RonLugge Bạn nói đúng. Với nhận thức và kinh nghiệm nhiều hơn, tôi không đồng ý với nhận xét ba năm của tôi. :)
Samy Dindane

Câu trả lời:


196

Sử dụng #instance_eval

@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
Nếu bạn thích, bạn cũng có thể nói: @ control.send (: current_account).
Nhầm lẫn

13
Ruby cho phép bạn gọi các phương thức riêng bằng gửi, nhưng điều đó không nhất thiết là bạn nên làm. Kiểm tra các phương thức riêng được thực hiện thông qua kiểm tra giao diện chung cho các phương thức đó. Cách tiếp cận này sẽ hiệu quả, nhưng nó không lý tưởng. Sẽ tốt hơn nếu phương thức nằm trong một mô-đun được đưa vào bộ điều khiển. Sau đó, nó cũng có thể được kiểm tra độc lập với bộ điều khiển.
Brian Hogan

9
Mặc dù, câu trả lời này về mặt kỹ thuật trả lời câu hỏi, tôi đang hạ thấp nó, bởi vì nó vi phạm các thực tiễn tốt nhất trong thử nghiệm. Các phương thức riêng tư không nên được kiểm tra và chỉ vì Ruby cung cấp cho bạn khả năng phá vỡ khả năng hiển thị của phương thức, điều đó không có nghĩa là bạn nên lạm dụng nó.
Srdjan Pejic

24
Srdjan Pejic bạn có thể giải thích lý do tại sao các phương pháp riêng tư không nên được thử nghiệm?
John Bachir

35
Tôi nghĩ bạn hạ thấp câu trả lời sai hoặc không trả lời câu hỏi. Câu trả lời này là chính xác và không nên bỏ qua. Nếu bạn không đồng ý với thực tiễn kiểm tra các phương pháp riêng tư, hãy đưa nó vào phần bình luận vì đó là thông tin tốt (như nhiều người đã làm) và sau đó mọi người có thể nêu lên nhận xét đó, điều này vẫn cho thấy quan điểm của bạn mà không cần hạ thấp một câu trả lời hoàn toàn hợp lệ.
ngày

37

Tôi sử dụng phương thức gửi. Ví dụ:

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

Bởi vì "gửi" có thể gọi các phương thức riêng tư


Làm thế nào bạn sẽ kiểm tra các biến thể hiện trong phương thức riêng bằng cách sử dụng .send?
12

23

Phương thức current_account đang được sử dụng ở đâu? Mục đích của nó là gì?

Nói chung, bạn không thử nghiệm các phương thức riêng tư mà nên kiểm tra các phương thức gọi phương thức riêng tư.


5
Tốt nhất người ta nên kiểm tra mọi phương pháp. Tôi đã sử dụng cả chủ đề.send và topic.instance_eval với nhiều thành công trong rspec
David W. Keith

7
@Pullets Tôi không đồng ý, bạn nên thử nghiệm các phương thức công khai của API sẽ gọi các phương thức riêng tư, như câu trả lời ban đầu của tôi nói. Bạn nên kiểm tra API bạn cung cấp, không phải các phương thức riêng tư mà bạn chỉ có thể thấy.
Ryan Bigg 17/03/2016

5
Tôi đồng ý với @Ryan Bigg. Bạn không kiểm tra các phương pháp riêng tư. Điều này loại bỏ khả năng tái cấu trúc hoặc thay đổi việc triển khai phương thức đã nói, ngay cả khi thay đổi đó không ảnh hưởng đến các phần công khai trong mã của bạn. Xin vui lòng đọc các thực hành tốt nhất khi viết bài kiểm tra tự động.
Srdjan Pejic

4
Hmm, có lẽ tôi đang thiếu một cái gì đó. Các lớp tôi viết có nhiều phương thức riêng tư hơn các phương thức công khai. Chỉ thử nghiệm thông qua API công khai sẽ dẫn đến một danh sách hàng trăm thử nghiệm không phản ánh mã mà họ đang thử nghiệm.
David W. Keith

9
Theo sự hiểu biết của tôi nếu bạn muốn đạt được độ chi tiết thực sự trong thử nghiệm đơn vị phương pháp riêng tư cũng nên được kiểm tra. Nếu bạn muốn cấu trúc lại bài kiểm tra đơn vị mã cũng phải được cấu trúc lại cho phù hợp. Điều này đảm bảo mã mới của bạn cũng hoạt động như mong đợi.
Indika K

7

Bạn không nên kiểm tra trực tiếp các phương thức riêng tư của mình, chúng có thể và nên được kiểm tra gián tiếp bằng cách sử dụng mã từ các phương thức công khai.

Điều này cho phép bạn thay đổi phần bên trong mã của mình mà không phải thay đổi các bài kiểm tra của mình.


4

Bạn có thể đặt phương thức riêng tư hoặc được bảo vệ thành công khai:

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

Chỉ cần đặt mã này trong lớp thử nghiệm của bạn thay thế tên lớp của bạn. Bao gồm không gian tên nếu có.


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

Đơn vị thử nghiệm phương pháp riêng tư dường như quá ngoài bối cảnh với hành vi của ứng dụng.

Bạn đang viết mã cuộc gọi của bạn đầu tiên? Mã này không được gọi trong ví dụ của bạn.

Hành vi là: bạn muốn một đối tượng được tải từ một đối tượng khác.

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

Tại sao bạn muốn viết bài kiểm tra ra khỏi bối cảnh từ hành vi bạn nên cố gắng mô tả?

Mã này có được sử dụng ở nhiều nơi không? Cần một cách tiếp cận chung chung hơn?

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


1

Sử dụng đá quý rspec-bối cảnh-riêng để tạm thời công khai các phương thức riêng tư trong một ngữ cảnh.

gem 'rspec-context-private'

Nó hoạt động bằng cách thêm một bối cảnh chia sẻ vào dự án của bạn.

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

Sau đó, nếu bạn chuyển :privatedưới dạng siêu dữ liệu cho một describekhối, các phương thức riêng tư sẽ được công khai trong bối cảnh đó.

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

0

Nếu bạn cần kiểm tra một hàm riêng, hãy tạo một phương thức công khai gọi phương thức riêng.


3
Tôi giả sử bạn có nghĩa là điều này nên được thực hiện trong mã kiểm tra đơn vị của bạn. Đó thực chất là những gì .instance_eval và .send làm trong một dòng mã. (Và ai muốn viết các bài kiểm tra dài hơn khi một bài kiểm tra ngắn hơn có cùng tác dụng?)
David W. Keith

3
thở dài, đó là một bộ điều khiển đường ray. Phương pháp phải là riêng tư. Cảm ơn đã đọc câu hỏi thực tế.
Michael Johnston

Bạn luôn có thể trừu tượng hóa phương thức riêng tư thành một phương thức công khai trong một đối tượng dịch vụ và tham chiếu nó theo cách đó. Bằng cách đó, bạn chỉ có thể kiểm tra các phương thức công khai, nhưng vẫn giữ mã DRY.
Jason

0

Tôi biết đây là một loại hacky, nhưng nó hoạt động nếu bạn muốn các phương thức có thể kiểm tra bằng rspec nhưng không hiển thị trong prod.

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

Bây giờ khi bạn có thể chạy, bạn sẽ như vậy:

RAILS_ENV=test bundle exec rspec spec/foo_spec.rb 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.