-
Notifications
You must be signed in to change notification settings - Fork 6
/
README.Rmd
340 lines (258 loc) · 8.03 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
webmockr
========
```{r echo=FALSE}
knitr::opts_chunk$set(
comment = "#>",
collapse = TRUE,
warning = FALSE
)
```
<!-- NOTE: run `make readme` to generate the README.md -->
[![cran checks](https://badges.cranchecks.info/worst/webmockr.svg)](https://cloud.r-project.org/web/checks/check_results_webmockr.html)
[![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
[![R-CMD-check](https://github.com/ropensci/webmockr/workflows/R-CMD-check/badge.svg)](https://github.com/ropensci/webmockr/actions/)
[![codecov](https://codecov.io/gh/ropensci/webmockr/branch/main/graph/badge.svg?token=1zWlEQbaEh)](https://codecov.io/gh/ropensci/webmockr)
[![rstudio mirror downloads](https://cranlogs.r-pkg.org/badges/webmockr)](https://github.com/r-hub/cranlogs.app)
[![cran version](https://www.r-pkg.org/badges/version/webmockr)](https://cran.r-project.org/package=webmockr)
R library for stubbing and setting expectations on HTTP requests.
Port of the Ruby gem [webmock](https://github.com/bblimke/webmock)
<details> <summary><strong>How it works in detail</strong></summary> <p>
```{r details, child="details.html"}
```
</p></details>
## Features
* Stubbing HTTP requests at low http client lib level
* Setting and verifying expectations on HTTP requests
* Matching requests based on method, URI, headers and body
* Support for `testthat` via [vcr][]
* Can be used for testing or outside of a testing context
* Supports async http request mocking with `crul` only
## Supported HTTP libraries
* [crul](https://github.com/ropensci/crul)
* [httr](https://github.com/r-lib/httr)
* [httr2](https://github.com/r-lib/httr2)
## Install
from cran
```{r eval=FALSE}
install.packages("webmockr")
```
Dev version
```{r eval=FALSE}
# install.packages("pak")
pak::pak("ropensci/webmockr")
```
```{r}
library(webmockr)
```
## Enable webmockr
```{r}
webmockr::enable()
```
## Inside a test framework
```{r}
library(crul)
library(testthat)
# make a stub
stub_request("get", "https://httpbin.org/get") %>%
to_return(body = "success!", status = 200)
# check that it's in the stub registry
stub_registry()
# make the request
z <- 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!")
```
```{r echo=FALSE}
stub_registry_clear()
```
## Outside a test framework
```{r}
library(crul)
```
### Stubbed request based on uri only and with the default response
```{r}
stub_request("get", "https://httpbin.org/get")
```
```{r}
x <- HttpClient$new(url = "https://httpbin.org")
x$get('get')
```
set return objects
```{r}
stub_request("get", "https://httpbin.org/get") %>%
wi_th(
query = list(hello = "world")) %>%
to_return(status = 418)
```
```{r}
x$get('get', query = list(hello = "world"))
```
### Stubbing requests based on method, uri and query params
```{r}
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"))
```
```{r}
stub_registry()
```
```{r}
x <- HttpClient$new(url = "https://httpbin.org")
x$get('get', query = list(hello = "world"))
```
### Stubbing requests and set expectation of a timeout
```{r error=TRUE}
stub_request("post", "https://httpbin.org/post") %>% to_timeout()
x <- HttpClient$new(url = "https://httpbin.org")
x$post('post')
```
### Stubbing requests and set HTTP error expectation
```{r error=TRUE}
library(fauxpas)
stub_request("get", "https://httpbin.org/get?a=b") %>% to_raise(HTTPBadRequest)
x <- HttpClient$new(url = "https://httpbin.org")
x$get('get', query = list(a = "b"))
```
## httr integration
```{r}
library(webmockr)
library(httr)
# turn on httr mocking
httr_mock()
```
```{r eval=FALSE}
# no stub found
GET("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
```{r}
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"))
```
now returns mocked response
```{r eval=FALSE}
(res <- GET("https://httpbin.org/get"))
res$status_code
#> [1] 418
res$headers
#> $im_a
#> [1] "teapot"
```
## httr2 integration
```{r message=FALSE}
library(webmockr)
library(httr2)
# turn on httr2 mocking
enable()
```
```{r eval=FALSE}
# no stub found
req <- request("https://hb.opencpu.org/get")
req_perform(req)
#> Error: Real HTTP connections are disabled.
#> Unregistered request:
#> GET https://hb.opencpu.org/get
#>
#> You can stub this request with the following snippet:
#>
#> stub_request('get', uri = 'https://hb.opencpu.org/get')
#> ============================================================
```
make a stub
```{r}
stub_request('get', uri = 'https://hb.opencpu.org/get') %>%
to_return(status = 418, body = "I'm a teapot!!!", headers = list(im_a = "teapot"))
```
now returns mocked response
```{r eval=FALSE}
req <- request("https://hb.opencpu.org/get")
res <- req_perform(req)
res
res$status_code
#> [1] 418
res$headers
#> <httr2_headers/list>
#> im_a: teapot
```
## Writing to disk
Write to a file before mocked request
```{r echo=FALSE}
stub_registry_clear()
request_registry_clear()
```
```{r}
## make a temp file
f <- tempfile(fileext = ".json")
## write something to the file
cat("{\"hello\":\"world\"}\n", file = f)
readLines(f)
## make the stub
invisible(stub_request("get", "https://httpbin.org/get") %>%
to_return(body = file(f)))
## make a request
out <- HttpClient$new("https://httpbin.org/get")$get(disk = f)
readLines(file(f))
```
OR - you can use `mock_file()` to have `webmockr` handle file and contents
```{r}
g <- tempfile(fileext = ".json")
## make the stub
invisible(stub_request("get", "https://httpbin.org/get") %>%
to_return(body = mock_file(g, "{\"hello\":\"mars\"}\n")))
## make a request
out <- crul::HttpClient$new("https://httpbin.org/get")$get(disk = g)
readLines(out$content)
```
Writing to disk is supported in `crul`, `httr`, and `httr2`
## Many requests in a row
e.g., many redirects, then a final successful request
```{r eval=FALSE}
webmockr::enable()
library(crul)
library(fauxpas)
z <- stub_request("get", "https://httpbin.org/get")
to_return(z, status = 200, body = "foobar", headers = list(a = 5))
to_return(z, status = 200, body = "bears", headers = list(b = 6))
to_raise(z, HTTPBadRequest)
z
con <- crul::HttpClient$new(url = "https://httpbin.org")
# the first to_return()
first <- con$get("get")
first
first$parse("UTF-8")
# the second to_return()
second <- con$get("get")
second
second$parse("UTF-8")
# the third to_return() - fails as specified
third <- con$get("get")
```
Note that subsequent requests past the number of responses given with `to_return()`/etc.
simply gives the last response you specified. Although if you set a `to_timeout` or
`to_raise` this feature won't happen since you fail out.
## Contributors
* [Scott Chamberlain](https://github.com/sckott)
* [Aaron Wolen](https://github.com/aaronwolen)
## Meta
* Please [report any issues or bugs](https://github.com/ropensci/webmockr/issues).
* License: MIT
* Get citation information for `webmockr` in R doing `citation(package = 'webmockr')`
* Please note that this package is released with a [Contributor Code of Conduct](https://ropensci.org/code-of-conduct/). By contributing to this project, you agree to abide by its terms.
[vcr]: https://github.com/ropensci/vcr