Perpustakaan untuk menanggalkan dan menetapkan harapan pada permintaan HTTP di Ruby.
Meneguk permintaan HTTP di level lib klien http rendah (tidak perlu mengubah tes saat Anda mengubah pustaka http)
Menetapkan dan memverifikasi ekspektasi pada permintaan HTTP
Permintaan pencocokan berdasarkan metode, URI, header dan tubuh
Pencocokan cerdas dari URI yang sama dalam representasi yang berbeda (juga bentuk yang dikodekan dan tidak dikodekan)
Pencocokan cerdas dari header yang sama dalam representasi yang berbeda.
Dukungan untuk Tes :: Unit
Dukungan untuk RSPEC
Dukungan untuk Minitest
Async :: http :: klien
Curb (saat ini hanya trotoar :: mudah)
Em-http-request
Excon
HttpClient
Permata http (juga dikenal sebagai http.rb)
httpx
Manticore
Net :: http dan perpustakaan lainnya berdasarkan net :: http, misalnya:
Httparty
Klien istirahat
Pelindung
Typhoeus (saat ini hanya Typhoeus :: Hydra)
MRI 2.6
MRI 2.7
MRI 3.0
MRI 3.1
MRI 3.2
MRI 3.3
Jruby
Permata Instal Webmock
atau sebagai alternatif:
# Tambahkan ke Gemfilegroup Anda: Tes lakukan Akhiri "webmock" permata
git clone http://github.com/bblimke/webmock.gitcd webmock instalasi rake
Webmock 2.x telah berubah sejak versi 1.x. Perubahan tercantum dalam changelog.md
Buat features/support/webmock.rb
dengan konten berikut:
membutuhkan 'webmock/mentimun'
Tambahkan kode berikut ke test/test_helper
:
membutuhkan 'webmock/minitest'
Tambahkan kode berikut ke spec/spec_helper
:
membutuhkan 'webmock/rspec'
Tambahkan kode berikut ke test/test_helper.rb
membutuhkan 'webmock/test_unit'
Anda juga dapat menggunakan webmock di luar kerangka tes:
Membutuhkan 'webmock'include webmock :: apiwebmock.enable!
stub_request (: any, "www.example.com") net :: http.get ("www.example.com", "/") # ===> sukses
stub_request (: posting, "www.example.com"). dengan (body: "ABC", header: {'konten-panjang' => 3}) uri = uri.parse ("http://www.example.com/") req = net :: http :: post. BARU (URI.PATH) REQ ['Content-Length'] = 3res = net :: http.start (uri.host, uri.port) do | http | http.request (req, "ABC") end # ===> Sukses
stub_request (: posting, "www.example.com"). dengan (body:/world $/, header: {"content-type" => /image/.+/}). to_return (body: "abc") uri = uri.parse ('http://www.example.com/') req = net :: http :: post.new (uri.path) req ['tipe konten' ] = 'image/png'res = net :: http.start (uri.host, uri.port) do | http | http.request (req, 'halo dunia') end # ===> sukses
stub_request (: posting, "www.example.com"). dengan (body: {data: {a: '1', b: 'lima'}}) restclient.post ('www.example.com', "data [a] = 1 & data [b] = lima", content_type: 'Application/X-WWW-Form-Urlencoded') # ===> SuccessRestClient.Post ('www.example.com', '{"data": {"a": "1", "b": "lima"}}', content_type: 'Application /JSON') # ===> SUCCESTRESTCLIENT.POST ('www.example.com', '<data a = "1" b = "lima" />', content_type: 'Application/xml') # ===> Sukses
stub_request (: posting, "www.example.com"). dengan (body: hash_including ({data: {a: '1', b: 'lima'}})) restclient.post ('www.example.com', "data [a] = 1 & data [b] = lima & x = 1 ",: content_type => 'Application/X-WWW-Form-Urlencoded') # ===> Sukses
stub_request (: any, "www.example.com"). dengan (header: {'header-name' => 'header-value'}) uri = uri.parse ('http://www.example.com/') req = net :: http :: post.new ( Uri.path) req ['header-name'] = 'header-value'res = net :: http.start (uri.host, uri.port) do | http | http.request (req, 'abc') end # ===> sukses
stub_request (: get, 'www.example.com'). dengan (header: {'accept' => ['image/jpeg', 'Image/png']}) req = net :: http :: get.new ("/") req ['accept'] = [' Image/png '] req.add_field (' terima ',' image/jpeg ') net :: http.start ("www.example.com") {| http | http.request (req)} # ===> sukses
stub_request (: post, "www.example.com"). Dengan {| permintaan | request.body == "ABC"} restclient.post ('www.example.com', 'ABC') # ===> Sukses
stub_request (: get, "www.example.com"). Dengan (BASIC_Auth: ['user', 'pass'])# atau# stub_request (: get, "www.example.com"). dengan (header: {'Otorisasi' => "Dasar #{base64.strict_encode64 ('user: pass'). Chomp}"}) net :: http.start ('www.example.com') do | http | req = net :: http :: get.new ('/') req.basic_auth 'user', 'pass' http.Request (req) end # ===> Sukses
stub_request(:get, "user:[email protected]")
tidak cocok dengan permintaan dengan kredensial yang disediakan di header otorisasi.stub_request (: get, "user: [email protected]") restclient.get ('user: [email protected]') # ===> sukses
stub_request (: any, /example/)net::http.get('www.example.com ','/') # ===> Sukses
stub_request (: any, -> (uri) {true})
URI_TEMPLATE = ALLAVESABLE :: Template.new "www.example.com/{id}/"stub_request(:any, uri_template) net :: http.get ('www.example.com', '/webmock/') # = ==> Sukses
Uri_template = Alamat :: template.new "www.example.com/thing/{id}.json {?x.y.z} {&other*}"stub_request(:any, uri_template) net :: http.get ('www. example.com ', '/thing/5.json?x=1&y=2&z=3&anyyparam=4') # ===> Sukses
stub_request (: get, "www.example.com"). dengan (query: {"a" => ["b", "c"]}) restclient.get ("http://www.example.com/ ? a [] = b & a [] = c ") # ===> sukses
stub_request (: get, "www.example.com"). dengan (query: hash_including ({"a" => ["b", "c"]})) restclient.get ("http://www.example.com/?auarkan==B&aace==c&x= 1 ") # ===> Sukses
stub_request (: get, "www.example.com"). dengan (query: hash_excluding ({"a" => "b"})) restclient.get ("http://www.example.com/?a=b") # ===> failurerestclient.get ("http : //www.example.com/? A = C ") # ===> Sukses
stub_request (: any, "www.example.com"). to_return (body: "abc", status: 200, header: {'konten-panjang' => 3}) net :: http.get ("www.example.com", '/') # ===> " ABC "
Tetapkan tipe konten yang sesuai untuk parsed_response
httparty.
stub_request (: any, "www.example.com"). TO_RETURN BODY: '{}', header: {content_type: 'Application/json'}
File.open ('/tmp/response_body.txt', 'w') {| f | f.puts 'ABC'} stub_request (: any, "www.example.com"). to_return (body: file.new ('/tmp/response_body.txt'), status: 200) net :: http.get ('www.example.com', '/') # ===> "ABCN"
stub_request (: any, "www.example.com"). to_return_json (body: {foo: "bar"}) net :: http.get ('www.example.com', '/') # ===> "{" foo ":" bar "}"
stub_request (: any, "www.example.com"). to_return (status: [500, "Kesalahan server internal"]) req = net :: http :: get.new ("/") net :: http.start ("www.example.com") {| http | http.request (req)}. Pesan # ===> "Kesalahan server internal"
curl -is
curl -is www.example.com > /tmp/example_curl_-is_output.txt
raw_response_file = file.new ("/tmp/example_curl_-is_output.txt")
dari file
stub_request (: get, "www.example.com"). to_return (raw_response_file)
atau string
stub_request (: get, "www.example.com"). to_return (raw_response_file.read)
stub_request (: any, 'www.example.net'). to_return {| request | {body: request.body}} restclient.post ('www.example.net', 'abc') # ===> "abcn"
stub_request (: any, 'www.example.net'). to_return (lambda {| request | {body: request.body}}) restclient.post ('www.example.net', 'abc') # ===> "abcn"
curl -is
curl -is www.example.com > /tmp/www.example.com.txt
stub_request (: get, "www.example.com"). to_return (lambda {| request | file.new ("/tmp/#{request.uri.host.to_s} .txt")}))
stub_request (: any, 'www.example.net'). to_return (body: lambda {| request | request.body}) restclient.post ('www.example.net', 'abc') # ===> "abcn"
Kelas MyRackApp def self.call (env) [200, {}, ["hello"]] endendStub_request (: get, "www.example.com"). to_rack (myrackapp) restclient.post ('www.example.com') # ===> "halo"
stub_request (: any, 'www.example.net'). to_raise (StandardError) restclient.post ('www.example.net', 'abc') # ===> StandardError
stub_request (: any, 'www.example.net'). to_raise (standarderror.new ("beberapa kesalahan")))
stub_request (: any, 'www.example.net'). to_raise ("beberapa kesalahan")
stub_request (: any, 'www.example.net'). TO_TIMEOUTRESTCLIENT.POST ('www.example.net', 'ABC') # ===> RestClient :: RequestTimeout
stub_request (: get, "www.example.com"). to_return ({body: "abc"}, {body: "def"}) net :: http.get ('www.example.com', '/') # ===> "abcn" net :: http. get ('www.example.com', '/') # ===> "DEFN" #After All Responses digunakan, respons terakhir akan dikembalikan tanpa batas :: http.get ('www.example.com', ' /') # ===> "defn"
to_return()
, to_raise()
atau to_timeout
stub_request (: get, "www.example.com"). to_return ({body: "ABC"}). Lalu. #then () hanyalah gula sintaksis to_return ({body: "def"}). Lalu. to_raise (myException) net :: http.get ('www.example.com', '/') # ===> "abcn" net :: http.get ('www.example.com', '/') # ===> "defn" net :: http.get ('www.example.com', '/') # ===> MyException dinaikkan
stub_request (: get, "www.example.com"). to_return ({body: "ABC"}). Times (2) .then. to_return ({body: "def"}) net :: http.get ('www.example.com', '/') # ===> "abcn" net :: http.get ('www.example.com ','/') # ===> "abcn" net :: http.get (' www.example.com ','/') # ===> "defn"
stub_get = stub_request (: get, "www.example.com") Remest_request_stub (stub_get)
Webmock.allow_net_connect! Stub_request (: any, "www.example.com"). To_return (body: "abc") net :: http.get ('www.example.com', '/') # ===> "ABC" net :: http.get ('www.something.com', '/') # ===> /.+something.+/webmock.disable_net_connect!net::http.get('www.something. com ','/') # ===> kegagalan
Webmock.disable_net_connect! (Izin_localhost: true) net :: http.get ('www.something.com', '/') # ===> failurenet :: http.get ('localhost: 9887', '/') # ===> Diizinkan. Mungkin untuk Selenium?
Permintaan yang diizinkan dapat ditentukan dalam beberapa cara.
Dengan String
yang menentukan nama host:
Webmock.disable_net_connect! (Izinkan: 'www.example.org') restclient.get ('www.something.com', '/') # ===> failurerestClient.get ('www.example.org', '/ ') # ===> diizinkanRestClient.get (' www.example.org:8080 ','/') # ===> diizinkan
Dengan String
yang menentukan nama host dan port:
Webmock.disable_net_connect! (Izinkan: 'www.example.org:8080')RestClient.get('www.something.com', '/') # ===> failurerestclient.get ('www.example.org', '/') # ===> failurerestclient.get ('www.example.org:8080', '/') # ===> diizinkan
Dengan Regexp
yang cocok dengan URI:
Webmock.disable_net_connect! (Izinkan: %r {ample.org/foo}) restclient.get ('www.example.org', '/foo/bar') # ===> diizinkanRestClient.get ('sample.org' , '/foo') # ===> diizinkanRestClient.get ('sample.org', '/bar') # ===> kegagalan
Dengan objek yang merespons #call
, menerima objek URI
dan mengembalikan boolean:
Denylist = ['google.com', 'facebook.com', 'apple.com'] diizinkan_sites = lambda {| uri | Denylist.None? {| Situs | uri.host.include? (Situs)}} webmock.disable_net_connect! (Izinkan: diizinkan_sites) restclient.get ('www.example.org', '/') # ===> diizinkanrestClient.get ('www.facebook. com ','/') # ===> failurerestclient.get (' apple.com ','/') # ===> kegagalan
Dengan Array
hal di atas:
Webmock.disable_net_connect! (Izinkan: [ Lambda {| uri | uri.host.length % 2 == 0}, /ample.org/, 'bbc.co.uk',]) restclient.get ('www.example.org', '/') # ===> diizinkanRestClient.get ('bbc.co.uk', '/') # == => DiizinkanRestClient.get ('bbc.com', '/') # ===> diizinkanRestClient.get ('www.bbc.com', '/') # ===> kegagalan
HTTP Protocol memiliki 3 langkah: terhubung, permintaan, dan respons (atau 4 dengan tutup). Sebagian besar pustaka klien Ruby HTTP memperlakukan Connect sebagai bagian dari langkah permintaan, dengan pengecualian Net::HTTP
yang memungkinkan koneksi pembukaan ke server secara terpisah ke permintaan, dengan menggunakan Net::HTTP.start
.
Webmock API juga dirancang dengan Connect menjadi bagian dari Langkah Permintaan, dan hanya memungkinkan permintaan yang mematikan, bukan koneksi. Ketika Net::HTTP.start
dipanggil, Webmock belum tahu apakah permintaan dibatalkan atau tidak. Webmock secara default menunda koneksi sampai permintaan diminta, jadi ketika tidak ada permintaan, Net::HTTP.start
tidak melakukan apa -apa. Ini berarti bahwa webmock memecahkan jaring :: perilaku http secara default!
Untuk mengatasi masalah ini, WebMock menawarkan :net_http_connect_on_start
, yang dapat diteruskan ke WebMock.allow_net_connect!
dan WebMock.disable_net_connect!
metode, yaitu
Webmock.allow_net_connect! (Net_http_connect_on_start: true)
Ini memaksa Webmock Net :: HTTP Adapter untuk selalu terhubung di Net::HTTP.start
. Pada saat koneksi yang dibuat tidak ada informasi tentang permintaan atau URL, oleh karena itu Webmock tidak dapat memutuskan apakah akan mematikan permintaan atau tidak dan semua koneksi diizinkan. Untuk mengaktifkan koneksi hanya ke domain tertentu (misalnya server uji Anda) Gunakan:
Webmock.allow_net_connect! (Net_http_connect_on_start: "www.example.com")
Membutuhkan 'webmock/test_unit'stub_request (: any, "www.example.com") uri = uri.parse (' http://www.example.com/ ') req = net :: http :: post.new ( Uri.path) req ['konten-panjang'] = 3res = net :: http.start (uri.host, uri.port) do | http | http.request (req, 'ABC') Endassert_requested: Post, "http://www.example.com", header: {'content-length' => 3}, body: "ABC", Waktu: 1 # ===> Successassert_not_requested: Get, "http://www.something.com" # ===> Successassert_requested (: post, "http://www.example.com", kali: 1) {| req | req.body == "ABC"}
Webmock.allow_net_connect! Net :: http.get ('www.example.com', '/') # ===> Successassert_requested: Get, "http://www.example.com" # ===> SUCCESS
stub_get = stub_request (: get, "www.example.com") stub_post = stub_request (: post, "www.example.com") net :: http.get ('www.example.com', '/') Assert_requested (stub_get) assert_not_requested (stub_post)
WebMock
Gaya ini dipinjam dari FakeWeb-Matcher
Membutuhkan 'webmock/rspec'expect (webmock) .to have_requested (: get, "www.example.com"). dengan (body: "ABC", header: {'konten-panjang' => 3}). TwiceExpect (webmock) .not_to have_requested (: get, "www.something.com") harapkan (webmock) .to have_requested (: Posting, "www.example.com"). dengan {| req | req.body == "ABC"}# Perhatikan bahwa blok dengan `do ... end` alih -alih kurung keriting tidak akan berfungsi!# Mengapa? Lihat komentar ini https://github.com/bblimke/webmock/issues/174#issueComment-34908908 Expect(webmock).to have_requested (: get, "www.example.com"). dengan (query: {"a" => ["b", "c"]}) harapkan (webmock) .to have_requested (: get, "www.example.com"). dengan (query: hash_including ({"a" => ["b", "c"]})) harapkan (webmock) .to have_requested (: get, "www.example.com"). dengan (body: {"a" => ["b", "c"]}, header: {'content-type' => 'application/json'})
a_request
Harapkan (a_request (: post, "www.example.com"). dengan (tubuh: "ABC", header: {'panjang konten' => 3})). to have_been_made.onceExpect (a_request (: post, "www.something.com")). to have_been_made.times (3) harapkan (a_request (: post, "www.something.com")). To have_been_made.at_least_onceExpect.com ")). (: Posting, "www.something.com")). untuk memiliki_been_made.at_least_times (3) harapkan (a_request (: post, "www.something.com")). to have_been_made.at_most_twiceExpect (a_request (: post, "www.something.com")). to have_been_made.made.made. ) harapkan (a_request (: any, "www.example.com")). not_to have_been_madeExpect (a_request (: post, "www.example.com"). Dengan {| req | req.body == "ABC"})) . untuk memiliki_been_madeExpect (a_request (: get, "www.example.com"). dengan (kueri: {"a" => ["b", "c"]})). untuk memiliki_been_madeExpect (a_request (: get, "www.example.com"). dengan (query: hash_including ({"a" => ["b", "c"]}))). to have_been_madeexpect (a_request (: post, "www.example.com"). dengan (body: {"a" => ["b", "c"]}, header: {'content-type' => 'application/json'})). to have_been_made
stub = stub_request (: get, "www.example.com")# ... membuat permintaan ... harapkan (stub) .to have_been_requested
Jika Anda ingin mengatur ulang semua rintisan saat ini dan riwayat permintaan, gunakan WebMock.reset!
stub_request (: any, "www.example.com") net :: http.get ('www.example.com', '/') # ===> SuccessWebMock.reset! Net :: http.get ('www .example.com ','/') # ===> failureassert_not_requested: get, "www.example.com" # ===> sukses
Jika Anda hanya ingin mengatur ulang penghitung permintaan yang dieksekusi, gunakan WebMock.reset_executed_requests!
stub = stub_request (: get, "www.example.com") stub2 = stub_request (: get, "www.example2.com") net :: http.get ('www.example.com', '/') net :: http.get ('www.example.com', '/')net:http.get('www.example2.com', '/') expect(stub).to have_been_requested.times (2) (stub2) .to have_been_requested.times (1) webmock.reset_executed_requests! harapkan (stub) .not_to have_been_requestedexpect (stub2) .not_to have_been_requested
# Nonaktifkan webmock (semua adapter) webmock.disable!# Nonaktifkan webmock untuk semua lib kecuali net :: httpwebmock.disable! (Kecuali: [: net_http])# aktifkan webmock (semua adaptor) webmock.enable!# Imbas webmock untuk semua libs Kecuali PatronWebMock.enable! (Kecuali: [: pelindung])
Permintaan yang dieksekusi cocok dengan permintaan yang menanggalkan jika lulus kriteria berikut:
Saat meminta URI cocok dengan permintaan string URI, pola RegExp atau RFC 6570 Template URI
Dan metode permintaan sama dengan metode permintaan yang bertekuk atau metode permintaan yang bertengkar adalah: apapun
Dan Badan Permintaan sama dengan Badan Permintaan yang Stokbed atau Badan Permintaan yang Bertahan tidak ditentukan
Dan header permintaan mencocokkan header permintaan yang mematikan, atau header permintaan yang mematikan cocok dengan subset header permintaan, atau header permintaan yang dibumbui tidak ditentukan
Dan permintaan pertandingan yang disediakan blok atau blok tidak disediakan
Selalu yang terakhir dinyatakan Pencocokan Stub. Permintaan akan diterapkan yaitu:
stub_request (: get, "www.example.com"). to_return (body: "abc") stub_request (: get, "www.example.com"). to_return (body: "def") net :: http.get ('www.example.com', '/') # ====> "def"
Webmock akan cocok dengan semua representasi berbeda dari URI yang sama.
Yaitu semua representasi URI berikut ini sama:
"www.example.com" "www.example.com/" "www.example.com:80" "www.example.com:80/" "http://www.example.com" "http: // www.example.com/""http://www.example.com:80""http://www.example.com:80/ "
URI berikut dengan userInfo juga sama untuk webmock
"A B: [email protected]" "A B: [email protected]/" "A B: [email protected]: 80" "A b: [email protected]: 80/" "http: // a b: [email protected]" "http: // a b: [email protected]/" "http: // a b: [email protected]: 80" "http : // A B: [email protected]: 80/"" A%20B: [email protected] "" A%20B: [email protected]/"" A%20B: PASS@ www.example.com:80""A%20b:[email protected]:80/""http://a%20b:[email protected]"maTtp://a%20b: [email protected]/ "" http: // a%20b: [email protected]: 80 "" http: // a%20b: [email protected]: 80/"
atau ini
"www.example.com/my path/? a = my param & b = c" "www.example.com/my%20path/?a=my%20param&b=c" "www.example.com:80/my/ ? a = param saya & b = c "" www.example.com:80/my%20path/?a=my%20param&b=c "" http://www.example.com/my path/? a = my param & b = b = C "" http://www.example.com/my%20path/?a=my%20param&b=c "" http://www.example.com:80/my path/? a = param & b = c " "http://www.example.com:80/my%20path/?a=my%20param&b=c"
Jika Anda memberikan RegExp untuk mencocokkan URI, Webmock akan mencoba mencocokkannya dengan setiap bentuk yang valid dari URL yang sama.
IE /my path/
akan cocok dengan www.example.com/my%20path
karena setara dengan www.example.com/my path
Jika Anda menggunakan alamat yang dapat dialamatkan :: Template untuk dicocokkan, maka WebMock akan menunda aturan pencocokan yang dapat dialamatkan, yang sesuai dengan RFC 6570.
Jika Anda menggunakan salah satu metode webmock untuk mencocokkan Params kueri, maka alamat yang dapat digunakan akan digunakan untuk mencocokkan pangkalan URI dan webmock akan cocok dengan Params kueri. Jika tidak, maka Webmock akan membiarkan alamat cocok dengan URI lengkap.
Webmock akan mencocokkan header permintaan dengan header permintaan yang bertekuk dalam situasi berikut:
Permintaan yang bertengkar memiliki header yang ditentukan dan meminta header sama dengan header yang bertekuk
yaitu header yang bertengkar: { 'Header1' => 'Value1', 'Header2' => 'Value2' }
, diminta: { 'Header1' => 'Value1', 'Header2' => 'Value2' }
Permintaan yang bertengkar memiliki header yang ditentukan dan header permintaan yang bertekuk adalah subset dari header permintaan
IE header yang bertengkar: { 'Header1' => 'Value1' }
, diminta: { 'Header1' => 'Value1', 'Header2' => 'Value2' }
Permintaan yang mematikan tidak memiliki header
IE header yang bertengkar: nil
, diminta: { 'Header1' => 'Value1', 'Header2' => 'Value2' }
Webmock normales header dan memperlakukan semua bentuk header yang sama sama: yaitu dua set header berikut adalah sama:
{ "Header1" => "value1", content_length: 123, X_CuStOm_hEAder: :value }
{ header1: "value1", "Content-Length" => 123, "x-cuSTOM-HeAder" => "value" }
Untuk merekam interaksi HTTP nyata aplikasi Anda dan mengulanginya nanti dalam tes, Anda dapat menggunakan VCR dengan webmock.
Webmock.after_request do | request_signature, respons | menempatkan "permintaan #{request_signature} dibuat dan #{respons} dikembalikan" end
Webmock.after_request (kecuali: [: pelindung], real_requests_only: true) do | req_signature, response | menempatkan "permintaan #{req_signature} dibuat dan #{response} dikembalikan" end
Harap kirimkan di sini http://github.com/bblimke/webmock/issues
Anda dapat berkontribusi dengan triaging isu -isu yang mungkin termasuk mereproduksi laporan bug atau meminta informasi penting, seperti nomor versi atau instruksi reproduksi. Jika Anda ingin memulai triaging masalah, salah satu cara mudah untuk memulai adalah dengan berlangganan Webmock pada codetriage.
Jika Anda memiliki saran tentang cara meningkatkan webmock, silakan kirim email ke mailing list groups.google.com/group/webmock-users
Saya sangat tertarik dengan bagaimana DSL dapat ditingkatkan.
Untuk bekerja di webmock, pertama -tama Anda perlu membayar dan mengkloning repo. Harap lakukan pekerjaan apa pun di cabang khusus dan rebase terhadap Master sebelum mengirim permintaan tarik.
Baris awal proyek ini ditulis selama hari peretasan bambu baru berkat sesama bambino saya untuk semua saran hebat!
Orang yang mengirimkan tambalan dan fitur baru atau perbaikan yang disarankan. Banyak terima kasih kepada orang -orang ini:
Ben Pickles
Mark Evans
Ivan Vega
Piotr Usewicz
Nick Plante
Nick Quaranto
Diego E. "Flameeyes" Pettenò
Niels Meersschaert
Mack Earnhardt
Arvicco
Sergio Gil
Jeffrey Jones
Tekin Suleyman
Tom Ward
Nadim Bitar
Myron Marston
Sam Phillips
Jose Angel Cortinas
Razia
Steve Tooke
Nathaniel Bibler
Martyn Loughran
Muness Alrubaie
Charles Li
Ryan Bigg
Pete Higgins
Hans de Graaff
Alastair Brunton
Sam Stokes
Eugene Bolshakov
James Conroy-Finn
Salvador Fuentes Jr
Alex Rothenberg
Aidan Feldman
Steve Hull
Jay Adkisson
Zach Dennis
Nikita Fedyashev
Lin Jen-Shin
David Yeu
Andreas Garnæs
Roman Shterenzon
Chris McGrath
Stephen Celis
Eugene Pimenov
Albert Llop
Christopher Pickslay
Tammer Saleh
Nicolas Fouché
Joe Van Dyk
Mark Abramov
Frank Schumacher
Dimitrij Denissenko
Marnen Laibow-Koser
Evgeniy Dolzhenko
Nick Recobra
Jordan Elver
Joe Karayusuf
Paul Cortens
JUUGYO
Aindustry
Eric Oestrich
Erwanlr
Ben Bleything
Jon Leighton
Ryan Schlesinger
Julien Boyer
Kevin Glowacz
Hans Hasselberg
Andrew France
Jonathan Hyman
Rex Feng
Pavel Forkert
Jordi Massaguer Pla
Jake Benilov
Tom Beauvais
Mokevnin Kirill
Alex Grant
Lucas Dohmen
Bastien Vaucher
Joost Baaij
Joel Chippindale
Murahashi Sanemat Kenichi
Tim Kurvers
Ilya Vassilevsky
gotwalt
Leif Bladt
Alex Tomlins
Mitsutaka Mimura
Tomy Kaira
Daniel Van Hoesel
Ian asaff
Ian Lesperance
Matthew Horan
Dmitry Gutov
Florian Dütsch
Manuel Meurer
Brian D. Burns
Riley Strong
Tamir Duberstein
Stefano Uliari
Alex Stupakov
Karen Wang
Matt Burke
Jon Rowe
Aleksey V. Zapparov
Praveen Arimbrathodiyil
Bo Jeanes
Matthew Conway
Rob Olson
Max Lincoln
Oleg Gritsenko
Hwan-joon choi
Shibata Hiroshi
Caleb Thompson
Theo Hultberg
Pablo Jairala
Insoo Buzz Jung
Carlos Alonso Pérez
Trlorenz
Alexander Simonov
Thorbjørn Hermanse
Mark Lorenz
tjsousa
Tasos Stathopoulos
Dan Buettner
Sven Riedel
Mark Lorenz
Dávid Kovács
Fishermand46
Franky Wahl
Chayoung Anda
Simon Russell
Steve Mitchell
Mattias Putman
Zachary Anker
Emmanuel Sambo
Ramon Tayag
Johannes Schlumberger
Siôn Le Roux
Matt Palmer
Zhao Wen
Krzysztof Rygielski
Land Magne
yurivm
Mike Knepper
Charles Pence
Alexey Zapparov
Pablo Brasero
Cedric Pimenta
Michiel Karnebeek
Alex Kestner
Manfred Stienstra
Tim Diggins
Gabriel Chaney
Chris Griego
Taiki ono
Jonathan Schatz
Jose Luis Honorato
Aaron Kromer
Pavel Jurašek
Jake Worth
Gabe Martin-Dempesy
Michael Grosser
Aleksei Maridashvili
Ville Lautanala
Koichi Ito
Jordan Harband
Tarmo Tänav
Joe Marty
Chris Thomson
Vít Ondruch
George Ulmer
Christof Koenig
Chung-yi chi
Olexandr Hoshylyk
Janko Marohnić
Pat Allan
Lagu Rick
Naruse, Yui
Piotr boniecki
Olia Kremmyda
Michał Matyas
Matt Brictson
Kenny Ortmann
redbar0n
Lukas Pokorny
Arkadiy Tetelman
Kazato Sugimoto
Olle Jonsson
Pavel Rosický
Geremia taglialatela
Koichi Sasada
Yusuke Endoh
Baker abu -abu
Soonkhen Owyong
Pavel Valena
Adam Sokolnicki
Jeff Felchner
Eike Send
Claudio Poli
CSABA APAGYI
Frederick Cheung
Fábio D. Batista
Andriy Yanko
y-yagi
Rafael França
George Claghorn
Alex Junger
Orien Madgwick
Andrei Sidorov
Marco Costa
Ryan Davis
Brandur
Samuel Williams
Patrik Ragnarsson
Alex Coomans
Vesa Laakso
John Hawthorn
Guppy0356
Thilo Rusche
Andrew Stuntz
Lucas Uyezu
Bruno Sutic
Ryan Kerr
Adam Harwood
Ben Koshy
Jesse Bowes
Marek Kasztelnik
CE07C3
Jun Jiang
OLEKSIY KOVYRIN
Matt Larraz
Tony Schneider
Niklas Hösl
Johanna Hartmann
Alex Vondrak
Will Storey
Eduardo Hernandez
Ojab
Giorgio Gambino
Timmitry
Michael Fairley
Ray Zane
Pergi sueyoshi
Cedric Sohrauer
Akira Matsuda
Mark Spangler
Henrik Nyh
Yoann Lecuyer
Lucas Arnaud
Marc Rohloff
Inkstak
Yuki Inoue
Brandon Weaver
Josh Nichols
Ricardo Trindade
Earlopain
James Brown
Kazuhiro Nishiyama
Étienne Barrié
Matt Brown
Victor Maslov
Gio Lodi
Ryan Brooks
Jacob Frautschi
Christian Schmidt
Rodrigo Argumedo
Untuk daftar lengkap kontributor, Anda dapat mengunjungi halaman kontributor.
Terima kasih FakeWeb! Perpustakaan ini terinspirasi oleh FakeWeb. Saya mengimpor beberapa solusi dari proyek itu ke Webmock. Saya juga menyalin beberapa kode yaitu net: adaptor http. Sayangnya, arsitektur FakeWeb tidak memungkinkan saya untuk memperpanjangnya dengan fitur yang saya butuhkan. Saya juga lebih suka beberapa hal untuk bekerja secara berbeda yaitu meminta stop prioritas.
Hak Cipta (C) 2009-2010 Bartosz Blimke. Lihat lisensi untuk detailnya.