2010-01-20 35 views
49

Tôi đang tìm để gửi bài thô dữ liệu (ví dụ unparamaterized JSON) đến một trong các bộ điều khiển của tôi để kiểm tra:Cách gửi dữ liệu bài thô trong bài kiểm tra chức năng Rails?

class LegacyOrderUpdateControllerTest < ActionController::TestCase 
    test "sending json" do 
    post :index, '{"foo":"bar", "bool":true}' 
    end 
end 

nhưng điều này mang lại cho tôi một lỗi NoMethodError: undefined method `symbolize_keys' for #<String:0x00000102cb6080>.

Cách chính xác để gửi dữ liệu bài đăng thô ở ActionController::TestCase là gì?

Dưới đây là một số mã điều khiển:

def index 
    post_data = request.body.read 
    req = JSON.parse(post_data) 
end 
+0

Tôi cũng tò mò về cách bạn thực hiện điều này, để thử nghiệm API dựa trên JSON. – tadman

Trả lời

55

Tôi đã gặp vấn đề tương tự hôm nay và đã tìm ra giải pháp.

Trong test_helper.rb xác định các phương pháp sau đây của bạn bên trong ActiveSupport::TestCase:

def raw_post(action, params, body) 
    @request.env['RAW_POST_DATA'] = body 
    response = post(action, params) 
    @request.env.delete('RAW_POST_DATA') 
    response 
end 

Trong thử nghiệm chức năng của mình, sử dụng nó giống như các phương pháp post nhưng vượt qua bài cơ thể thô như là đối số thứ ba.

class LegacyOrderUpdateControllerTest < ActionController::TestCase 
    test "sending json" do 
    raw_post :index, {}, {:foo => "bar", :bool => true}.to_json 
    end 
end 

Tôi thử nghiệm này on Rails 2.3.4 khi đọc bài cơ thể thô sử dụng

request.raw_post 

thay vì

request.body.read 

Nếu bạn nhìn vào source code bạn sẽ thấy rằng raw_post chỉ cần kết thúc tốt đẹp request.body.read bằng séc cho số này RAW_POST_DATA trong mã băm en en request.

+0

Bạn đá, cảm ơn – brian

+1

Cách tiếp cận này tiếp tục hoạt động chính xác trong Rails 3.1 – cfeduke

+1

Huh, yeah Và Rails 3.2 quá. Cảm ơn! – zigomir

-5
post :index, {:foo=> 'bar', :bool => 'true'} 
+0

Đó không phải là JSON thô, đó là giải thích băm của nó. – tadman

0

Phương pháp post hy vọng một hash của các cặp tên-giá trị, vì vậy bạn sẽ cần phải làm một cái gì đó như thế này:

post :index, :data => '{"foo":"bar", "bool":true}' 

Sau đó, trong bộ điều khiển của bạn, hãy lấy dữ liệu được phân tích cú pháp như sau:

post_data = params[:data] 
+0

Tôi đã thử điều này, nó cần phải hoàn toàn nguyên mặc dù {"phản hồi": "lỗi", "lỗi": "không thể phân tích cú pháp yêu cầu: 598: mã thông báo không mong muốn tại 'data = – brian

+0

Bạn phân tích cú pháp JSON như thế nào trong điều khiển của bạn? Bạn có thể thêm một số mã điều khiển cho câu hỏi của bạn? –

+0

ok Tôi sẽ làm điều đó ngay bây giờ – brian

15

Tôi thực sự đã giải quyết các vấn đề tương tự chỉ cần thêm một dòng trước khi mô phỏng yêu cầu đăng rspec. Những gì bạn làm là điền "RAW_POST_DATA". Tôi đã cố gắng xóa thuộc tính var trên bài đăng: tạo, nhưng nếu tôi làm như vậy, nó sẽ không tìm thấy hành động.

Đây là giải pháp của tôi.

 
def do_create(attributes) 
    request.env['RAW_POST_DATA'] = attributes.to_json 
    post :create, attributes 
end 

Trong bộ điều khiển mã bạn cần phải đọc JSON là một cái gì đó tương tự như sau

 
    @property = Property.new(JSON.parse(request.body.read)) 
+1

tuyệt vời! Chỉ một dòng, và tôi đã làm việc đó ngay cả khi không có 'thuộc tính' được gửi vào bài đăng. –

+0

này hoạt động cho Rails 3.2.x, nhưng có vẻ như hack-ish. RSpec nên hỗ trợ các thuộc tính được đăng dưới dạng JSON. Lỗi Rspec? – Tilo

9

Nhìn vào stack trace chạy một bài kiểm tra, bạn có thể có được kiểm soát nhiều hơn về chuẩn bị yêu cầu: ActionDispatch: : Tích hợp :: RequestHelpers.post =>ActionDispatch::Integration::Session.process => Rack::Test::Session.env_for

Bạn có thể chuyển chuỗi json thành: params AND chỉ định kiểu nội dung "application/json". Trong trường hợp khác, loại nội dung sẽ được đặt thành "application/x-www-form-urlencoded" và json của bạn sẽ được phân tích cú pháp chính xác.

Vì vậy, bạn chỉ cần có để xác định "CONTENT_TYPE":

post :index, '{"foo":"bar", "bool":true}', "CONTENT_TYPE" => 'application/json' 
+5

Điều này không phù hợp với tôi. Tôi nhận được một lỗi như "undefined method' symbolize_keys 'cho # " –

+0

Yup, bạn có thể xem nguồn Rails cho' post' (gọi là 'process'). Nếu arg đầu tiên sau khi action là một String, nó sẽ được đọc là 'RAW_POST_DATA'. –

+0

Điều này hoạt động trong Rails 4+, không phải trong một số phiên bản trước đó. –

4

Nếu bạn đang sử dụng RSpec (> = 2.12.0) và viết Yêu cầu thông số kỹ thuật, mô-đun được bao gồm là ActionDispatch::Integration::Runner. Nếu bạn nhìn vào mã nguồn, bạn có thể nhận thấy rằng phương thức post gọi process chấp nhận tham số rack_env.

Tất cả điều này có nghĩa là bạn chỉ có thể làm như sau trong spec của bạn:

#spec/requests/articles_spec.rb 

post '/articles', {}, {'RAW_POST_DATA' => 'something'} 

Và trong bộ điều khiển:

#app/controllers/articles_controller.rb 

def create 
    puts request.body.read 
end 
+1

Điều này chỉ hoạt động cho các bài kiểm tra tích hợp chứ không phải cho các bài kiểm tra chức năng được mở rộng từ ActionController :: TestCase. – bibstha

1

Sử dụng Rails 4, tôi đã tìm cách để làm điều này để kiểm tra xử lý xml thô đã được đăng lên bộ điều khiển. Tôi đã có thể làm điều đó bằng cách chỉ cung cấp các chuỗi các bài:

raw_xml = File.read("my_raw.xml") 
post :message, raw_xml, format: :xml 

Tôi tin rằng nếu tham số được cung cấp là một chuỗi, nó chỉ được thông qua cùng với bộ điều khiển như cơ thể.

+0

Tôi không biết tại sao bạn đang nhận được downvoted, điều này là đúng. Đối số thứ hai cho 'post' có thể là một String hoặc IO, và nó trở thành phần thân. – ZiggyTheHamster

0

Tính đến Rails 4.1.5, đây là điều duy nhất mà làm việc cho tôi:

class LegacyOrderUpdateControllerTest < ActionController::TestCase 
    def setup 
    @request.headers["Content-Type"] = 'application/json' 
    end 

    test "sending json" do 
    post :index, '{"foo":"bar", "bool":true}'.to_json, { account_id: 5, order_id: 10 } 
    end 
end 

cho một url vào/chiếm/5/lệnh/10/mặt hàng. Điều này nhận được các thông số url được truyền đạt cũng như nội dung JSON. Tất nhiên, nếu đơn đặt hàng không được nhúng thì bạn có thể bỏ qua hàm băm params.

class LegacyOrderUpdateControllerTest < ActionController::TestCase 
    def setup 
    @request.headers["Content-Type"] = 'application/json' 
    end 

    test "sending json" do 
    post :index, '{"foo":"bar", "bool":true}'.to_json 
    end 
end 
+0

Chuỗi # to_json có nghĩa là bạn đang chuyển vào một chuỗi JSON chứa JSON trong đó ... đó không phải là điều bạn muốn. – ZiggyTheHamster

2

Phiên bản dành cho Rails 5:

post :create, body: '{"foo": "bar", "bool": true}' 

Xem here - Thông số body chuỗi được xử lý như cơ thể yêu cầu thô.

2

Đối với những người sử dụng thử nghiệm hội nhập Rails5 +, các (không có giấy tờ) cách để làm điều này là để vượt qua một chuỗi trong đối số params, vì vậy:

post '/path', params: raw_body, headers: { 'Content-Type' => 'application/json' } 
+1

Điều này đã ngừng hoạt động đối với tôi trong đường ray 5.1. Nếu bạn nhìn vào request.body.read - nó rỗng. –

0

Trong đường ray, 5.1 các công việc sau đây đối với tôi khi thực hiện một xóa yêu cầu mà cần dữ liệu trong cơ thể:

delete your_app_url, as: :json, env: { 
    "RAW_POST_DATA" => {"a_key" => "a_value"}.to_json 
} 

LƯU Ý: này chỉ hoạt động khi thực hiện một thử nghiệm tích hợp.

+0

Xóa là một lỗi trong thư viện cơ bản. https://github.com/rack-test/rack-test/issues/200 –

Các vấn đề liên quan