Stubbing and setting expectations on 'HTTP' requests. Includes tools for stubbing 'HTTP' requests, including expected request conditions and response conditions. Match on 'HTTP' method, query parameters, request body, headers and more. Can be used for unit tests or outside of a testing context.
R library for stubbing and setting expectations on HTTP requests.
Port of the Ruby gem webmock
The very very short version is: webmockr
helps you stub HTTP requests so you
don't have to repeat yourself.
More details
You tell webmockr
what HTTP request you want to match against and if it sees a
request matching your criteria it doesn't actually do the HTTP request. Instead,
it gives back the same object you would have gotten back with a real request, but
only with the bits it knows about. For example, we can't give back the actual
data you'd get from a real HTTP request as the request wasn't performed.
In addition, if you set an expectation of what webmockr
should return, we
return that. For example, if you expect a request to return a 418 error
(I'm a Teapot), then that's what you'll get.
What you can match against
Plus any single or combination of the following:
hello world
to hello%20world
)message = hello world
to
message = hello%20world
)list(H1 = "value1", content_length = 123, X_CuStOm_hEAder = "foo")
list(h1 = "value1", "Content-Length" = 123, "x-cuSTOM-HeAder" = "foo")
Real HTTP requests
There's a few scenarios to think about when using webmockr
:
After doing
library(webmockr)
webmockr
is loaded but not turned on. At this point webmockr
doesn't
change anythning.
Once you turn on webmockr
like
webmockr::enable()
webmockr
will now by default not allow real HTTP requests from the http
libraries that adapters are loaded for (right now only crul
).
You can optionally allow real requests via webmockr_allow_net_connect()
, and
disallow real requests via webmockr_disable_net_connect()
. You can check
whether you are allowing real requests with webmockr_net_connect_allowed()
.
Certain kinds of real HTTP requests allowed: We don't suppoprt this yet,
but you can allow localhost HTTP requests with the allow_localhost
parameter
in the webmockr_configure()
function.
Storing actual HTTP responses
webmockr
doesn't do that. Check out vcr
testthat
via vcrfrom cran
install.packages("webmockr")
Dev version
devtools::install_github("ropensci/webmockr")
library(webmockr)
webmockr::enable()#> CrulAdapter enabled!#> HttrAdapter enabled!
library(crul)library(testthat) # make a stubstub_request("get", "https://httpbin.org/get") %>% to_return(body = "success!", status = 200)#> <webmockr stub> #> method: get#> uri: https://httpbin.org/get#> with: #> query: #> body: #> request_headers: #> to_return: #> status: 200#> body: success!#> response_headers: #> should_timeout: FALSE#> should_raise: FALSE # check that it's in the stub registrystub_registry()#> <webmockr stub registry> #> Registered Stubs#> GET: https://httpbin.org/get | to_return: with body "success!" with status 200 # make the requestz <- crul::HttpClient$new(url = "https://httpbin.org")$get("get") # run tests (nothing returned means it passed)expect_is(z, "HttpResponse")expect_equal(z$status_code, 200)expect_equal(z$parse("UTF-8"), "success!")
library(crul)
stub_request("get", "https://httpbin.org/get")#> <webmockr stub> #> method: get#> uri: https://httpbin.org/get#> with: #> query: #> body: #> request_headers: #> to_return: #> status: #> body: #> response_headers: #> should_timeout: FALSE#> should_raise: FALSE
x <- HttpClient$new(url = "https://httpbin.org")x$get('get')#> <crul response> #> url: https://httpbin.org/get#> request_headers: #> User-Agent: libcurl/7.54.0 r-curl/3.3 crul/0.7.0.9100#> Accept-Encoding: gzip, deflate#> Accept: application/json, text/xml, application/xml, */*#> response_headers: #> status: 200
set return objects
stub_request("get", "https://httpbin.org/get") %>% wi_th( query = list(hello = "world")) %>% to_return(status = 418)#> <webmockr stub> #> method: get#> uri: https://httpbin.org/get#> with: #> query: hello=world#> body: #> request_headers: #> to_return: #> status: 418#> body: #> response_headers: #> should_timeout: FALSE#> should_raise: FALSE
x$get('get', query = list(hello = "world"))#> <crul response> #> url: https://httpbin.org/get?hello=world#> request_headers: #> User-Agent: libcurl/7.54.0 r-curl/3.3 crul/0.7.0.9100#> Accept-Encoding: gzip, deflate#> Accept: application/json, text/xml, application/xml, */*#> response_headers: #> params: #> hello: world#> status: 418
stub_request("get", "https://httpbin.org/get") %>% wi_th(query = list(hello = "world"), headers = list('User-Agent' = 'libcurl/7.51.0 r-curl/2.6 crul/0.3.6', 'Accept-Encoding' = "gzip, deflate"))#> <webmockr stub> #> method: get#> uri: https://httpbin.org/get#> with: #> query: hello=world#> body: #> request_headers: User-Agent=libcurl/7.51.0 r-cur..., Accept-Encoding=gzip, deflate#> to_return: #> status: #> body: #> response_headers: #> should_timeout: FALSE#> should_raise: FALSE
stub_registry()#> <webmockr stub registry> #> Registered Stubs#> GET: https://httpbin.org/get #> GET: https://httpbin.org/get?hello=world | to_return: with status 418 #> GET: https://httpbin.org/get?hello=world with headers {"User-Agent":"libcurl/7.51.0 r-curl/2.6 crul/0.3.6","Accept-Encoding":"gzip, deflate"}
x <- HttpClient$new(url = "https://httpbin.org")x$get('get', query = list(hello = "world"))#> <crul response> #> url: https://httpbin.org/get?hello=world#> request_headers: #> User-Agent: libcurl/7.54.0 r-curl/3.3 crul/0.7.0.9100#> Accept-Encoding: gzip, deflate#> Accept: application/json, text/xml, application/xml, */*#> response_headers: #> params: #> hello: world#> status: 418
stub_request("post", "https://httpbin.org/post") %>% to_timeout()#> <webmockr stub> #> method: post#> uri: https://httpbin.org/post#> with: #> query: #> body: #> request_headers: #> to_return: #> status: #> body: #> response_headers: #> should_timeout: TRUE#> should_raise: FALSEx <- HttpClient$new(url = "https://httpbin.org")x$post('post')#> Error: Request Timeout (HTTP 408).#> - The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time.
library(fauxpas)stub_request("get", "https://httpbin.org/get?a=b") %>% to_raise(HTTPBadRequest)#> <webmockr stub> #> method: get#> uri: https://httpbin.org/get?a=b#> with: #> query: #> body: #> request_headers: #> to_return: #> status: #> body: #> response_headers: #> should_timeout: FALSE#> should_raise: HTTPBadRequestx <- HttpClient$new(url = "https://httpbin.org")x$get('get', query = list(a = "b"))#> Error: Bad Request (HTTP 400).#> - The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.
library(webmockr)library(httr)#> #> Attaching package: 'httr'#> The following object is masked from 'package:crul':#> #> handle # turn on httr mockinghttr_mock()
# no stub foundGET("https://httpbin.org/get")#> Error: Real HTTP connections are disabled.#> Unregistered request:#> GET https://httpbin.org/get with headers {Accept: application/json, text/xml, application/xml, */*}#> #> You can stub this request with the following snippet:#> #> stub_request('get', uri = 'https://httpbin.org/get') %>%#> wi_th(#> headers = list('Accept' = 'application/json, text/xml, application/xml, */*')#> )#> ============================================================
make a stub
stub_request('get', uri = 'https://httpbin.org/get') %>% wi_th( headers = list('Accept' = 'application/json, text/xml, application/xml, */*') ) %>% to_return(status = 418, body = "I'm a teapot!!!", headers = list(im_a = "teapot"))#> <webmockr stub> #> method: get#> uri: https://httpbin.org/get#> with: #> query: #> body: #> request_headers: Accept=application/json, te...#> to_return: #> status: 418#> body: I'm a teapot!!!#> response_headers: im_a=teapot#> should_timeout: FALSE#> should_raise: FALSE
now returns mocked response
(res <- GET("https://httpbin.org/get"))res$status_code#> [1] 418res$response_headers#> $im_a#> [1] "teapot"
webmockr
in R doing citation(package = 'webmockr')
to_return_()
and wi_th_()
are defunct (#60) (#64)to_return()
gains parameter .list
(#60) (#64)StubbedRequest
, to have better behavior for very long strings such as in headers and bodies (#63)httr
response object to match the date format that httr
uses in real HTTP requests (#58) (#61) via https://github.com/ropensci/vcr/issues/91httr
response objects. httr
makes the list of headers insensitive to case, so we now use that function from the package (#59) (#61)to_return()
and wi_th()
drop use of the lazyeval
package and fall back to using the simple list(...)
- fixes problem where creating stubs was failing within test_that()
blocks due to some weird lazy eval conflicts (i think) (#60) (#64) thanks @karawoo !crul
and httr
. now fixed. (#49) thanks @hlappvcr
if vcr
is not available (#53)response_headers_all
slot (#51) (#54)request_registry()
and stub_registry()
print methods more similar to avoid confusion for users (#35)enable
/disable
to indicate that crul
and httr
supported (#46) (related to #45)requireNamespace
so only run when httr available.onLoad
call, removing commented out code, and add note about creating adapter objects does not load crul and httr packagesenable()
and disable()
methods. even though httr
is in Suggests, we were loading all adapters (crul, httr) with stop
when the package was not found. We now give a message and skip when a package not installed. In addition, we enable()
and disable()
gain an adapter
parameter to indicate which package you want to enable or disable. If adapter
not given we attempt all adapters. Note that this bug shouldn't have affected vcr
users as httr
is in Imports in that package, so you'd have to have httr
installed (#45) thanks to @maelle for uncovering the problemhttr
; see HttrAdapter
for the details; webmockr
now integrates with two HTTP R packages: crul
and httr
(#43) (#44)httr
integration is a new method httr_mock()
to turn on mocking for httr
; and two methods build_httr_response
and build_httr_request
meant for internal usevcr
(now on CRAN) for doing HTTP request cachingenabled()
to ask if webmockr
is enabled, gives a
booleanwi_th()
gains new parameter .list
as an escape hatch to avoid
NSE. examples added in the wi_th
man file to clarify its use?stub_request
(#36)crul
handles all types of matches (#29)crul
versionResponse
class that was not dealing with capitalization
correctlyto_raise()
to say that a matched response should return a certain exception, currently to_raise
accepts error classes from the fauxpas
package (#9)to_timeout()
to say that a matched response should return a timeout. This is a special case of to_raise
to easily do a timeout expectation (#11)request_registry()
to list requests in the request registry (#23)crul
moved to Imports from Suggests as it's the only http client supported for now. will move back to Suggests once we support at least one other http clientwebmockr_configure()
changes: turn_on
has been removed; allow_net_connect
and allow_localhost
were ignored before, but are now used and are now set to FALSE
by default; fixed usage of allow
which now accepts character vector of URLs instead of a boolean; the following correctly marked as being ignored for now until fixed net_http_connect_on_start
, show_stubbing_instructions
, query_values_notation
, show_body_diff
(#19) (#21)webmockr_disable_net_connect()
now accepts an allow
parameter to disable all other connections except those URLs given in allow
webmockr_net_connect_allowed()
now accepts a uri
parameter to test if a URI/URL is allowedstub_registry()
and stub_registry_clea()
manual files (#24)build_crul_request
and build_crul_response
moved outside of the CrulAdapter
class so that they can be accesed like webmockr::
in other packagesenable()
and disable()
now return booleans invisiblyto_return()
method, and docs details on what to input to the methodwi_th()
method, and docs details on what to input to the methodallow_localhost
, which wasn't actually workin before (#25)webmockr_enable()
and webmockr_disable
are now defunct. Use webmockr::enable()
and webmockr::disable()
instead