diff --git a/main.epub b/main.epub index 378ab04..fd87fa2 100644 Binary files a/main.epub and b/main.epub differ diff --git a/main.pdf b/main.pdf index adbbc0f..2b8ad84 100644 Binary files a/main.pdf and b/main.pdf differ diff --git a/search.json b/search.json index c820caa..049cd2c 100644 --- a/search.json +++ b/search.json @@ -1 +1 @@ -[{"path":"index.html","id":"preamble","chapter":"1 Preamble","heading":"1 Preamble","text":"working R package accessing resources web, cat facts API, scientific data source system Customer relationship management?\npackages, appropriate unit testing can make code robust.\nunit testing package interacting web resources, however, brings special challenges:\ndependence tests good internet connection, testing absence authentication secrets, etc.\ntests fail due resources slow, development CRAN, means time loss everyone involved (slower development, messages CRAN).\nAlthough packages accessing remote resources well tested, lack resources around best practices.book meant free, central reference developers R packages accessing web resources, help faster robust development.\naim develop useful guide go great recent tools vcr, webmockr, httptest, httptest2 webfakes.expect know package development basics, git.Note related previous versions: book intended detailed guide using particular suite packages HTTP mocking testing R code /packages, namely maintained Scott Chamberlain (crul, webmockr, vcr), scope extended generalize explanation concepts similar packages.can also read PDF version epub version book.Thanks contributors book:\nAlex Whan,\nAurèle,\nChristophe Dervieux,\nDaniel Possenriede,\nHugo Gruson,\nJon Harmon,\nLluís Revilla Sancho,\nXavier .\nProject funded rOpenSci (Scott\nChamberlain’s work) & R\nConsortium (Maëlle Salmon’s work).\n","code":""},{"path":"http-in-r-101.html","id":"http-in-r-101","chapter":"2 HTTP in R 101","heading":"2 HTTP in R 101","text":"","code":""},{"path":"http-in-r-101.html","id":"what-is-http","chapter":"2 HTTP in R 101","heading":"2.1 What is HTTP?","text":"HTTP means HyperText Transport Protocol, probably just looking translation abbreviation.\nHTTP way exchange information remote server.\npackage, information going back forth R session internet, using sort HTTP tooling.\npackage making requests receives responses.","code":""},{"path":"http-in-r-101.html","id":"http-requests","chapter":"2 HTTP in R 101","heading":"2.1.1 HTTP requests","text":"HTTP request package makes.\nmethod (fetching information via GET? sending information via POST?), different parts URL (domain, endpoint, query string), headers (containing instance secret identifiers).\ncan contain body. instance, might sending data JSON.\ncase one headers describe content.know request make package?\nHopefully interacting well documented web resource explain methods associated endpoints.","code":""},{"path":"http-in-r-101.html","id":"http-responses","chapter":"2 HTTP in R 101","heading":"2.1.2 HTTP responses","text":"HTTP response remote server provides, package parses.\nresponse status code indicating whether request succeeded, response headers, (optionally) response body.Hopefully documentation web API web resource working shows good examples responses.\ncase ’ll find experimenting different requests see response “looks like”.","code":""},{"path":"http-in-r-101.html","id":"more-resources-about-http","chapter":"2 HTTP in R 101","heading":"2.1.3 More resources about HTTP","text":"get started interacting HTTP R?","code":""},{"path":"http-in-r-101.html","id":"general-http-resources","chapter":"2 HTTP in R 101","heading":"2.1.3.1 General HTTP resources","text":"Mozilla Developer Network docs HTTP (recommended zine mentioned hereafter)(free) Julia Evans’ Zine “HTTP: Learn browser’s language!”docs web API aiming work , search engine understand words new.","code":""},{"path":"http-in-r-101.html","id":"http-with-r","chapter":"2 HTTP in R 101","heading":"2.1.3.2 HTTP with R","text":"docs R package end choosing!Digging source code another package similar things.","code":""},{"path":"http-in-r-101.html","id":"http-requests-in-r-what-package","chapter":"2 HTTP in R 101","heading":"2.2 HTTP requests in R: what package?","text":"R, interact web resources, recommended use curl; higher-level interfaces httr (pronounced hitter h-t-t-r), httr2 crul.use RCurl, actively maintained!writing package interacting web resources, probably use httr2, httr crul.httr popular oldest three packages, supports OAuth.\nhttr docs feature vignette called Best practices API packageshttr popular oldest three packages, supports OAuth.\nhttr docs feature vignette called Best practices API packageshttr2 “ground-rewrite httr provides pipeable API explicit request object solves problems felt packages wrap APIs (e.g. built-rate-limiting, retries, OAuth, secure secrets, )” might good idea adopt rather httr new package. vignette Wrapping APIs.httr2 “ground-rewrite httr provides pipeable API explicit request object solves problems felt packages wrap APIs (e.g. built-rate-limiting, retries, OAuth, secure secrets, )” might good idea adopt rather httr new package. vignette Wrapping APIs.crul support OAuth uses object-oriented interface, might like.\ncrul set clients, ways perform requests, might handy. crul also vignette API package best practices.crul support OAuth uses object-oriented interface, might like.\ncrul set clients, ways perform requests, might handy. crul also vignette API package best practices.try programmatically access status GitHub, open-source platform provided company name.\naccess information httr2 crul\ndecide try low-level curl, feel free contribute example.\ninternet enough examples httr.URL leaves doubt format data provided , JSON!Let’s first use httr2.Now, crul.Hopefully short snippets give idea syntax expect choosing one packages.Note choice package constrain HTTP testing tools can use.\nHowever, general ideas remain .\nswitch package backend , say, crul httr without changing tests, tests test many specificities internals.","code":"\ngithub_url <- \"https://kctbh9vrtdwd.statuspage.io/api/v2/status.json\"\nlibrary(\"magrittr\")\nresponse <- httr2::request(github_url) %>%\n httr2::req_perform()\n\n# Check the response status\nhttr2::resp_status(response)## [1] 200\n# Or in a package you'd write\nhttr2::resp_check_status(response)\n\n# Parse the content\nhttr2::resp_body_json(response)## $page\n## $page$id\n## [1] \"kctbh9vrtdwd\"\n## \n## $page$name\n## [1] \"GitHub\"\n## \n## $page$url\n## [1] \"https://www.githubstatus.com\"\n## \n## $page$time_zone\n## [1] \"Etc/UTC\"\n## \n## $page$updated_at\n## [1] \"2024-02-02T07:44:51.526Z\"\n## \n## \n## $status\n## $status$indicator\n## [1] \"none\"\n## \n## $status$description\n## [1] \"All Systems Operational\"\n# In case you wonder, the format was obtained from a header\nhttr2::resp_header(response, \"content-type\")## [1] \"application/json; charset=utf-8\"\n# Create a client and get a response\nclient <- crul::HttpClient$new(github_url)\nresponse <- client$get()\n\n# Check the response status\nresponse$status_http()## \n## Message: OK\n## Explanation: Request fulfilled, document follows\n# Or in a package you'd write\nresponse$raise_for_status()\n\n# Parse the content\nresponse$parse()## No encoding supplied: defaulting to UTF-8.## [1] \"{\\\"page\\\":{\\\"id\\\":\\\"kctbh9vrtdwd\\\",\\\"name\\\":\\\"GitHub\\\",\\\"url\\\":\\\"https://www.githubstatus.com\\\",\\\"time_zone\\\":\\\"Etc/UTC\\\",\\\"updated_at\\\":\\\"2024-02-02T07:44:51.526Z\\\"},\\\"status\\\":{\\\"indicator\\\":\\\"none\\\",\\\"description\\\":\\\"All Systems Operational\\\"}}\"\njsonlite::fromJSON(response$parse())## No encoding supplied: defaulting to UTF-8.## $page\n## $page$id\n## [1] \"kctbh9vrtdwd\"\n## \n## $page$name\n## [1] \"GitHub\"\n## \n## $page$url\n## [1] \"https://www.githubstatus.com\"\n## \n## $page$time_zone\n## [1] \"Etc/UTC\"\n## \n## $page$updated_at\n## [1] \"2024-02-02T07:44:51.526Z\"\n## \n## \n## $status\n## $status$indicator\n## [1] \"none\"\n## \n## $status$description\n## [1] \"All Systems Operational\""},{"path":"graceful.html","id":"graceful","chapter":"3 Graceful HTTP R packages","heading":"3 Graceful HTTP R packages","text":"Based previous chapter, package interacting web resource dependency curl, httr, httr2 crul. hopefully read docs dependency chose, including, case httr, httr2 crul, vignette best practices HTTP packages. Now, chapter want give tips aimed making HTTP R package graceful, part ’ll learn book!write graceful HTTP R package? First , graceful nice adjective. 💃🕺Second, graceful adjective used CRAN repository policy “Packages use Internet resources fail gracefully informative message resource available changed (give check warning error).” Therefore, let’s review make R package graceful day forward, success failure.","code":""},{"path":"graceful.html","id":"choose-the-http-resource-wisely","chapter":"3 Graceful HTTP R packages","heading":"3.1 Choose the HTTP resource wisely","text":"First , life life package’s users easier web service ’re wrapping well maintained well documented. choice, try rely fragile web service. Moreover, can, try communicate API providers (telling package; reporting feature requests bug reports preferred way).","code":""},{"path":"graceful.html","id":"user-facing-grace-how-your-package-actually-works","chapter":"3 Graceful HTTP R packages","heading":"3.2 User-facing grace (how your package actually works)","text":"can, request API every time user asks something; cache data instead. API call, API call failure! 😉 See R-hub blog post “Caching results functions R package”. remember answers across sessions, see approaches presented R-hub blog post “Persistent config data R packages”. Caching behavior well documented users, probably expiration time caches ’s based often data updated remote service.can, request API every time user asks something; cache data instead. API call, API call failure! 😉 See R-hub blog post “Caching results functions R package”. remember answers across sessions, see approaches presented R-hub blog post “Persistent config data R packages”. Caching behavior well documented users, probably expiration time caches ’s based often data updated remote service.Try send correct requests knowing API expects validating user inputs; correct rate.Try send correct requests knowing API expects validating user inputs; correct rate.instance, don’t even try interacting web API requiring authentication user provide authentication information.limiting rate (sending many requests), automatically wait. API docs allow define ideal maximal rate, set request rate advance using ratelimitr package (, httr2, httr2::req_throttle()).’s status API (separate API indicating whether web resource ), use . tells API , stop() (rlang::abort()) informative error message.’s status API (separate API indicating whether web resource ), use . tells API , stop() (rlang::abort()) informative error message.API indicates error, depending actual error,\nserver seems issues, re-try exponential back-. httr2 httr2::req_retry().\nOtherwise, transform error useful error.\nused retry nothing sent maximal number retries, show informative error message.\nAPI indicates error, depending actual error,server seems issues, re-try exponential back-. httr2 httr2::req_retry().server seems issues, re-try exponential back-. httr2 httr2::req_retry().Otherwise, transform error useful error.Otherwise, transform error useful error.used retry nothing sent maximal number retries, show informative error message.used retry nothing sent maximal number retries, show informative error message.aspects user care . Now, might problematic package’s fate CRAN automatic checks happen submission regularly.","code":""},{"path":"graceful.html","id":"graceful-vignettes-and-examples","chapter":"3 Graceful HTTP R packages","heading":"3.3 Graceful vignettes and examples","text":"Pre-compute vignettes way. Don’t use tests; showcase. course system prevent going stale, maybe even simple reminders (potentially unexported release_questions() function). Don’t let vignettes run system failure bad consequences.Don’t run examples CRAN. Now, first submission, CRAN maintainers might complain example. case, might want add minimal example, e.g.two precautions ensure CRAN checks won’t end WARNINGs, e.g. example failed API .","code":"\nif (crul::ok(\"some-url\")) {\n my_fun() # some eg that uses some-url\n}"},{"path":"graceful.html","id":"graceful-code","chapter":"3 Graceful HTTP R packages","heading":"3.4 Graceful code","text":"simplifying life contributors, make sure re-use code package e.g. defining helper functions making requests, handling responses etc.\nmake easier support interactions parts web API.\nWriting DRY (don’t repeat ) code means less lines code test, less API calls make fake!Also, export function à la gh::gh(), ’ll help users call endpoint web API even haven’t written high-level helper yet.","code":""},{"path":"graceful.html","id":"graceful-tests","chapter":"3 Graceful HTTP R packages","heading":"3.5 Graceful tests","text":"’re getting closer actual topic book!Read rest book! tests ideally run without needing actual internet connection API . tests need interact API skipped CRAN. testthat::skip_on_cran() (skip_if_offline() skips test run offline CRAN) ensure .test “success” behavior! Test behavior package case API errors, shall also covered later book.","code":""},{"path":"graceful.html","id":"conclusion","chapter":"3 Graceful HTTP R packages","heading":"3.6 Conclusion","text":"summary, graceful HTTP package, make current best practice user interface; escape examples vignettes CRAN; make tests independent actual HTTP requests. forget CRAN’s “graceful failure” policy mostly ensuring clean R CMD check result CRAN platforms (0 ERROR, 0 WARNING, 0 NOTE) even web service ’re wrapping hiccups.","code":""},{"path":"pkgs-testing-chapter.html","id":"pkgs-testing-chapter","chapter":"4 Packages for HTTP testing","heading":"4 Packages for HTTP testing","text":"brief presentation packages ’ll “meet” later book!","code":""},{"path":"pkgs-testing-chapter.html","id":"why-do-we-need-special-packages-for-http-testing","chapter":"4 Packages for HTTP testing","heading":"4.1 Why do we need special packages for HTTP testing?","text":"Packages HTTP testing useful challenges HTTP testing.\nPackages HTTP testing help solve challenges, rather letting solve homegrown solutions (can still choose , course).challenges HTTP testing?tests depend internet connection ideal.tests depend secrets authentication hand ideal.tests situations hard trigger (e.g. failure remote server) tricky.","code":""},{"path":"pkgs-testing-chapter.html","id":"webmockr","chapter":"4 Packages for HTTP testing","heading":"4.2 webmockr","text":"webmockr, maintained Scott Chamberlain, R package help “mock” HTTP requests. “mock” mean? Mock refers fact ’re faking response. works:“stub” request. , set rules HTTP request ’d like respond fake response. E.g. rule might method, URL.also can set rules fake response ’d like respond , anything (nothing, give NULL).make HTTP requests, match stub .e. set rules return requested returned.webmockr use, real HTTP interactions allowed. Therefore need stub possible HTTP requests happening via code. ’ll get error messages HTTP requests covered stub.recording interactions disk , just mocked responses given user specifies R session.webmockr works crul package httr package.webmockr quite low-level first tool ’ll use directly day--day HTTP testing.\nmay never use directly use vcr ’s one foundations.webmockr inspired Ruby webmock gem.","code":""},{"path":"pkgs-testing-chapter.html","id":"what-vcr","chapter":"4 Packages for HTTP testing","heading":"4.3 What is vcr?","text":"short version vcr, maintained Scott Chamberlain, helps stub HTTP requests don’t repeat HTTP requests, mostly unit tests.\nuses power webmockr, higher level interface.using vcr tests, first time run test, API response stored YAML JSON file.\nsubsequent runs test use local file instead really calling API.\nTherefore tests work independently internet connection.vcr inspired Ruby vcr gem.vcr works packages using httr crul.Direct link {vcr} (& {webmockr}) demo","code":""},{"path":"pkgs-testing-chapter.html","id":"what-is-httptest","chapter":"4 Packages for HTTP testing","heading":"4.4 What is httptest?","text":"httptest, maintained Neal Richardson, uses mocked API responses (like vcr).\n“enables one test logic R sides API package without requiring access remote service.”Contrary vcr, httptest also lets define mock files hand (copying API docs, dumbing real responses), whereas vcr mock files come recording real interactions (although can choose edit {vcr} mock files recording).httptest works packages using httr.Direct link {httptest} demoThe differences similarities httptest vcr become clearer chapters provide whole games .","code":""},{"path":"pkgs-testing-chapter.html","id":"what-is-httptest2","chapter":"4 Packages for HTTP testing","heading":"4.5 What is httptest2?","text":"httptest2, maintained Neal Richardson, like httptest, httr2.Direct link {httptest2} demoWith vcr, httptest httptest2 tests use sort fake API responses.vcr called fixtures cassettes.\nhttptest httptest2 called mock files.","code":""},{"path":"pkgs-testing-chapter.html","id":"what-is-webfakes","chapter":"4 Packages for HTTP testing","heading":"4.6 What is webfakes?","text":"webfakes, maintained Gábor Csárdi, provides alternative (complementary?) tool HTTP testing.\nlet fake whole web service, potentially outputting responses mock files ’ll created.\nhelp recording fake responses.\nruns fake web service, can even interact said web service browser curl command line.webfakes works packages using HTTP package (.e. works curl, crul, httr, httr2).Direct link {webfakes} demo","code":""},{"path":"pkgs-testing-chapter.html","id":"testthat","chapter":"4 Packages for HTTP testing","heading":"4.7 testthat","text":"testthat, maintained Hadley Wickham, package specifically HTTP testing; package general-purpose unit testing R packages.\nbook assume use, popularity.use alternative like tinytest,httptest won’t work ’s specifically designed complement testthat;httptest won’t work ’s specifically designed complement testthat;vcr might work;vcr might work;webfakes can work.webfakes can work.","code":""},{"path":"pkgs-testing-chapter.html","id":"conclusion-1","chapter":"4 Packages for HTTP testing","heading":"4.8 Conclusion","text":"Now idea tools can use HTTP testing, ’ll now create minimal package amend three versions tested withvcr webmockr;httptest;httptest2;webfakes.minimal package use httr (except httptest2, ’ll use httr2). However, help understand concepts even end using crul curl.1","code":""},{"path":"introduction.html","id":"introduction","chapter":"5 Introduction","heading":"5 Introduction","text":"Similar Whole Game chapter R packages book Hadley Wickham Jenny Bryan, shall go add HTTP tests minimal package.\nHowever, three times present alternative approaches: vcr, httptest, webfakes.\nexercise, shall compare approaches: compare packages involve mocking .e. vcr vs. httptest; three HTTP packages last chapter.\nnext section present single topics “deal authentication” details.","code":""},{"path":"introduction.html","id":"our-example-packages","chapter":"5 Introduction","heading":"5.1 Our example packages","text":"minimal packages, exemplighratia exemplighratia2, access GitHub status API one endpoint GitHub V3 REST API.\nnamed Latin phrase exempli gratia means “instance”, H GH.\nreally need interact GitHub V3 API, recommend gh package.\nalso recommend looking source gh package, docs GitHub V3 API, particular authentication.example packages call web APIs tools concepts applicable packages wrapping web resource, even poorly documented ones.2GitHub V3 API works without authentication , lower rate.\nsake example package requiring authentication shall assume API usable without authentication.\nAuthentication , , setting token HTTP header (quite simple, compared e.g. OAuth).GitHub Status API, contrary, necessitate authentication .shall create two functions, one works without authentication, one works authentication.create packages?\nobviously free use favorite workflow tools, share workflow using usethis package.followed usethis setup article.","code":""},{"path":"introduction.html","id":"exemplighratia2-httr2","chapter":"5 Introduction","heading":"5.1.1 exemplighratia2 (httr2)","text":"ranusethis::create_package(\"path//folder/exemplighratia2\") create open package project;usethis::use_mit_license() add MIT license;usethis::use_package(\"httr2\") add dependency httr2;usethis::use_package(\"purrr\") add dependency purrr;use_r(\"api-status.R\") add first function whose code written ;use_test(\"api-status\") (using testthat latest edition setting Config/testthat/edition: 3 DESCRIPTION) add simple test whose code .use_r(\"organizations.R\") add second function. Note ideal version function sort callback retry, call gh_api_status() function (maybe httr2::req_retry()’s is_transient argument).use_test(\"organizations\") add simple test.","code":"\nstatus_url <- function() {\n \"https://kctbh9vrtdwd.statuspage.io/api/v2/components.json\"\n}\n\n#' GitHub APIs status\n#'\n#' @description Get the status of requests to GitHub APIs\n#'\n#' @importFrom magrittr `%>%`\n#'\n#' @return A character vector, one of \"operational\", \"degraded_performance\",\n#' \"partial_outage\", or \"major_outage.\"\n#'\n#' @details See details in https://www.githubstatus.com/api#components.\n#' @export\n#'\n#' @examples\n#' \\dontrun{\n#' gh_api_status()\n#' }\ngh_api_status <- function() {\n response <- status_url() %>%\n httr2::request() %>%\n httr2::req_perform()\n\n # Check status\n httr2::resp_check_status(response)\n\n # Parse the content\n content <- httr2::resp_body_json(response)\n\n # Extract the part about the API status\n components <- content$components\n api_status <- components[purrr::map_chr(components, \"name\") == \"API Requests\"][[1]]\n\n # Return status\n api_status$status\n\n}\ntest_that(\"gh_api_status() works\", {\n testthat::expect_type(gh_api_status(), \"character\")\n})\ngh_v3_url <- function() {\n \"https://api.github.com/\"\n}\n\n#' GitHub organizations\n#'\n#' @description Get logins of GitHub organizations.\n#'\n#' @param since The integer ID of the last organization that you've seen.\n#'\n#' @return A character vector of at most 30 elements.\n#' @export\n#'\n#' @details Refer to https://developer.github.com/v3/orgs/#list-organizations\n#'\n#' @examples\n#' \\dontrun{\n#' gh_organizations(since = 42)\n#' }\ngh_organizations <- function(since = 1) {\n\n token <- Sys.getenv(\"GITHUB_PAT\")\n\n if (!nchar(token)) {\n stop(\"No token provided! Set up the GITHUB_PAT environment variable please.\")\n }\n\n response <- httr2::request(gh_v3_url()) %>%\n httr2::req_url_path_append(\"organizations\") %>%\n httr2::req_url_query(since = since) %>%\n httr2::req_headers(\"Authorization\" = paste(\"token\", token)) %>%\n httr2::req_retry(max_tries = 3, max_seconds = 120) %>%\n httr2::req_perform()\n\n httr2::resp_check_status(response)\n\n content <- httr2::resp_body_json(response)\n\n purrr::map_chr(content, \"login\")\n\n}\ntest_that(\"gh_organizations works\", {\n testthat::expect_type(gh_organizations(), \"character\")\n})"},{"path":"introduction.html","id":"exemplighratia-httr","chapter":"5 Introduction","heading":"5.1.2 exemplighratia (httr)","text":"ranusethis::create_package(\"path//folder/exemplighratia\") create open package project;usethis::use_mit_license() add MIT license;usethis::use_package(\"httr\") add dependency httr;usethis::use_package(\"purrr\") add dependency purrr;usethis::use_r(\"api-status.R\") add first function whose code written ;use_test(\"api-status\") (using testthat latest edition setting Config/testthat/edition: 3 DESCRIPTION) add simple test whose code .usethis::use_r(\"organizations.R\") add second function. Note ideal version function sort callback retry, call gh_api_status() function (seems easier implement crul’s retry method).use_test(\"organizations\") add simple test.","code":"\nstatus_url <- function() {\n \"https://kctbh9vrtdwd.statuspage.io/api/v2/components.json\"\n}\n\n#' GitHub APIs status\n#'\n#' @description Get the status of requests to GitHub APIs\n#'\n#' @return A character vector, one of \"operational\", \"degraded_performance\",\n#' \"partial_outage\", or \"major_outage.\"\n#'\n#' @details See details in https://www.githubstatus.com/api#components.\n#' @export\n#'\n#' @examples\n#' \\dontrun{\n#' gh_api_status()\n#' }\ngh_api_status <- function() {\n response <- httr::GET(status_url())\n\n # Check status\n httr::stop_for_status(response)\n\n # Parse the content\n content <- httr::content(response)\n\n # Extract the part about the API status\n components <- content$components\n api_status <- components[purrr::map_chr(components, \"name\") == \"API Requests\"][[1]]\n\n # Return status\n api_status$status\n\n}\ntest_that(\"gh_api_status() works\", {\n testthat::expect_type(gh_api_status(), \"character\")\n})\ngh_v3_url <- function() {\n \"https://api.github.com/\"\n}\n\n#' GitHub organizations\n#'\n#' @description Get logins of GitHub organizations.\n#'\n#' @param since The integer ID of the last organization that you've seen.\n#'\n#' @return A character vector of at most 30 elements.\n#' @export\n#'\n#' @details Refer to https://developer.github.com/v3/orgs/#list-organizations\n#'\n#' @examples\n#' \\dontrun{\n#' gh_organizations(since = 42)\n#' }\ngh_organizations <- function(since = 1) {\n url <- httr::modify_url(\n gh_v3_url(),\n path = \"organizations\",\n query = list(since = since)\n )\n\n token <- Sys.getenv(\"GITHUB_PAT\")\n\n if (!nchar(token)) {\n stop(\"No token provided! Set up the GITHUB_PAT environment variable please.\")\n }\n\n response <- httr::RETRY(\n \"GET\",\n url,\n httr::add_headers(\"Authorization\" = paste(\"token\", token))\n )\n\n httr::stop_for_status(response)\n\n content <- httr::content(response)\n\n purrr::map_chr(content, \"login\")\n\n}\ntest_that(\"gh_organizations works\", {\n testthat::expect_type(gh_organizations(), \"character\")\n})"},{"path":"introduction.html","id":"conclusion-2","chapter":"5 Introduction","heading":"5.2 Conclusion","text":"good, now package 100% test coverage passes R CMD Check (granted, tests thorough, remember minimal example).\ntry working without connection?\nfollowing chapters, ’ll add robust testing infrastructure minimal package, four times compare packages/approaches: vcr, httptest, httptest2, webfakes.","code":""},{"path":"vcr.html","id":"vcr","chapter":"6 Use vcr (& webmockr)","heading":"6 Use vcr (& webmockr)","text":"chapter aim adding HTTP testing infrastructure exemplighratia using vcr (& webmockr).Corresponding pull request exemplighratia. Feel free fork repository experiment !","code":""},{"path":"vcr.html","id":"setup","chapter":"6 Use vcr (& webmockr)","heading":"6.1 Setup","text":"working , need install vcr.First, need run vcr::use_vcr() (exemplighratia directory) effects:Adding vcr dependency DESCRIPTION, Suggests just like testthat.Creating example test file us look . useful first times setup vcr another package, might even delete without reading .Adding .gitattributes file line tests/fixtures/**/* -diff hide changes cassettes git diff. makes git diff easier deal . 3Creating setup file tests/testthat/helper-vcr.R,testthat runs tests, files whose name start “helper” always run first.\nalso loaded devtools::load_all(), vcr setup loaded developing testing interactively.\nSee table R-hub blog post “Helper code files testthat tests”.helper file created vcrloads vcr,indicates mocked responses saved (\"../fixtures\" translates, root package, tests/fixtures),checks using name twice cassettes (mock files).tweak vcr setup bit needs.want API token appear mock responses, know ’s used Authorization header requests, use filter_request_headers argument vcr::vcr_configure(). secret filtering one can use filter_response_headers filter_sensitive_data (regular expression purging, whole saved interactions).want API token appear mock responses, know ’s used Authorization header requests, use filter_request_headers argument vcr::vcr_configure(). secret filtering one can use filter_response_headers filter_sensitive_data (regular expression purging, whole saved interactions).need ensure set fake API key API token around.\n? remember well, code function gh_organizations() checks presence token.\nmock responses around, don’t need token still need fool package contexts token (e.g. continuous integration checks fork GitHub repository).need ensure set fake API key API token around.\n? remember well, code function gh_organizations() checks presence token.\nmock responses around, don’t need token still need fool package contexts token (e.g. continuous integration checks fork GitHub repository).updated setup file saved tests/testthat/helper-vcr.R.just setup, now adapting tests!","code":"\nlibrary(\"vcr\")\ninvisible(vcr::vcr_configure(\n dir = vcr::vcr_test_path(\"fixtures\")\n))\nvcr::check_cassette_names()\nlibrary(\"vcr\")\n\nvcr_dir <- vcr::vcr_test_path(\"fixtures\")\n\nif (!nzchar(Sys.getenv(\"GITHUB_PAT\"))) {\n if (dir.exists(vcr_dir)) {\n # Fake API token to fool our package\n Sys.setenv(\"GITHUB_PAT\" = \"foobar\")\n } else {\n # If there's no mock files nor API token, impossible to run tests\n stop(\"No API key nor cassettes, tests cannot be run.\",\n call. = FALSE)\n }\n}\n\ninvisible(vcr::vcr_configure(\n dir = vcr_dir,\n # Filter the request header where the token is sent, make sure you know\n # how authentication works in your case and read the Security chapter :-)\n filter_request_headers = list(Authorization = \"My bearer token is safe\")\n))"},{"path":"vcr.html","id":"actual-testing","chapter":"6 Use vcr (& webmockr)","heading":"6.2 Actual testing","text":"important function vcr::use_cassette(\"cassette-informative--unique-name\", {code-block}) tells vcr create mock file store API responses API calls occurring code block.Let’s tweak test gh_api_status, now becomesWe wrap code involving interactions API, status <- gh_api_status(), vcr::use_cassette().run test (RStudio clicking “Run test”),first time, vcr creates cassette (mock file) tests/testthat/fixtures/gh_api_status.yml stores API response.\ncontains information related requests responses, headers included.times , unless delete mock file, vcr simply uses mock files instead actually calling API.Let’s tweak test, gh_organizations().\nthings get exciting complicated, also set adding test error behavior.\ninspired us change error behavior bit slightly specific error message .e. httr::stop_for_status(response) became httr::stop_for_status(response, task = \"get data API, oops\").test file tests/testthat/test-organizations.R now:first test similar gh_api_status().\nsecond test unpack.enable use webmockr beginning webmockr::enable(). webmockr? can help mock failure scenario.explicitly write request https://api.github.com/organizations?since=1 return status 502.test error message expect_error(gh_organizations(), \"oops\").disable webmockr webmockr::disable().Instead using webmockr creating fake API eror, haverecorded normal cassette;edited replace status code.Read pros cons approach vcr vignette edit vcr cassettes?, especially don’t find webmockr approach enjoyable.Without HTTP testing infrastructure, testing behavior package case API errors difficult.Regarding secret API token, first time run test file, vcr creates cassette notice linesOur API token replaced string indicated vcr::vcr_configure(), bearer token safe.","code":"\ntest_that(\"gh_api_status() works\", {\n vcr::use_cassette(\"gh_api_status\", {\n status <- gh_api_status()\n })\n testthat::expect_type(status, \"character\")\n})http_interactions:\n- request:\n method: get\n uri: https://kctbh9vrtdwd.statuspage.io/api/v2/components.json\n body:\n encoding: ''\n string: ''\n headers:\n Accept: application/json, text/xml, application/xml, */*\n response:\n status:\n status_code: 200\n category: Success\n reason: OK\n message: 'Success: (200) OK'\n headers:\n vary: Accept,Accept-Encoding,Fastly-SSL\n cache-control: max-age=0, private, must-revalidate\n x-cache: MISS\n content-type: application/json; charset=utf-8\n content-encoding: gzip\n strict-transport-security: max-age=259200\n date: Thu, 15 Oct 2020 11:59:23 GMT\n x-request-id: d9888435-3f04-4401-be5c-b9d1bfdfa015\n x-download-options: noopen\n x-xss-protection: 1; mode=block\n x-runtime: '0.037254'\n x-permitted-cross-domain-policies: none\n access-control-allow-origin: '*'\n accept-ranges: bytes\n x-content-type-options: nosniff\n etag: W/\"gz[a479c9894f51b7db286dc31cd922e7bf]\"\n x-statuspage-skip-logging: 'true'\n x-statuspage-version: fd137a4bb14c20ce721393e5b6540ea6eebff3a3\n referrer-policy: strict-origin-when-cross-origin\n age: '0'\n body:\n encoding: UTF-8\n file: no\n string: '{\"page\":{\"id\":\"kctbh9vrtdwd\",\"name\":\"GitHub\",\"url\":\"https://www.githubstatus.com\",\"time_zone\":\"Etc/UTC\",\"updated_at\":\"2020-10-15T08:57:35.302Z\"},\"components\":[{\"id\":\"8l4ygp009s5s\",\"name\":\"Git\n Operations\",\"status\":\"operational\",\"created_at\":\"2017-01-31T20:05:05.370Z\",\"updated_at\":\"2020-09-24T02:32:00.916Z\",\"position\":1,\"description\":\"Performance\n of git clones, pulls, pushes, and associated operations\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"brv1bkgrwx7q\",\"name\":\"API\n Requests\",\"status\":\"operational\",\"created_at\":\"2017-01-31T20:01:46.621Z\",\"updated_at\":\"2020-09-30T19:00:29.476Z\",\"position\":2,\"description\":\"Requests\n for GitHub APIs\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"4230lsnqdsld\",\"name\":\"Webhooks\",\"status\":\"operational\",\"created_at\":\"2019-11-13T18:00:24.256Z\",\"updated_at\":\"2020-10-13T14:51:17.928Z\",\"position\":3,\"description\":\"Real\n time HTTP callbacks of user-generated and system events\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"0l2p9nhqnxpd\",\"name\":\"Visit\n www.githubstatus.com for more information\",\"status\":\"operational\",\"created_at\":\"2018-12-05T19:39:40.838Z\",\"updated_at\":\"2020-04-02T21:56:21.954Z\",\"position\":4,\"description\":null,\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"kr09ddfgbfsf\",\"name\":\"Issues\",\"status\":\"operational\",\"created_at\":\"2017-01-31T20:01:46.638Z\",\"updated_at\":\"2020-10-10T00:02:16.199Z\",\"position\":5,\"description\":\"Requests\n for Issues on GitHub.com\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"hhtssxt0f5v2\",\"name\":\"Pull\n Requests\",\"status\":\"operational\",\"created_at\":\"2020-09-02T15:39:06.329Z\",\"updated_at\":\"2020-10-10T00:02:49.033Z\",\"position\":6,\"description\":\"Requests\n for Pull Requests on GitHub.com\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"br0l2tvcx85d\",\"name\":\"GitHub\n Actions\",\"status\":\"operational\",\"created_at\":\"2019-11-13T18:02:19.432Z\",\"updated_at\":\"2020-10-13T20:23:36.040Z\",\"position\":7,\"description\":\"Workflows,\n Compute and Orchestration for GitHub Actions\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"st3j38cctv9l\",\"name\":\"GitHub\n Packages\",\"status\":\"operational\",\"created_at\":\"2019-11-13T18:02:40.064Z\",\"updated_at\":\"2020-09-08T15:50:32.845Z\",\"position\":8,\"description\":\"API\n requests and webhook delivery for GitHub Packages\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"vg70hn9s2tyj\",\"name\":\"GitHub\n Pages\",\"status\":\"operational\",\"created_at\":\"2017-01-31T20:04:33.923Z\",\"updated_at\":\"2020-10-10T00:02:38.220Z\",\"position\":9,\"description\":\"Frontend\n application and API servers for Pages builds\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false}]}'\n recorded_at: 2020-10-15 11:59:23 GMT\n recorded_with: vcr/0.5.4, webmockr/0.7.0\ntest_that(\"gh_organizations works\", {\n vcr::use_cassette(\"gh_organizations\", {\n orgs <- gh_organizations()\n })\n testthat::expect_type(orgs, \"character\")\n})\n\ntest_that(\"gh_organizations errors when the API doesn't behave\", {\n webmockr::enable()\n stub <- webmockr::stub_request(\"get\", \"https://api.github.com/organizations?since=1\")\n webmockr::to_return(stub, status = 502)\n expect_error(gh_organizations(), \"oops\")\n webmockr::disable()\n})\n stub <- webmockr::stub_request(\"get\", \"https://api.github.com/organizations?since=1\")\n webmockr::to_return(stub, status = 502)http_interactions:\n- request:\n method: get\n uri: https://api.github.com/organizations?since=1\n body:\n encoding: ''\n string: ''\n headers:\n Accept: application/json, text/xml, application/xml, */*\n Content-Type: ''\n Authorization: My bearer token is safe"},{"path":"vcr.html","id":"also-testing-for-real-interactions","chapter":"6 Use vcr (& webmockr)","heading":"6.3 Also testing for real interactions","text":"API responses change?\nHopefully ’d notice thanks following API news.\nHowever, sometimes web APIs change without notice.\nTherefore important run tests real web service .vcr package provides various methods turn vcr use allow real requests .e. ignoring mock files.\nSee ?vcr::lightswitch.case exemplighratia, added GitHub Actions workflow run schedule week, one build vcr turned via VCR_TURN_OFF environment variable.\nchose one build vcr turned otherwise configuration make easier assess broke case failure (builds fail, web API probably culprit).\nCompared continuous integration builds vcr turned , one build needs access GITHUB_PAT secret environment variable. Furthermore, slower.One imagine strategies:Always one continuous integration build vcr turned skipping contexts isn’t token (pull requests forks instance?);running tests vcr turned locally .","code":""},{"path":"vcr.html","id":"summary","chapter":"6 Use vcr (& webmockr)","heading":"6.4 Summary","text":"set vcr usage package exemplighratia running use_vcr() tweaking setup file protect secret API key fool package needs API token.Inside test_that() blocks, wrapped parts code vcr::use_cassette() ran tests first time generate mock files hold information API interactions.one tests, used webmockr create environment fake requests allowed. defined request gh_organizations() makes get 502 status. therefore able test error message gh_organizations() returns cases.Now, make sure works?Turn wifi, run tests . works! Turn wifi .Open .Renviron (usethis::edit_r_environ()), edit “GITHUB_PAT” “byeGITHUB_PAT”, re-start R, run tests . works! Fix “GITHUB_PAT” token .Renviron.now tests longer rely internet connection API credentials.also added continuous integration workflow build using real interactions every week, important regularly make sure package still works latest API responses.full list changes applied exemplighratia chapter, see pull request diff GitHub.get packages? Let’s try httptest next chapter!","code":""},{"path":"vcr.html","id":"ps-where-to-put-use_cassette","chapter":"6 Use vcr (& webmockr)","heading":"6.5 PS: Where to put use_cassette()","text":"put vcr::use_cassette() call?\nWell, written manual page function, ’s ways get correct line numbers failed tests one way get correct line numbers:\n’s correct?Wrapping whole testthat::test_that() call (test contains instance `skip_on_cran()``);Wrapping lines inside testthat::test_that() excluding expectations expect_blabla()’s incorrect?used solution wrapping lines containing API calls vcr::use_cassette(), choose prefer.","code":"\nvcr::use_cassette(\"thing\", {\n testthat::test_that(\"thing\", {\n lala <- get_foo()\n expect_true(lala)\n })\n})\ntestthat::test_that(\"thing\", {\n vcr::use_cassette(\"thing\", {\n lala <- get_foo()\n })\n expect_true(lala)\n})\ntestthat::test_that(\"thing\", {\n vcr::use_cassette(\"thing\", {\n lala <- get_foo()\n expect_true(lala)\n })\n})"},{"path":"httptest.html","id":"httptest","chapter":"7 Use httptest","heading":"7 Use httptest","text":"chapter aim adding HTTP testing infrastructure exemplighratia using httptest.\n, start initial state exemplighratia . Back square one!Note httptest::with_mock_dir() function available httptest version >= 4.0.0 (released CRAN 2021-02-01).Corresponding pull request exemplighratia Feel free fork repository experiment !","code":""},{"path":"httptest.html","id":"setup-1","chapter":"7 Use httptest","heading":"7.1 Setup","text":"working , need install httptest.First, need run httptest::use_httptest() effects:Adding httptest dependency DESCRIPTION, Suggests just like testthat.Creating setup file tests/testthat/setup,testthat runs tests, files whose name starts “setup” always run first.\nsetup file added httptest loads httptest.shall tweak bit fool package believing API token around contexts . Since tests use recorded responses recording, need actual API token recording, need gh_organizations() stop Sys.getenv(\"GITHUB_PAT\") returns nothing.just setup, now adapting tests!","code":"\nlibrary(httptest)\nlibrary(httptest)\n\n# for contexts where the package needs to be fooled\n# (CRAN, forks)\n# this is ok because the package will used recorded responses\n# so no need for a real secret\nif (!nzchar(Sys.getenv(\"GITHUB_PAT\"))) {\n Sys.setenv(GITHUB_PAT = \"foobar\")\n}"},{"path":"httptest.html","id":"actual-testing-1","chapter":"7 Use httptest","heading":"7.2 Actual testing","text":"key function httptest::with_mock_dir(\"dir\", {code-block}) tells httptest create mock files tests/testthat/dir store API responses API calls occurring code block.\nallowed tweak mock files hand, cases.Let’s tweak test file gh_status_api, becomesWe wrap whole test httptest::with_mock_dir().run test (RStudio clicking “Run test”),first time, httptest creates mock file tests/testthat/gh_api_status/kctbh9vrtdwd.statuspage.io/api/v2/components.json.json stores API response.\nhowever dumbed hand, toall times , httptest simply uses mock file instead actually calling API.Let’s tweak test, gh_organizations().things get exciting complicated, also set adding test error behavior.\ninspired us change error behavior bit slightly specific error message .e. httr::stop_for_status(response) became httr::stop_for_status(response, task = \"get data API, oops\").test file tests/testthat/test-organizations.R now:first test similar gh_api_status() except didn’t touch mock file time, laziness.\nsecond test unpack: get mock file corresponding error?first run test . fails error, expected. Note simplify = FALSE means mock file also contains headers response.replaced 200L 502L removed body, end simple mock file tests/testthat/gh_organizations_error/api.github.com/organizations-5377e8.RWe re-run tests. got expected error message.Without HTTP testing infrastructure, testing behavior package case API errors difficult.Regarding secret API token, since httptest doesn’t save requests, since responses don’t contain token, safe without making effort.demo used httptest::with_mock_dir() ways use httptest, e.g. using httptest::with_mock_api() require naming directory (’d still need use separate directory mocking error response).Find main httptest vignette.","code":"\nwith_mock_dir(\"gh_api_status\", {\n test_that(\"gh_api_status() works\", {\n testthat::expect_type(gh_api_status(), \"character\")\n testthat::expect_equal(gh_api_status(), \"operational\")\n })\n}){\"components\":[{\"name\":\"API Requests\",\"status\":\"operational\"}]}\nwith_mock_dir(\"gh_organizations\", {\n test_that(\"gh_organizations works\", {\n testthat::expect_type(gh_organizations(), \"character\")\n })\n})\n\nwith_mock_dir(\"gh_organizations_error\", {\n test_that(\"gh_organizations errors if the API doesn't behave\", {\n testthat::expect_error(gh_organizations())\n })\n},\nsimplify = FALSE)\nstructure(list(url = \"https://api.github.com/organizations?since=1\",\n status_code = 502L, headers = NULL), class = \"response\")"},{"path":"httptest.html","id":"httptest-real","chapter":"7 Use httptest","heading":"7.3 Also testing for real interactions","text":"API responses change?\nHopefully ’d notice thanks following API news.\nHowever, sometimes web APIs change without notice.\nTherefore important run tests real web service .vcr setup GitHub Actions workflow runs week tests real web service.\ndifference tests .\ntests custom made mock files can specific (e.g. testing actual values, whereas latest responses API different values), instead turning mock files usage, use old original tests put folder called real-tests.\ntime real-tests .Rbuildignored scheduled run, checking package replace content tests real-tests.\nalternative use testthat::test_dir() directory case failures get artifacts R CMD check (least without effort)., one imagine strategies, cases important keep checking package real web service fairly regularly.","code":""},{"path":"httptest.html","id":"summary-1","chapter":"7 Use httptest","heading":"7.4 Summary","text":"set httptest usage package exemplighratia running use_httptest() tweaking setup file fool package needs API token.wrapped test_that() httptest::with_mock_dir() ran tests first time generate mock files hold information API responses. cases modified mock files make smaller make correspond API error.Now, make sure works?Turn wifi, run tests . works! Turn wifi .Open .Renviron (usethis::edit_r_environ()), edit “GITHUB_PAT” “byeGITHUB_PAT”, re-start R, run tests . works! Fix “GITHUB_PAT” token .Renviron.now tests longer rely internet connection API credentials.also added continuous integration workflow build using real interactions every week, important regularly make sure package still works latest API responses.full list changes applied exemplighratia chapter, see pull request diff GitHub.get yet another package? ’ll try webfakes first let’s compare vcr httptest use mocking.","code":""},{"path":"mocking-pkgs-comparison.html","id":"mocking-pkgs-comparison","chapter":"8 vcr and httptest","heading":"8 vcr and httptest","text":"just followed similar processes add HTTP testing infrastructure involving mock files exemplighratiaAdding package Suggests dependency;Creating helper file particular loads package test;Tweaking tests, cases wrapping tests functions allows record API responses mock files play back said mock files; cases (httptest), creating mock files .Now, differences.\nwon’t end advocating one package particular since merits, hope help differentiate two packages.","code":""},{"path":"mocking-pkgs-comparison.html","id":"setting-up-the-infrastructure","chapter":"8 vcr and httptest","heading":"8.1 Setting up the infrastructure","text":"set HTTP testing infrastructure, one case need run vcr::use_vcr() another case need run httptest::use_httptest(). hard remember.","code":""},{"path":"mocking-pkgs-comparison.html","id":"calling-mock-files","chapter":"8 vcr and httptest","heading":"8.2 Calling mock files","text":"mentioned , vcr httptest use mock files call differently.vcr called fixtures cassettes.\nhttptest called mock files.\nNote fixtures specific cassettes mock files: cassettes mock files fixtures, anything (csv file input instance) use consistently test package fixture.","code":""},{"path":"mocking-pkgs-comparison.html","id":"naming-mock-files","chapter":"8 vcr and httptest","heading":"8.3 Naming mock files","text":"vcr use_cassette() call needs include name used create filename mock file.\nhelp ?use_cassette explains criteria naming , fact cassette names need unique.\nNow wrap whole test_that() block might just well use name similar test name, already make meaningful, right?httptest mock filepaths translated requests according several rules incorporate request method, URL, query parameters, body.\nuse with_mock_dir() need name directory mock files saved, can make meaningful.Also note vcr one file can () contain several HTTP interactions (requests responses) whereas httptest one file contains one response (filename helps matching request).","code":""},{"path":"mocking-pkgs-comparison.html","id":"matching-requests","chapter":"8 vcr and httptest","heading":"8.4 Matching requests","text":"httptest mock file name includes everything ’s potentially varying request, mock file corresponds one request .vcr, different possible configurations matching request saved interaction default can mostly expect one saved interaction corresponds one request .","code":""},{"path":"mocking-pkgs-comparison.html","id":"handling-secrets","chapter":"8 vcr and httptest","heading":"8.5 Handling secrets","text":"vcr, since everything HTTP interactions recorded, always need add sort configuration sure wipe API tokens mock files.httptest, responses saved, often, bodies.\noften, responses don’t contain secrets e.g. don’t contain API token.\nresponse contains secrets, refer httptest’s article “Redacting sensitive information”.","code":""},{"path":"mocking-pkgs-comparison.html","id":"recording-playing-back","chapter":"8 vcr and httptest","heading":"8.6 Recording, playing back","text":"using mock files testing, first need record responses mock files; want use mock files instead real HTTP interactions (’s whole point).vcr, recording vs playing back modes happen automatically depending existence cassette. write vcr::use_cassette(\"blabla\", ) ’s cassette called blabla, vcr create . Note change HTTP interactions code block, ’ll re-record cassette simple deleting running test. Note can also change way vcr behaves looking ?vcr::vcr_configure’s “Cassette Options”.httptest, lot flexibility around record mock files. httptest doesn’t assume every API mock came real request real server; maybe copy mocks directly API docs.Note nothing prevents editing vcr cassettes hand, ’ll careful re-recording mistake.httptest flexiblity comes original design principles httptest“[httptest] doesn’t assume every API mock came real request real server, designed able see modify test fixtures.\nAmong considerations:1. many cases, API responses contain way content necessary test R code around : 100 records 2 suffice, request metadata don’t care can’t meaningfully assert things , . interest minimally reproducible examples, making tests readable, often makes sense take actual API response delete lot content, even fabricate one entirely.2. ’s good keep API mock fixed know exactly . re-recorded Twitter API response , say, recent 10 tweets #rstats, specific content change every time record , tests can’t say much response without rewrite every time .3. conditions (rate limiting, server errors, e.g.) difficult test real responses, can hand-create API mock , say, 503 response status code test code handles , can confidence package respond rare event happens real API.4. Re-recording responses can make huge code diff, can blow repository size make code review harder.”Now, creating mock files hand (inventing custom scripts create ) involves elbow grease, ’s compromise.","code":""},{"path":"mocking-pkgs-comparison.html","id":"testing-for-api-errors","chapter":"8 vcr and httptest","heading":"8.7 Testing for API errors","text":"test suite probably want check things go server returns 502 , trigger response record .httptest, test API errors, need create one several fake mock file(s).\neasiest way might use httptest::with_mock_dir() create mock files expected filenames locations, can tweak.\nreading error message httptest::with_mock_ap() helps know create mock file.vcr, eitheruse webmockr showed demo. one hand ’s compact creating fake mock file, hand ’s way test ’s different vcr cassette.edit cassette hand similar testing API errors httptest. , ’d need skip test vcr , vcr real requests made. can use vcr::skip_if_vcr_off().","code":"\ntest_that(\"gh_organizations errors when the API doesn't behave\", {\n webmockr::enable()\n stub <- webmockr::stub_request(\"get\", \"https://api.github.com/organizations?since=1\")\n webmockr::to_return(stub, status = 502)\n expect_error(gh_organizations(), \"oops\")\n webmockr::disable()\n})"},{"path":"mocking-pkgs-comparison.html","id":"conclusion-3","chapter":"8 vcr and httptest","heading":"8.8 Conclusion","text":"vcr httptest similar packages use mock files allowing easier HTTP testing.\nbit different design philosophy features, might help choose one .now, make things even complex, fun, shall explore third HTTP testing package mock requests instead spins local fake web service.","code":""},{"path":"httptest2.html","id":"httptest2","chapter":"9 Use httptest2","heading":"9 Use httptest2","text":"chapter aim adding HTTP testing infrastructure exemplighratia2 using httptest2.\n, start initial state exemplighratia2 . Back square one!Corresponding pull request exemplighratia2 Feel free fork repository experiment !","code":""},{"path":"httptest2.html","id":"setup-2","chapter":"9 Use httptest2","heading":"9.1 Setup","text":"working , need install httptest2.First, need run httptest2::use_httptest2() effects:Adding httptest2 dependency DESCRIPTION, Suggests just like testthat.Creating setup file tests/testthat/setup,testthat runs tests, files whose name starts “setup” always run first.\nsetup file added httptest2 loads httptest2.shall tweak bit fool package believing API token around contexts . Since tests use recorded responses recording, need actual API token recording, need gh_organizations() stop Sys.getenv(\"GITHUB_PAT\") returns nothing.just setup, now adapting tests!","code":"\nlibrary(httptest2)\nlibrary(httptest2)\n\n# for contexts where the package needs to be fooled\n# (CRAN, forks)\n# this is ok because the package will used recorded responses\n# so no need for a real secret\nif (!nzchar(Sys.getenv(\"GITHUB_PAT\"))) {\n Sys.setenv(GITHUB_PAT = \"foobar\")\n}"},{"path":"httptest2.html","id":"actual-testing-2","chapter":"9 Use httptest2","heading":"9.2 Actual testing","text":"key function httptest2::with_mock_dir(\"dir\", {code-block}) tells httptest create mock files tests/testthat/dir store API responses API calls occurring code block.\nallowed tweak mock files hand, cases.Let’s tweak test file gh_status_api, becomesWe wrap whole test httptest2::with_mock_dir().run test (RStudio clicking “Run test”),first time, httptest2 creates mock file tests/testthat/gh_api_status/kctbh9vrtdwd.statuspage.io/api/v2/components.json.json stores API response.\nhowever dumbed hand, toall times , httptest2 simply uses mock file instead actually calling API.Let’s tweak test, gh_organizations().things get exciting complicated, also set adding test error behavior.\ninspired us change error behavior bit slightly specific error message .e. httr2::resp_check_status(response) became httr2::resp_check_status(response, info = \"Oops, try later?\").test file tests/testthat/test-organizations.R now:first test similar gh_api_status() except didn’t touch mock file time, laziness.\nsecond test unpack: get mock file corresponding error?first run test . fails error, expected. Note simplify = FALSE means mock file also contains headers response.replaced 200L 502L removed body, end simpler mock file tests/testthat/gh_organizations_error/api.github.com/organizations-5377e8.RWe re-run tests. got expected error message.Without HTTP testing infrastructure, testing behavior package case API errors difficult.Regarding secret API token, since httptest2 doesn’t save requests4, since responses don’t contain token, safe without making effort.demo used httptest2::with_mock_dir() ways use httptest2, e.g. using httptest2::with_mock_api() require naming directory (’d still need use separate directory mocking error response).Find main httptest2 vignette.","code":"\nwith_mock_dir(\"gh_api_status\", {\n test_that(\"gh_api_status() works\", {\n testthat::expect_type(gh_api_status(), \"character\")\n testthat::expect_equal(gh_api_status(), \"operational\")\n })\n}){\"components\":[{\"name\":\"API Requests\",\"status\":\"operational\"}]}\nwith_mock_dir(\"gh_organizations\", {\n test_that(\"gh_organizations works\", {\n testthat::expect_type(gh_organizations(), \"character\")\n })\n})\n\nwith_mock_dir(\"gh_organizations_error\", {\n test_that(\"gh_organizations errors if the API doesn't behave\", {\n testthat::expect_snapshot_error(gh_organizations())\n })\n},\nsimplify = FALSE)\nstructure(list(method = \"GET\", url = \"https://api.github.com/organizations?since=1\",\n status_code = 502L, headers = structure(list(server = \"GitHub.com\",\n date = \"Thu, 17 Feb 2022 12:40:29 GMT\", `content-type` = \"application/json; charset=utf-8\",\n `cache-control` = \"private, max-age=60, s-maxage=60\",\n vary = \"Accept, Authorization, Cookie, X-GitHub-OTP\",\n etag = \"W/\\\"d56e867402a909d66653b6cb53d83286ba9a16eef993dc8f3cb64c43b66389f4\\\"\",\n `x-oauth-scopes` = \"gist, repo, user, workflow\", `x-accepted-oauth-scopes` = \"\",\n `x-github-media-type` = \"github.v3; format=json\", link = \"; rel=\\\"next\\\", ; rel=\\\"first\\\"\",\n `x-ratelimit-limit` = \"5000\", `x-ratelimit-remaining` = \"4986\",\n `x-ratelimit-reset` = \"1645104327\", `x-ratelimit-used` = \"14\",\n `x-ratelimit-resource` = \"core\", `access-control-expose-headers` = \"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset\",\n `access-control-allow-origin` = \"*\", `strict-transport-security` = \"max-age=31536000; includeSubdomains; preload\",\n `x-frame-options` = \"deny\", `x-content-type-options` = \"nosniff\",\n `x-xss-protection` = \"0\", `referrer-policy` = \"origin-when-cross-origin, strict-origin-when-cross-origin\",\n `content-security-policy` = \"default-src 'none'\", vary = \"Accept-Encoding, Accept, X-Requested-With\",\n `content-encoding` = \"gzip\", `x-github-request-id` = \"A4BA:12D5C:178438:211160:620E423C\"), class = \"httr2_headers\"),\n body = charToRaw(\"\")), class = \"httr2_response\")"},{"path":"httptest2.html","id":"httptest2-real","chapter":"9 Use httptest2","heading":"9.3 Also testing for real interactions","text":"API responses change?\nHopefully ’d notice thanks following API news.\nHowever, sometimes web APIs change without notice.\nTherefore important run tests real web service .One use strategy one demonstrated httptest .e. different test folder., one imagine strategies, cases important keep checking package real web service fairly regularly.","code":""},{"path":"httptest2.html","id":"summary-2","chapter":"9 Use httptest2","heading":"9.4 Summary","text":"set httptest2 usage package exemplighratia running use_httptest2() tweaking setup file fool package needs API token.wrapped test_that() httptest2::with_mock_dir() ran tests first time generate mock files hold information API responses. cases modified mock files make smaller make correspond API error.Now, make sure works?Turn wifi, run tests . works! Turn wifi .Open .Renviron (usethis::edit_r_environ()), edit “GITHUB_PAT” “byeGITHUB_PAT”, re-start R, run tests . works! Fix “GITHUB_PAT” token .Renviron.now tests longer rely internet connection API credentials.also added continuous integration workflow build using real interactions every week, important regularly make sure package still works latest API responses.full list changes applied exemplighratia chapter, see pull request diff GitHub.get yet another package? ’ll try webfakes.","code":""},{"path":"webfakes.html","id":"webfakes","chapter":"10 Use webfakes","heading":"10 Use webfakes","text":"chapter aim adding HTTP testing infrastructure exemplighratia using webfakes.","code":""},{"path":"webfakes.html","id":"setup-3","chapter":"10 Use webfakes","heading":"10.1 Setup","text":"working , need install webfakes, install.packages(\"webfakes\")., need add webfakes Suggests dependency package, potentially via running usethis::use_package(\"webfakes\", type = \"Suggests\").Last least, create setup file tests/testthat/setup.R.\ntestthat runs tests, files whose name starts “setup” always run first.\nneed ensure set fake API key API token around.\n? remember well, code function gh_organizations() checks presence token.\nusing fake web service, obviously don’t really need token still need fool package contexts token (e.g. continuous integration checks fork GitHub repository).setup file also load webfakes, demo namespace webfakes functions instead.","code":"\nif(!nzchar(Sys.getenv(\"REAL_REQUESTS\"))) {\n Sys.setenv(\"GITHUB_PAT\" = \"foobar\")\n}"},{"path":"webfakes.html","id":"actual-testing-3","chapter":"10 Use webfakes","heading":"10.2 Actual testing","text":"webfakes spinning local fake web services want package interact instead real APIs.\nTherefore, first need amend code functions returning URLs services able change via environment variable.\nbecome:andHaving two switches crucial., let’s tweak test gh_api_status().’s happening ?’re asking requests real service made (Sys.getenv(\"REAL_REQUESTS\")), prepare new app via webfakes::new_app(). ’s simple one, returns, GET requests, list corresponding ’re used getting status API, except ) ’s much smaller b) “operational” status hard-coded.create local app process via webfakes::local_app_process(, start = TRUE). start right away thanks start=TRUE chosen start later via calling e.g. web$url() (see ?webfakes::local_app_process); importantly stopped automatically test. mess made!set EXEMPLIGHRATIA_GITHUB_STATUS_URL variable URL local app process. connects code fake web service.might seem like lot overhead code butIt means real requests made ultimate goal.get used .can write helper code testthat helper file repeat test files; even app shared test files depending package.Now, let’s add test error behavior.\ninspired us change error behavior bit slightly specific error message .e. httr::stop_for_status(response) became httr::stop_for_status(response, task = \"get API status, ouch!\").’s similar process earlier test:setting new app;return something chose, case 502 status;launching local app process;connecting code via setting EXEMPLIGHRATIA_GITHUB_STATUS_URL environment variable URL fake service;test.Last least let’s convert test gh_organizations(),wecreate new app;returned something chose GET request /organizations endpoint. case, return content JSON file created tests/testthat/responses/organizations.json copy-pasting real response API;launch local app process;set URL EXEMPLIGHRATIA_GITHUB_API_URL environment variable;test.","code":"\nstatus_url <- function() {\n\n env_url <- Sys.getenv(\"EXEMPLIGHRATIA_GITHUB_STATUS_URL\")\n\n if (nzchar(env_url)) {\n return(env_url)\n }\n\n \"https://kctbh9vrtdwd.statuspage.io/api/v2/components.json\"\n}\ngh_v3_url <- function() {\n\n api_url <- Sys.getenv(\"EXEMPLIGHRATIA_GITHUB_API_URL\")\n\n if (nzchar(api_url)) {\n return(api_url)\n }\n\n \"https://api.github.com/\"\n}\ntest_that(\"gh_api_status() works\", {\n if (!nzchar(Sys.getenv(\"REAL_REQUESTS\"))) {\n app <- webfakes::new_app()\n app$get(\"/\", function(req, res) {\n res$send_json(\n list( components =\n list(\n list(\n name = \"API Requests\",\n status = \"operational\"\n )\n )\n ),\n auto_unbox = TRUE\n )\n })\n web <- webfakes::local_app_process(app, start = TRUE)\n web$local_env(list(EXEMPLIGHRATIA_GITHUB_STATUS_URL = \"{url}\"))\n }\n\n testthat::expect_type(gh_api_status(), \"character\")\n})\ntest_that(\"gh_api_status() errors when the API does not behave\", {\n app <- webfakes::new_app()\n app$get(\"/\", function(req, res) {\n res$send_status(502L)\n })\n web <- webfakes::local_app_process(app, start = TRUE)\n web$local_env(list(EXEMPLIGHRATIA_GITHUB_STATUS_URL = \"{url}\"))\n testthat::expect_error(gh_api_status(), \"ouch\")\n})\ntest_that(\"gh_organizations works\", {\n\n if (!nzchar(Sys.getenv(\"REAL_REQUESTS\"))) {\n app <- webfakes::new_app()\n app$get(\"/organizations\", function(req, res) {\n res$send_json(\n jsonlite::read_json(\n testthat::test_path(\n file.path(\"responses\", \"organizations.json\")\n )\n ),\n auto_unbox = TRUE\n )\n })\n web <- webfakes::local_app_process(app, start = TRUE)\n web$local_env(list(EXEMPLIGHRATIA_GITHUB_API_URL = \"{url}\"))\n }\n\n testthat::expect_type(gh_organizations(), \"character\")\n})"},{"path":"webfakes.html","id":"also-testing-for-real-interactions-1","chapter":"10 Use webfakes","heading":"10.3 Also testing for real interactions","text":"API responses change?\nHopefully ’d notice thanks following API news.\nHowever, sometimes web APIs change without notice.\nTherefore important run tests real web service .tests used conditionbefore launching app using URL URL service.\ntests generic enough, can add CI build environment variable REAL_REQUESTS set true.\ngeneric enough, can use theapproach exemplified chapter httptest.set folder real-tests tests interacting real web service;add Rbuildignore;CI build, delete tests/testthat replace real-tests, running R CMD check.","code":"if (!nzchar(Sys.getenv(\"REAL_REQUESTS\"))) {"},{"path":"webfakes.html","id":"summary-3","chapter":"10 Use webfakes","heading":"10.4 Summary","text":"set webfakes usage package exemplighratia adding dependency webfakes adding setup file fool package needs API token.created launched fake apps test files.Now, make sure works?Turn wifi, run tests . works! Turn wifi .Open .Renviron (usethis::edit_r_environ()), edit “GITHUB_PAT” “byeGITHUB_PAT”, re-start R, run tests . works! Fix “GITHUB_PAT” token .Renviron.now tests longer rely internet connection API credentials.full list changes applied exemplighratia chapter, see pull request diff GitHub.next chapter, shall compare three approaches HTTP testing ’ve demo-ed.","code":""},{"path":"pkgs-comparison.html","id":"pkgs-comparison","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11 vcr (& webmockr), httptest, webfakes","text":"’re now nice stage made demo usage HTTP testing packages, exemplighratia package.\ncourse, choice strategy demo bit subjective, hope showed best tool.first message ’s important us: ’re learning HTTP testing using branch package sounds daunting, create minimal package playing!","code":""},{"path":"pkgs-comparison.html","id":"what-http-client-can-you-use-curl-httr-crul","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.1 What HTTP client can you use (curl, httr, crul)","text":"httptest works httr (popular HTTP R client);vcr (& webmockr) works httr crul (two “high-level” HTTP R clients);webfakes works R HTTP client, even base R wish.","code":""},{"path":"pkgs-comparison.html","id":"sustainability-of-the-packages","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.2 Sustainability of the packages","text":"packages (vcr, webmockr, httptest, webfakes) actively maintained.\nwriting book, issues pull requests tackled rather quickly, always nice way.","code":""},{"path":"pkgs-comparison.html","id":"test-writing-experience","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.3 Test writing experience","text":"cases HTTP tests, .e. tests work independently internet connection, depends onsetup, mainly adding dependency HTTP testing packages DESCRIPTION, setup helper file;providing responses API.difference packages, test writing experience depends can provide responses API, real ones fake ones.vcr httptest tests testing normal behavior, set (helper function), testing just function away (vcr::use_cassette(), httptest::with_mock_dir(), httptest::with_mock_api()).\nRecording happens automatically first run tests.\nmight also provide fake recorded response dumb existing ones.\ncreating API errors, API sequence responses (e.g. 502 200), end either using webmockr, amending mock files, see vcr httptest related docs.5With webfakes need create app.\none per test, per test file whole test suite.\nmight seem like overhead code able share app different tests reduces effort.\ncan test API sequence responses (e.g. 502 200) following -.\none thing ’s supported webfakes yet smooth workflow recording responses, time writing might need write workflow recording responses.general setup&test writing might easier packages mocking (vcr httptest) might able replicate complex behavior webfakes (OAuth dance).","code":""},{"path":"pkgs-comparison.html","id":"the-special-case-of-secrets","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.3.1 The special case of secrets","text":"webfakes authentication needed point, less chance exposing secret.httptest body responses saved, unless contains secrets, effort needed. need redact mock files, see corresponding vignette.vcr HTTP interactions, including request URLs headers, saved disk, often use filter_sensitive_data, filter_request_header /filter_response_header arguments vcr::vcr_configure().","code":""},{"path":"pkgs-comparison.html","id":"how-about-making-real-requests","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.3.2 How about making real requests","text":"three cases, switching back real requests might environment variable away (turning vcr , setting URL real web service URL connected instead webfakes fake web service).\nHowever, tests using fixed/fake responses / fake web service might work real requests can’t trigger API error, might test specific values tests using mock files whereas API returns something different every day.\nTherefore, ’s challenge common three packages, might need choose distinct tests integration tests/contract tests.\nSee also chapter making real requests.","code":""},{"path":"pkgs-comparison.html","id":"test-debugging-experience","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.4 Test debugging experience","text":"Sadly sometimes one needs run code tests interactive session, either debug tests making code change, learn write HTTP tests.webfakes, debugging works way: load helper test file wherethe app created,environment variable connecting package code fake web service changed.run code. debug webfakes apps, follow guidance.vcr, refer debugging vignette: ’ll load helper file source setup file making sure paths use work tests/testthat/ package root (see ?vcr::vcr_test_path), use vcr::inject_cassette(); don’t forget run vcr::eject_cassette() afterwards.\nwebmockr debugging quite natural, run code ’s test, particular webmockr::enable() webmockr::disable().httptest, process similar vcr except key functions areuse_mock_api().mockPaths.","code":""},{"path":"pkgs-comparison.html","id":"conclusion-4","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.5 Conclusion","text":"chapter compared three R packages make HTTP testing easier.\nstill unsure one pick, first try packages without commitment, branches , choose one commit lock-.“Every piece code written given language framework step away language, five minutes ’ll spend migrating something else. ’s fine. just decide ’re willing locked .(…)Code days becomes obsolete quickly, regardless ’s chosen. time needs change, time latest framework obsolete, code rotten anyway(…)dangerous feature articles examining cloud lock-introduce kind paralysis teams result applications never completely fleshed finished.”Vicki Boykis, “Commit lock-”.","code":""},{"path":"real-requests-chapter.html","id":"real-requests-chapter","chapter":"12 Making real requests","heading":"12 Making real requests","text":"touched upon Whole Games section, ’s good tests real API.\nIndeed, web resource can change.","code":""},{"path":"real-requests-chapter.html","id":"what-can-change","chapter":"12 Making real requests","heading":"12.1 What can change?","text":"can happen?API introducing rate-limiting;web resource disappearing;etc.","code":""},{"path":"real-requests-chapter.html","id":"how-to-make-real-requests","chapter":"12 Making real requests","heading":"12.2 How to make real requests","text":"Maybe can just run tests without using mock files.vcr, behavior one environment variable away (namely, VCR_TURN_OFF).httptest httptest2 can create kind behavior.webfakes can also create behavior.Now means assuming tests work real requests.tests won’t work real requests (say fixture mimicking API error, specific answer today given date) can skip tests mocking/faking web service . vcr means using vcr::skip_if_vcr_off(); httptest webfakes ’d create custom skipper.tests won’t work real requests, creating different folder tests making real requests makes sense. might less unit-y view tests integration/contract tests. Maybe use testthat’s snapshot testing (view ’s different API).","code":""},{"path":"real-requests-chapter.html","id":"when-to-make-real-requests","chapter":"12 Making real requests","heading":"12.2.1 When to make real requests?","text":"Locally, might want make real requests , particular CRAN release.continuous integration learn trigger workflows configure build matrices e.g.one build build matrix using real requests commit (might much, see next section);one scheduled workflow day week using real requests.","code":""},{"path":"real-requests-chapter.html","id":"why-not-make-only-or-too-many-real-requests","chapter":"12 Making real requests","heading":"12.3 Why not make only or too many real requests?","text":"reasons can’t make real requests tests reasons reading book:slower;can’t test API errors;etc.Now matter setup don’t want make many real requests can bad web resource bad (e.g. using allowed requests!).\nRegarding allowed requests, possible however increase requesting sort special development account thing exists API working .","code":""},{"path":"real-requests-chapter.html","id":"a-complement-to-real-requests-api-news","chapter":"12 Making real requests","heading":"12.4 A complement to real requests: API news!","text":"Running real requests important notice something changes API (expected requests, responses).\nNow, can also follow news web resource using case something place.Subscribe API newsletter ’s one;Read API changelogs public;particular, API developed GitHub/GitLab/etc. watch repo subscribe releases, might automatically get notified.","code":""},{"path":"cran-preparedness.html","id":"cran-preparedness","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13 CRAN- (and Bioconductor) preparedness for your tests","text":"one right answer manage tests CRAN, except \nwant clean check result CRAN times.\nprobably applies Bioconductor .\nfollowing \ndiscussion various considerations - give enough\ninformation make educated decision.","code":""},{"path":"cran-preparedness.html","id":"running-tests-on-cran","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13.1 Running tests on CRAN?","text":"can run vcr/httptest/httptest2/webfakes enabled tests CRAN.\nCRAN okay files associated tests,\ngeneral CRAN can run tests use cassettes, mock files recorded responses CRAN.\nAnother aspect presence dependencies: make sure HTTP testing package use listed Suggests dependency DESCRIPTION!\nwebfakes might mean also listing optional dependencies DESCRIPTION.\nwebfakes, tests run CRAN assume availability given port.running HTTP tests CRAN, aware things:tests require secret environment variables R options (apart “foobar” ones used fool package using saved response),\nwon’t available CRAN. cases likely want skip \ntests testthat::skip_on_cran().tests cassettes, mock files recorded responses sensitive information ,\nprobably want cassettes internet, case\nwon’t running vcr enabled tests CRAN either. case sensitive\ninformation, might want Rbuildignore cassettes, mock files recorded responses (gitignore make package development repository private).maximal size package sources want cassettes, mock files recorded responses big. three ways limit size\nMake requests generate huge response (e.g. tweak time range);\nEdit recorded responses (even copy-paste responses API docs often short) — see vcr docs editing cassettes pros cons;\nShare cassettes / mock files / recorded responses tests.\nMake requests generate huge response (e.g. tweak time range);Edit recorded responses (even copy-paste responses API docs often short) — see vcr docs editing cassettes pros cons;Share cassettes / mock files / recorded responses tests.compress cassettes, mock files recorded responses: CRAN submissions already compressed; compressed files make git diffs hard use.","code":""},{"path":"cran-preparedness.html","id":"skipping-a-few-tests-on-cran","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13.2 Skipping a few tests on CRAN?","text":"worried problems HTTP tests CRAN can use\ntestthat::skip_on_cran() skip specific tests.\nMake sure tests run somewhere else (continuous integration) regularly!’d recommend running tests making real requests CRAN, even interacting API without authentication.","code":""},{"path":"cran-preparedness.html","id":"skipping-all-tests-on-cran","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13.3 Skipping all tests on CRAN?","text":"good continuous integration setup (several operating systems, scheduled runs, etc.) skip tests CRAN?","code":""},{"path":"cran-preparedness.html","id":"stress-test-your-package","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13.4 Stress-test your package","text":"stress-test package CRAN submission, use rhub::check_for_cran() without passing environment variable function, use WinBuilder.","code":""},{"path":"security-chapter.html","id":"security-chapter","chapter":"14 Security","heading":"14 Security","text":"developing package uses secrets (API keys, OAuth tokens) produces (OAuth tokens, sensitive data),want secrets usable , collaborators CI services, without readable anyone else;want tests checks (e.g. vignette building) use secrets turned environments secrets won’t available (CRAN, forks development repository).general attitude think :secrets (API key, OAuth2.0 access token refresh token, etc.) /exactly used (query part URL? header? header, Authentication something else?) – packages like httr httr2 might abstract complexity need really know secrets used leaked,go wrong (e.g. token ending published),prevent (save unedited token outside package, make sure printed logs present package check artefacts),fix mistakes (deactivate token check one used meantime).","code":""},{"path":"security-chapter.html","id":"managing-secrets-securely","chapter":"14 Security","heading":"14.1 Managing secrets securely","text":"","code":""},{"path":"security-chapter.html","id":"follow-best-practice-when-developing-your-package","chapter":"14 Security","heading":"14.1.1 Follow best practice when developing your package","text":"book testing security starts develop package.\nbetter protect users’ secret,might best let users pass API keys parameters. ’s best save .Renviron e.g. using keyring package. way, API keys scripts. opencage package might provide inspiration.might best let users pass API keys parameters. ’s best save .Renviron e.g. using keyring package. way, API keys scripts. opencage package might provide inspiration.API working lets pass keys either request headers query string, prefer use request headers.API working lets pass keys either request headers query string, prefer use request headers.","code":""},{"path":"security-chapter.html","id":"share-secrets-with-continuous-integration-services","chapter":"14 Security","heading":"14.1.2 Share secrets with continuous integration services","text":"need share secrets continuous integration services… real requests !\ntests using vcr, httptest, httptest2 webfakes, need fake secret, e.g. “foobar” API key – except recording cassettes mock files, something locally.GitHub repositories, storing new secret, save quotes.\n.e. secret “blabla”, field contain blabla, \"blabla\" 'blabla'.","code":"\nknitr::include_graphics(\"secret.png\")"},{"path":"security-chapter.html","id":"api-keys","chapter":"14 Security","heading":"14.1.2.1 API keys","text":"API keys, can use something like GitHub repo secrets use GitHub Actions.\nsecret accessible environment variable workflow GitHub Actions explained gargle docs need add line like","code":"env:\n PACKAGE_PASSWORD: ${{ secrets.PACKAGE_PASSWORD }}"},{"path":"security-chapter.html","id":"more-complex-objects","chapter":"14 Security","heading":"14.1.2.2 More complex objects","text":"secret OAuth token, might able re-create pieces, pieces strings can store repo secrets much like ’d API key.\nE.g. secret OAuth token, actual secrets access token refresh token.Therefore re-create using e.g. credentials argument httr::oauth2.0_token().\nre-creation using environment variables Sys.getenv(\"ACCESS_TOKEN\") Sys.getenv(\"REFRESH_TOKEN\") happen testthat helper file.","code":"env:\n ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}\n REFRESH_TOKEN: ${{ secrets.REFRESH_TOKEN }}"},{"path":"security-chapter.html","id":"secret-files","chapter":"14 Security","heading":"14.1.3 Secret files","text":"files, need use encryption store text-version encryption key/passwords GitHub repo secret use GitHub Actions.\nRead documentation continuous integration service using find secrets protected can use builds.See gargle vignette securely managing tokens.approach :Create OAuth token locally, either outside package folder, inside really want , gitignored Rbuildignored.Encrypt using e.g. user-friendly cyphr package. Save code step file e.g. inst/secrets.R need re-create token even refresh tokens expire.encrypting need sort password. want save securely text user-level .Renviron GitHub repo secrets (equivalent secret place CI services). E.g. create key via sodium_key <- sodium::keygen() get text equivalent via sodium::bin2hex(sodium_key). E.g. latter command might give e46b7faf296e3f0624e6240a6efafe3dfb17b92ae0089c7e51952934b60749f2 save .RenvironExample script creating encrypting OAuth token (Meetup API).tests setup / helper file code like .Now happens contexts MEETUPR_PWD available?\nWell tests using !\nSee chapter making real requests.","code":"MEETUPR_PWD=\"e46b7faf296e3f0624e6240a6efafe3dfb17b92ae0089c7e51952934b60749f2\"\n# thanks Jenny Bryan https://github.com/r-lib/gargle/blob/4fcf142fde43d107c6a20f905052f24859133c30/R/secret.R\n\ntoken_path <- testthat::test_path(\".meetup_token.rds\")\nuse_build_ignore(token_path)\nuse_git_ignore(token_path)\n\nmeetupr::meetup_auth(\n token = NULL,\n cache = TRUE,\n set_renv = FALSE,\n token_path = token_path\n)\n\n# sodium_key <- sodium::keygen()\n# set_renv(\"MEETUPR_PWD\" = sodium::bin2hex(sodium_key))\n# set_renv being an internal function taken from rtweet\n# that saves something to .Renviron\n\n# get key from environment variable\nkey <- cyphr::key_sodium(sodium::hex2bin(Sys.getenv(\"MEETUPR_PWD\")))\n\ncyphr::encrypt_file(\n token_path,\n key = key,\n dest = testthat::test_path(\"secret.rds\")\n)\nkey <- cyphr::key_sodium(sodium::hex2bin(Sys.getenv(\"MEETUPR_PWD\")))\n\ntemptoken <- tempfile(fileext = \".rds\")\n\ncyphr::decrypt_file(\n testthat::test_path(\"secret.rds\"),\n key = key,\n dest = temptoken\n)"},{"path":"security-chapter.html","id":"do-not-store-secrets-in-the-cassettes-mock-files-recorded-responses","chapter":"14 Security","heading":"14.1.4 Do not store secrets in the cassettes, mock files, recorded responses","text":"vcr make sure configure vcr correctly.httptest httptest2 response body (headers, default) recorded. contains secrets, refer documentation redacting sensitive information (httptest2).webfakes creating recorded responses , make sure process leak secrets. test something related authentication, use fake secrets.API interacting uses OAuth instance, make sure leaking access tokens refresh tokens.","code":""},{"path":"security-chapter.html","id":"escape-tests-that-require-secrets","chapter":"14 Security","heading":"14.1.5 Escape tests that require secrets","text":"depends setup testing real requests.\nsure test requiring secrets run CRAN instance.","code":""},{"path":"security-chapter.html","id":"sensitive-recorded-responses","chapter":"14 Security","heading":"14.2 Sensitive recorded responses?","text":"case might want gitignore cassettes / mock files / recorded responses,\nskip tests using continuous integration (e.g. testthat::skip_on_ci() something involved).\n’d also Rbuildignore cassettes / mock files / recorded responses, want release CRAN.","code":""},{"path":"security-chapter.html","id":"further-resources","chapter":"14 Security","heading":"14.3 Further resources","text":"tools might help detect leaks prevent .shhgit’s goal “Find secrets code. Secrets detection GitHub, GitLab Bitbucket repositories”.Yelp’s detect-secret “enterprise friendly way detecting preventing secrets code.”.git-secret “bash tool store private data inside git repo”.","code":""},{"path":"errors-chapter.html","id":"errors-chapter","chapter":"15 Faking HTTP errors","heading":"15 Faking HTTP errors","text":"HTTP testing can test behavior package case API error without actually trigger API error.\nimportant testing package’s gracefulness (informative error message user) robustness (e.g. use retrying case API errors).","code":""},{"path":"errors-chapter.html","id":"how-to-test-for-api-errors-e.g.-503","chapter":"15 Faking HTTP errors","heading":"15.1 How to test for API errors (e.g. 503)","text":"Different possibilities:Use webmockr demo.Edit vcr cassette; careful skip test vcr vcr::skip_if_vcr_is_off().httptest httptest2, edit mock file demo, create scratch.webfakes, choose return, specific app test, see demo.","code":""},{"path":"errors-chapter.html","id":"how-to-test-for-sequence-of-responses-e.g.-503-then-200","chapter":"15 Faking HTTP errors","heading":"15.2 How to test for sequence of responses (e.g. 503 then 200)","text":"Different possibilities:Use webmockr.Edit vcr cassette; careful skip test vcr vcr::skip_if_vcr_is_off()httptest, easy yet (httptest2 issue)webfakes, follow docs. Also specific app test behavior want tests.`","code":""},{"path":"contributor-friendliness.html","id":"contributor-friendliness","chapter":"16 Contributor friendliness","heading":"16 Contributor friendliness","text":"make package wrapping HTTP resource contributor-friendly?rOpenSci general advice contributor-friendliness.Now, aspects dealing HTTP testing.","code":""},{"path":"contributor-friendliness.html","id":"taking-notes-about-encryption","chapter":"16 Contributor friendliness","heading":"16.1 Taking notes about encryption","text":"contributing guide, make sure note e.g. created encrypted token tests. Link script one run re-create . Good future contributors including !","code":""},{"path":"contributor-friendliness.html","id":"providing-a-sandbox","chapter":"16 Contributor friendliness","heading":"16.2 Providing a sandbox","text":"might neat provide sandbox, even just .interacting say Twitter API might want create Twitter account dedicated .don’t use live API credentials tests 🤣. Leaving humorous replies https://t.co/x0COfvt2QDIf interacting sort web platform might want create account special storing test data.interacting sort web platform might want create account special storing test data.web APIs provide test API key, test account one can request access .web APIs provide test API key, test account one can request access .Make sure take notes create / request access sandbox, contributing guide.","code":""},{"path":"contributor-friendliness.html","id":"switching-between-accounts-depending-on-the-development-mode","chapter":"16 Contributor friendliness","heading":"16.3 Switching between accounts depending on the development mode","text":"package might behaviour load default token instance, placed app dir.\nNow, testing, might want load another token, probably also want token choice automatic possible.rtweet package logic.detects testing/dev mode.environment variables present able create testing token.testing token default token loaded depending development mode.","code":"\nis_testing <- function() {\n identical(Sys.getenv(\"TESTTHAT\"), \"true\") \n}\nis_dev_mode <- function() {\n exists(\".__DEVTOOLS__\", .getNamespace(\"rtweet\"))\n}\nrtweet_test <- function() {\n access_token <- Sys.getenv(\"RTWEET_ACCESS_TOKEN\")\n access_secret <- Sys.getenv(\"RTWEET_ACCESS_SECRET\")\n \n if (identical(access_token, \"\") || identical(access_secret, \"\")) {\n return()\n }\n\n rtweet_bot(\n \"7rX1CfEYOjrtZenmBhjljPzO3\",\n \"rM3HOLDqmjWzr9UN4cvscchlkFprPNNg99zJJU5R8iYtpC0P0q\",\n access_token,\n access_secret\n )\n}"},{"path":"contributor-friendliness.html","id":"documenting-http-testing","chapter":"16 Contributor friendliness","heading":"16.4 Documenting HTTP testing","text":"Contributors package might familiar HTTP testing package(s) use (true non-trivial test setup). Make sure contributing guide mentions pre-requisites link resources (maybe even book?).","code":""},{"path":"conclusion-5.html","id":"conclusion-5","chapter":"17 Conclusion","heading":"17 Conclusion","text":"get read basic HTTP (testing) concepts R,\ndiscovered five great packages demos (httptest2, vcr&webmockr, httptest, webfakes), \ndived advanced topics like security.’s next? Applying tools package(s), course!Pick one several HTTP testing package(s) package. Examples combinations:\nvcr testing normal behavior, webmockr testing behavior case web resource errors.\nvcr httptest2 tests, webfakes advanced things like OAuth2.0 flows slow internet connection.\nPick one several HTTP testing package(s) package. Examples combinations:vcr testing normal behavior, webmockr testing behavior case web resource errors.vcr httptest2 tests, webfakes advanced things like OAuth2.0 flows slow internet connection.Read docs HTTP testing package(s) choose – worthy use time. vcr webmockr can even stay book take advantage “vcr details” “webmockr details” sections.Read docs HTTP testing package(s) choose – worthy use time. vcr webmockr can even stay book take advantage “vcr details” “webmockr details” sections.examples, also look reverse dependencies HTTP testing package(s) use see used developers.examples, also look reverse dependencies HTTP testing package(s) use see used developers.Follow developments HTTP testing package(s) choose. five packages presented developed GitHub, e.g. release-watch repositories. also distributed CRAN, might use usual channel learning CRAN updates.Follow developments HTTP testing package(s) choose. five packages presented developed GitHub, e.g. release-watch repositories. also distributed CRAN, might use usual channel learning CRAN updates.Participate development HTTP testing package(s) choose? bug reports, feature requests, contributions might helpful. Make sure read contributing guide look current activity repositories.Participate development HTTP testing package(s) choose? bug reports, feature requests, contributions might helpful. Make sure read contributing guide look current activity repositories.Report feedback book, experience HTTP testing, tips, etc.\nGitHub repository book,\nrOpenSci forum.\nReport feedback book, experience HTTP testing, tips, etc.GitHub repository book,rOpenSci forum.Happy HTTP testing!","code":""},{"path":"mocking.html","id":"mocking","chapter":"18 Mocking HTTP Requests","heading":"18 Mocking HTTP Requests","text":"short version : webmockr helps stub HTTP requests \ndon’t repeat .","code":""},{"path":"mocking.html","id":"webmockr-pkgdown","chapter":"18 Mocking HTTP Requests","heading":"18.1 Package documentation","text":"Check https://docs.ropensci.org/webmockr/ documentation webmockr functions.","code":""},{"path":"mocking.html","id":"webmockr-features","chapter":"18 Mocking HTTP Requests","heading":"18.2 Features","text":"Stubbing HTTP requests low http client lib levelSetting verifying expectations HTTP requestsMatching requests based method, URI, headers bodySupport testthat via vcrCan used testing outside testing context","code":""},{"path":"mocking.html","id":"webmockr-detail","chapter":"18 Mocking HTTP Requests","heading":"18.3 How webmockr works in detail","text":"tell webmockr HTTP request want match sees \nrequest matching criteria doesn’t actually HTTP request. Instead,\ngives back object gotten back real request, \nbits knows . example, can’t give back actual\ndata ’d get real HTTP request request wasn’t performed.addition, set expectation webmockr return, \nreturn . example, expect request return 418 error\n(’m Teapot), ’s ’ll get.can match againstHTTP method (required)Plus single combination following:URI\nRight now, can match directly URI’s, regex URI patterns.\nEventually, support RFC 6570 URI templates.\nnormalize URI paths URL encoded things match\nURL un-encoded things (e.g. hello world hello%20world)\nRight now, can match directly URI’s, regex URI patterns.\nEventually, support RFC 6570 URI templates.normalize URI paths URL encoded things match\nURL un-encoded things (e.g. hello world hello%20world)Query parameters\nnormalize query parameter values URL encoded things match\nURL un-encoded things (e.g. message = hello world \nmessage = hello%20world)\nnormalize query parameter values URL encoded things match\nURL un-encoded things (e.g. message = hello world \nmessage = hello%20world)Request headers\nnormalize headers treat forms headers equal. \nexample, following two sets headers equal:\nlist(H1 = \"value1\", content_length = 123, X_CuStOm_hEAder = \"foo\")\nlist(h1 = \"value1\", \"Content-Length\" = 123, \"x-cuSTOM-HeAder\" = \"foo\")\n\nnormalize headers treat forms headers equal. \nexample, following two sets headers equal:\nlist(H1 = \"value1\", content_length = 123, X_CuStOm_hEAder = \"foo\")\nlist(h1 = \"value1\", \"Content-Length\" = 123, \"x-cuSTOM-HeAder\" = \"foo\")\nlist(H1 = \"value1\", content_length = 123, X_CuStOm_hEAder = \"foo\")list(h1 = \"value1\", \"Content-Length\" = 123, \"x-cuSTOM-HeAder\" = \"foo\")Request bodyReal HTTP requestsThere’s scenarios think using webmockr:doingwebmockr loaded turned . point webmockr doesn’t\nchange anything.turn webmockr likewebmockr now default allow real HTTP requests http\nlibraries adapters loaded (crul httr).can optionally allow real requests via webmockr_allow_net_connect(), \ndisallow real requests via webmockr_disable_net_connect(). can check\nwhether allowing real requests webmockr_net_connect_allowed().Certain kinds real HTTP requests allowed: don’t suppoprt yet,\ncan allow localhost HTTP requests allow_localhost parameter\nwebmockr_configure() function.Storing actual HTTP responseswebmockr doesn’t . Check vcr","code":"\nlibrary(webmockr)\nwebmockr::enable()"},{"path":"mocking.html","id":"webmockr-basic-usage","chapter":"18 Mocking HTTP Requests","heading":"18.4 Basic usage","text":"Stubbed request based uri default response","code":"\nlibrary(\"webmockr\")\n# enable webmockr\nwebmockr::enable()\nstub_request(\"get\", \"https://httpbin.org/get\")#> \n#> method: get\n#> uri: https://httpbin.org/get\n#> with: \n#> query: \n#> body: \n#> request_headers: \n#> to_return:\nlibrary(\"crul\")\nx <- HttpClient$new(url = \"https://httpbin.org\")\nx$get('get')#> \n#> url: https://httpbin.org/get\n#> request_headers: \n#> User-Agent: libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\n#> Accept-Encoding: gzip, deflate\n#> Accept: application/json, text/xml, application/xml, */*\n#> response_headers: \n#> status: 200"},{"path":"webmockr-stubs.html","id":"webmockr-stubs","chapter":"19 stubs","heading":"19 stubs","text":"set return objectsStubbing requests based method, uri query paramsStubbing requests set expectation timeoutStubbing requests set HTTP error expectation","code":"\nlibrary(\"webmockr\")\nstub_request(\"get\", \"https://httpbin.org/get\") %>%\n wi_th(\n query = list(hello = \"world\")) %>%\n to_return(status = 418)#> \n#> method: get\n#> uri: https://httpbin.org/get\n#> with: \n#> query: hello=world\n#> body: \n#> request_headers: \n#> to_return: \n#> - status: 418\n#> body: \n#> response_headers: \n#> should_timeout: FALSE\n#> should_raise: FALSE\nx$get('get', query = list(hello = \"world\"))#> \n#> url: https://httpbin.org/get\n#> request_headers: \n#> User-Agent: libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\n#> Accept-Encoding: gzip, deflate\n#> Accept: application/json, text/xml, application/xml, */*\n#> response_headers: \n#> status: 418\nstub_request(\"get\", \"https://httpbin.org/get\") %>%\n wi_th(query = list(hello = \"world\"), \n headers = list('User-Agent' = 'libcurl/7.51.0 r-curl/2.6 crul/0.3.6', \n 'Accept-Encoding' = \"gzip, deflate\"))#> \n#> method: get\n#> uri: https://httpbin.org/get\n#> with: \n#> query: hello=world\n#> body: \n#> request_headers: User-Agent=libcurl/7.51.0 r-cur..., Accept-Encoding=gzip, deflate\n#> to_return:\nstub_registry()#> \n#> Registered Stubs\n#> GET: https://httpbin.org/get \n#> GET: https://httpbin.org/get?hello=world | to_return: with status 418 \n#> 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\"}\nx <- HttpClient$new(url = \"https://httpbin.org\")\nx$get('get', query = list(hello = \"world\"))#> \n#> url: https://httpbin.org/get\n#> request_headers: \n#> User-Agent: libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\n#> Accept-Encoding: gzip, deflate\n#> Accept: application/json, text/xml, application/xml, */*\n#> response_headers: \n#> status: 418\nstub_request(\"post\", \"https://httpbin.org/post\") %>% to_timeout()\nx <- HttpClient$new(url = \"https://httpbin.org\")\nx$post('post')\n#> Error: Request Timeout (HTTP 408).\n#> - The client did not produce a request within the time that the server was prepared\n#> to wait. The client MAY repeat the request without modifications at any later time.\nlibrary(fauxpas)\nstub_request(\"get\", \"https://httpbin.org/get?a=b\") %>% to_raise(HTTPBadRequest)\nx <- HttpClient$new(url = \"https://httpbin.org\")\nx$get('get', query = list(a = \"b\"))\n#> Error: Bad Request (HTTP 400).\n#> - The request could not be understood by the server due to malformed syntax.\n#> The client SHOULD NOT repeat the request without modifications."},{"path":"webmockr-stubs.html","id":"webmockr-disk","chapter":"19 stubs","heading":"19.1 Writing to disk","text":"two ways deal mocking writing disk. First, can create file\ndata ’d like file, tell crul httr file .\nSecond, can simply give webmockr file path (doesn’t exist yet) \ndata, webmockr can take care putting data file.’s first method, put data file mock, pass \nfile connection (file()) to_return().second method, use webmockr::mock_file() webmockr handle file\ncontents.webmockr also supports httr::write_disk(), letting webmockr handle \nmock file creation:","code":"\n## make a temp file\nf <- tempfile(fileext = \".json\")\n## write something to the file\ncat(\"{\\\"hello\\\":\\\"world\\\"}\\n\", file = f)\n## make the stub\ninvisible(stub_request(\"get\", \"https://httpbin.org/get\") %>% \n to_return(body = file(f)))\n## make a request\nout <- HttpClient$new(\"https://httpbin.org/get\")$get(disk = f)\n## view stubbed file content\nreadLines(file(f))#> [1] \"{\\\"hello\\\":\\\"world\\\"}\"\ng <- tempfile(fileext = \".json\")\n## make the stub\ninvisible(stub_request(\"get\", \"https://httpbin.org/get?a=b\") %>% \n to_return(body = mock_file(path = g, payload = \"{\\\"hello\\\":\\\"mars\\\"}\\n\")))\n## make a request\nout <- crul::HttpClient$new(\"https://httpbin.org/get?a=b\")$get(disk = g)\n## view stubbed file content\nreadLines(out$content)#> [1] \"{\\\"hello\\\":\\\"mars\\\"}\" \"\"\nlibrary(httr)\nhttr_mock()\n## make a temp file\nf <- tempfile(fileext = \".json\")\n## make the stub\ninvisible(stub_request(\"get\", \"https://httpbin.org/get?cheese=swiss\") %>% \n to_return(\n body = mock_file(path = f, payload = \"{\\\"foo\\\": \\\"bar\\\"}\"),\n headers = list('content-type' = \"application/json\")\n ))\n## make a request\nout <- GET(\"https://httpbin.org/get?cheese=swiss\", write_disk(f, TRUE))\n## view stubbed file content\nreadLines(out$content)#> [1] \"{\\\"foo\\\": \\\"bar\\\"}\""},{"path":"webmockr-testing.html","id":"webmockr-testing","chapter":"20 testing","heading":"20 testing","text":"","code":"\nlibrary(\"webmockr\")\nlibrary(\"crul\")\nlibrary(\"testthat\")\n\nstub_registry_clear()\n\n# make a stub\nstub_request(\"get\", \"https://httpbin.org/get\") %>%\n to_return(body = \"success!\", status = 200)#> \n#> method: get\n#> uri: https://httpbin.org/get\n#> with: \n#> query: \n#> body: \n#> request_headers: \n#> to_return: \n#> - status: 200\n#> body: success!\n#> response_headers: \n#> should_timeout: FALSE\n#> should_raise: FALSE\n# check that it's in the stub registry\nstub_registry()#> \n#> Registered Stubs\n#> GET: https://httpbin.org/get | to_return: with body \"success!\" with status 200\n# make the request\nz <- crul::HttpClient$new(url = \"https://httpbin.org\")$get(\"get\")\n\n# run tests (nothing returned means it passed)\nexpect_is(z, \"HttpResponse\")\nexpect_equal(z$status_code, 200)\nexpect_equal(z$parse(\"UTF-8\"), \"success!\")"},{"path":"webmockr-utilities.html","id":"webmockr-utilities","chapter":"21 utilities","heading":"21 utilities","text":"","code":"\nlibrary(\"webmockr\")"},{"path":"webmockr-utilities.html","id":"webmockr-","chapter":"21 utilities","heading":"21.1 Managing stubs","text":"enable()enabled()disable()httr_mock()","code":""},{"path":"webmockr-utilities.html","id":"webmockr-managing-stubs","chapter":"21 utilities","heading":"21.2 Managing stubs","text":"stub_registry()stub_registry_clear()remove_request_stub()","code":""},{"path":"webmockr-utilities.html","id":"webmockr-managing-requests","chapter":"21 utilities","heading":"21.3 Managing requests","text":"request_registry()","code":""},{"path":"vcr-intro.html","id":"vcr-intro","chapter":"22 Caching HTTP requests","heading":"22 Caching HTTP requests","text":"Record HTTP calls replay ","code":""},{"path":"vcr-intro.html","id":"vcr-pkgdown","chapter":"22 Caching HTTP requests","heading":"22.1 Package documentation","text":"Check https://docs.ropensci.org/vcr/ documentation vcr functions.","code":""},{"path":"vcr-intro.html","id":"terminology","chapter":"22 Caching HTTP requests","heading":"22.2 Terminology","text":"vcr: name comes idea want record something play back later, like vcrcassette: thing record HTTP interactions . Right now option file system (writing files), future things, e.g. key-value store like Redisfixture: fixture something used consistently test piece software. case, cassette (just defined ) fixture - used unit tests. use setup function vcr_setup() default directory created hold cassettes called fixtures/ signal folder contains.Persisters: save requests - currently option file systemserialize: translating data format can stored; , translate HTTP request response data representation disk read back laterSerializers: serialize HTTP response - currently option YAML; options future include e.g. JSONinsert cassette: create cassette (HTTP interactions recorded cassette)eject cassette: eject cassette (longer recording cassette)replay: refers using cached result http request recorded earlier","code":""},{"path":"vcr-intro.html","id":"design","chapter":"22 Caching HTTP requests","heading":"22.3 Design","text":"section explains vcr’s internal design architecture.","code":""},{"path":"vcr-intro.html","id":"where-vcr-comes-from-and-why-r6","chapter":"22 Caching HTTP requests","heading":"22.3.1 Where vcr comes from and why R6","text":"vcr “ported” Ruby gem (aka, library) name6.\nported Ruby, object-oriented programming language\nthought easier use object system R \nclosely resemble used Ruby (least opinion). \nthinking lead choosing R6. exported functions users interact\nR6 classes, rather normal R functions. However,\ninternal code package uses R6. Thus, familiarity\nR6 important people may want contribute vcr,\nrequired vcr users.","code":""},{"path":"vcr-intro.html","id":"principles","chapter":"22 Caching HTTP requests","heading":"22.3.2 Principles","text":"","code":""},{"path":"vcr-intro.html","id":"an-easy-to-use-interface-hides-complexity","chapter":"22 Caching HTTP requests","heading":"22.3.2.1 An easy to use interface hides complexity","text":"described , vcr uses R6 internally, users interact \nnormal R functions. Internal functions quite complicated \nlargely R6 exported, simpler functions users interact \nnormal R functions.","code":""},{"path":"vcr-intro.html","id":"classfunction-names-are-inherited-from-ruby-vcr","chapter":"22 Caching HTTP requests","heading":"22.3.2.2 Class/function names are inherited from Ruby vcr","text":"Since R vcr ported Ruby, kept names \nfunctions/classes variables. ’re wondering \nfunction, class, variable particular name, derivation\ncan found package, part .","code":""},{"path":"vcr-intro.html","id":"hooks-into-http-clients","chapter":"22 Caching HTTP requests","heading":"22.3.2.3 Hooks into HTTP clients","text":"Perhaps fundamental thing package work \nknows HTTP requests made. stumped \nquite long time. looking Ruby vcr, first thought \nmust “listening” HTTP requests somehow. found \nmonkey patching; ’s ’s achieved Ruby. , Ruby\nvcr package literally overrides certain methods Ruby HTTP clients,\nhijacking internals HTTP clients.However, monkey patching allowed R. Thus, R \nsomehow “hooks” HTTP clients R. Fortunately, Scott \nmaintainer one HTTP clients, crul, able quickly\ncreate hook. fortunately, already hook mechanism\nhttr package.actual hooks vcr, webmockr. vcr depends \nwebmockr hooking HTTP clients httr crul.","code":""},{"path":"vcr-intro.html","id":"internal-classes","chapter":"22 Caching HTTP requests","heading":"22.3.3 Internal classes","text":"overview important aspects vcr.","code":""},{"path":"vcr-intro.html","id":"configuration","chapter":"22 Caching HTTP requests","heading":"22.3.3.1 Configuration","text":"internal object (vcr_c) created vcr loaded \ndefault vcr configuration options inside R6 class VCRConfig -\nsee https://github.com/ropensci/vcr/blob/main/R/onLoad.R. \nclass keeps track default user specified configuration options.\ncan access vcr_c using triple namespace :::, though \nintended general use. Whenever make calls vcr_configure()\nconfiguration functions, vcr_c affected.","code":""},{"path":"vcr-intro.html","id":"cassette-class","chapter":"22 Caching HTTP requests","heading":"22.3.3.2 Cassette class","text":"Cassette R6 class handles internals/state cassette.\ntime run use_cassette() class used. class quite\nmethods , ’s lot going class. Ideally\nclass separated subclasses handle similar sets\nlogic, ’s easy way R6.note Cassette called, within initialize()\ncall webmockr used create webmockr stubs.","code":""},{"path":"vcr-intro.html","id":"how-http-requests-are-handled","chapter":"22 Caching HTTP requests","heading":"22.3.3.3 How HTTP requests are handled","text":"Within webmockr, calls vcr class RequestHandler, \nchild classes RequestHandlerCrul RequestHandlerHttr \ncrul httr, respectively. classes determine \nHTTP request. options HTTP request include:Ignored can ignore HTTP requests certain rules using \nconfiguration options ignore_hosts ignore_localhostStubbed vcr HTTP request match found\ncassette defined use_cassette()/insert_cassette() call.\ncase matching request/response cassette returned\nreal HTTP request allowed.Recordable HTTP request match found\ncassette defined use_cassette()/insert_cassette() call.\ncase real HTTP request allowed, request/response \nrecorded cassette.Unhandled group cases, cause error\nthrown message trying help user figure \nfix problem.use vcr logging ’ll see categories logs.","code":""},{"path":"vcr-intro.html","id":"serializers","chapter":"22 Caching HTTP requests","heading":"22.3.3.4 Serializers","text":"Serializers handle format cassettes written files disk.\ncurrent options YAML (default) JSON. YAML implemented first\nvcr ’s default option Ruby vcr.R6 class Serializer parent class serializer types;\nYAML JSON R6 classes inherit Serializer. \nYAML JSON define just two methods: serialize() deserialize()\nconverting R structures yaml json, converting yaml json back\nR structures, respectively.","code":""},{"path":"vcr-intro.html","id":"environments","chapter":"22 Caching HTTP requests","heading":"22.3.4 Environments","text":"","code":""},{"path":"vcr-intro.html","id":"logging","chapter":"22 Caching HTTP requests","heading":"22.3.4.1 Logging","text":"internal environment (vcr_log_env) used use logging.\npoint keeps track one variable - file - able\nrefer file used logging across many classes/functions\nneed write log file.","code":""},{"path":"vcr-intro.html","id":"a-bit-of-housekeeping","chapter":"22 Caching HTTP requests","heading":"22.3.4.2 A bit of housekeeping","text":"Another internal environment (vcr__env) used keep track \nitems, including current cassette use, last vcr error.","code":""},{"path":"vcr-intro.html","id":"lightswitch","chapter":"22 Caching HTTP requests","heading":"22.3.4.3 Lightswitch","text":"Another internal environment (light_switch) used keep track users\nturning vcr. See ?lightswitch.","code":""},{"path":"vcr-intro.html","id":"vcr-basic-usage","chapter":"22 Caching HTTP requests","heading":"22.4 Basic usage","text":"","code":""},{"path":"vcr-intro.html","id":"in-tests","chapter":"22 Caching HTTP requests","heading":"22.4.1 In tests","text":"tests, whichever tests want use vcr, wrap vcr::use_cassette() call like:put vcr::use_cassette() block inside, put testthat expectations outside \nvcr::use_cassette() block:Don’t wrap use_cassette() block inside test_that() block testthat expectations inside use_cassette() block, ’ll get line number use_cassette() block starts failures.first time run tests, “cassette” .e. file recorded HTTP interactions, created tests/fixtures/rl_citation.yml.\ntimes , cassette used.\nchange code HTTP interactions needed code wrapped vcr::use_cassette(\"rl_citation\", delete tests/fixtures/rl_citation.yml run tests re-recording cassette.","code":"\nlibrary(testthat)\nvcr::use_cassette(\"rl_citation\", {\n test_that(\"my test\", {\n aa <- rl_citation()\n\n expect_is(aa, \"character\")\n expect_match(aa, \"IUCN\")\n expect_match(aa, \"www.iucnredlist.org\")\n })\n})\nlibrary(testthat)\ntest_that(\"my test\", {\n vcr::use_cassette(\"rl_citation\", {\n aa <- rl_citation()\n })\n\n expect_is(aa, \"character\")\n expect_match(aa, \"IUCN\")\n expect_match(aa, \"www.iucnredlist.org\")\n})"},{"path":"vcr-intro.html","id":"outside-of-tests","chapter":"22 Caching HTTP requests","heading":"22.4.2 Outside of tests","text":"want get feel vcr works, although don’t need .request gets recorded, subsequent requests form used cached HTTP response, much fasterImportantly, unit test deals inputs outputs - behind scenes use cached HTTP response - thus, tests run faster.cached response looks something like (condensed brevity):components request response preserved, HTTP client (case crul) can reconstruct response just wasn’t using vcr.","code":"\nlibrary(vcr)\nlibrary(crul)\n\ncli <- crul::HttpClient$new(url = \"https://eu.httpbin.org\")\nsystem.time(\n use_cassette(name = \"helloworld\", {\n cli$get(\"get\")\n })\n)\nsystem.time(\n use_cassette(name = \"helloworld\", {\n cli$get(\"get\")\n })\n)http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '200'\n message: OK\n explanation: Request fulfilled, document follows\n headers:\n status: HTTP/1.1 200 OK\n connection: keep-alive\n body:\n encoding: UTF-8\n string: \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\"\n recorded_at: 2018-04-03 22:55:02 GMT\n recorded_with: vcr/0.1.0, webmockr/0.2.4, crul/0.5.2"},{"path":"vcr-intro.html","id":"less-basic-usage","chapter":"22 Caching HTTP requests","heading":"22.4.3 Less basic usage","text":"tweaking things needs, make sure read docs configuration (e.g., fixtures saved? can re-recorded automatically regulary?) request matching (vcr match request recorded interaction?)components request response preserved, HTTP client (case crul) can reconstruct response just wasn’t using vcr.","code":""},{"path":"vcr-intro.html","id":"vcr-enabled-testing","chapter":"22 Caching HTTP requests","heading":"22.5 vcr enabled testing","text":"","code":""},{"path":"vcr-intro.html","id":"check-vs-test","chapter":"22 Caching HTTP requests","heading":"22.5.1 check vs. test","text":"TLDR: Run devtools::test() running devtools::check() recording cassettes.running tests checks whole package, note ’ll get different results \ndevtools::check() (check button RStudio build pane) vs. devtools::test() (test button RStudio build pane). arises devtools::check() runs \ntemporary directory files created (vcr cassettes) temporary directory \nthus don’t persist devtools::check() exits.However, devtools::test() run temporary directory, files created (vcr\ncassettes) whatever directory ’re running .Alternatively, can run devtools::test_file() (“Run test” button RStudio) create vcr cassettes one test file time.","code":""},{"path":"vcr-intro.html","id":"vcr-ci","chapter":"22 Caching HTTP requests","heading":"22.5.2 CI sites: GitHub Actions, Appveyor, etc.","text":"Refer security chapter.","code":""},{"path":"vcr-usage.html","id":"vcr-usage","chapter":"23 Advanced vcr usage","heading":"23 Advanced vcr usage","text":"Now ’ve covered basic vcr usage, ’s time advanced usage topics.","code":"\nlibrary(\"vcr\")"},{"path":"vcr-usage.html","id":"vcr-disk","chapter":"23 Advanced vcr usage","heading":"23.1 Mocking writing to disk","text":"http requests write response disk, \nuse vcr_configure() set write_disk_path option. See \nwrite_disk_path configuration option., create temporary directory, set fixturesThen pass file path (doesn’t exist yet) crul’s disk parameter.\nvcr take care handling writing response file \naddition cassette.also works httr. difference write disk\nfunction httr::write_disk(path) rather parameter.Note write disk using vcr, cassette slightly\nchanged. Instead holding http response body , cassette\nfile path response body.file response body otherwise string\nyaml field :","code":"\ntmpdir <- tempdir()\nvcr_configure(\n dir = file.path(tmpdir, \"fixtures\"),\n write_disk_path = file.path(tmpdir, \"files\")\n)#> \n#> Cassette Dir: /tmp/RtmporE3ZE/fixtures\n#> Record: once\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path: /tmp/RtmporE3ZE/files\nlibrary(crul)\n## make a temp file\nf <- tempfile(fileext = \".json\")\n## make a request\ncas <- use_cassette(\"test_write_to_disk\", {\n out <- HttpClient$new(\"https://httpbin.org/get\")$get(disk = f)\n})\nfile.exists(out$content)#> [1] TRUE\nout$parse()#> [1] \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json, text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\": \\\"libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\\\", \\n \\\"X-Amzn-Trace-Id\\\": \\\"Root=1-65bcaca2-7a50cb5878c03def0bb9e5dd\\\"\\n }, \\n \\\"origin\\\": \\\"13.90.131.63\\\", \\n \\\"url\\\": \\\"https://httpbin.org/get\\\"\\n}\\n\"http_interactions:\n- request:\n method: get\n uri: https://httpbin.org/get\n response:\n headers:\n status: HTTP/1.1 200 OK\n access-control-allow-credentials: 'true'\n body:\n encoding: UTF-8\n file: yes\n string: /private/var/folders/fc/n7g_vrvn0sx_st0p8lxb3ts40000gn/T/Rtmp5W4olr/files/file177e2e5d97ec.json{\n \"args\": {}, \n \"headers\": {\n \"Accept\": \"application/json, text/xml, application/xml, */*\", \n \"Accept-Encoding\": \"gzip, deflate\", \n \"Host\": \"httpbin.org\", \n \"User-Agent\": \"libcurl/7.54.0 r-curl/4.3 crul/0.9.0\"\n }, \n \"origin\": \"24.21.229.59, 24.21.229.59\", \n \"url\": \"https://httpbin.org/get\"\n}"},{"path":"vcr-configuration.html","id":"vcr-configuration","chapter":"24 Configure vcr","heading":"24 Configure vcr","text":"can also get default configuration variables via vcr_config_defaults()defaults set load vcr - can override described .","code":"\nlibrary(\"vcr\")\nvcr_config_defaults()#> $warn_on_empty_cassette\n#> [1] TRUE\n#> \n#> $quiet\n#> [1] TRUE\n#> \n#> $verbose_errors\n#> [1] FALSE\n#> \n#> $write_disk_path\n#> NULL\n#> \n#> $filter_query_parameters\n#> NULL\n#> \n#> $filter_response_headers\n#> NULL\n#> \n#> $filter_request_headers\n#> NULL\n#> \n#> $filter_sensitive_data_regex\n#> NULL\n#> \n#> $filter_sensitive_data\n#> NULL\n#> \n#> $log_opts\n#> $log_opts$file\n#> [1] \"vcr.log\"\n#> \n#> $log_opts$log_prefix\n#> [1] \"Cassette\"\n#> \n#> $log_opts$date\n#> [1] TRUE\n#> \n#> \n#> $log\n#> [1] FALSE\n#> \n#> $linked_context\n#> NULL\n#> \n#> $cassettes\n#> list()\n#> \n#> $allow_http_connections_when_no_cassette\n#> [1] FALSE\n#> \n#> $clean_outdated_http_interactions\n#> [1] FALSE\n#> \n#> $re_record_interval\n#> NULL\n#> \n#> $turned_off\n#> [1] FALSE\n#> \n#> $preserve_exact_body_bytes\n#> [1] FALSE\n#> \n#> $uri_parser\n#> [1] \"crul::url_parse\"\n#> \n#> $ignore_request\n#> NULL\n#> \n#> $ignore_localhost\n#> [1] FALSE\n#> \n#> $ignore_hosts\n#> NULL\n#> \n#> $persist_with\n#> [1] \"FileSystem\"\n#> \n#> $json_pretty\n#> [1] FALSE\n#> \n#> $serialize_with\n#> [1] \"yaml\"\n#> \n#> $allow_unused_http_interactions\n#> [1] TRUE\n#> \n#> $match_requests_on\n#> [1] \"method\" \"uri\" \n#> \n#> $record\n#> [1] \"once\"\n#> \n#> $dir\n#> [1] \".\""},{"path":"vcr-configuration.html","id":"set-configuration-variables","chapter":"24 Configure vcr","heading":"24.1 Set configuration variables","text":"Use vcr_configure() set configuration variables.example, set single variable:many :","code":"\nvcr_configure(\n dir = \"foobar/vcr_cassettes\"\n)#> \n#> Cassette Dir: foobar/vcr_cassettes\n#> Record: once\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:\nvcr_configure(\n dir = \"foobar/vcr_cassettes\",\n record = \"all\"\n)#> \n#> Cassette Dir: foobar/vcr_cassettes\n#> Record: all\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"re-set-to-defaults","chapter":"24 Configure vcr","heading":"24.2 Re-set to defaults","text":"","code":"\nvcr_configure_reset()"},{"path":"vcr-configuration.html","id":"details-on-some-of-the-config-options","chapter":"24 Configure vcr","heading":"24.3 Details on some of the config options","text":"","code":""},{"path":"vcr-configuration.html","id":"dir","chapter":"24 Configure vcr","heading":"24.3.1 dir","text":"Directory cassettes stored","code":"\nvcr_configure(dir = \"new/path\")#> \n#> Cassette Dir: new/path\n#> Record: once\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"record","chapter":"24 Configure vcr","heading":"24.3.2 record","text":"record modeOne : ‘’, ‘none’, ‘new_episodes’, ‘’. See ?recording info options","code":"\nvcr_configure(record = \"new_episodes\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"match_requests_on","chapter":"24 Configure vcr","heading":"24.3.3 match_requests_on","text":"Customize vcr matches requests","code":"\nvcr_configure(match_requests_on = c('query', 'headers'))#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"allow_unused_http_interactions","chapter":"24 Configure vcr","heading":"24.3.4 allow_unused_http_interactions","text":"Allow HTTP connections cassetteDefault TRUE, thus error http interactions unused. \ncan set FALSE case vcr errors cassette ejected \nhttp interactions used.","code":"\nvcr_configure(allow_unused_http_interactions = FALSE)#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"serialize_with","chapter":"24 Configure vcr","heading":"24.3.5 serialize_with","text":"serializer use: “yaml” “json”. Note can \nmultiple cassettes name long use different\nserializers; want one cassette given cassette name,\nmake sure switch serializers, clean files longer need.","code":"\nvcr_configure(serialize_with = \"yaml\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"persist_with","chapter":"24 Configure vcr","heading":"24.3.6 persist_with","text":"persister use. Right now option “FileSystem”","code":"\nvcr_configure(persist_with = \"FileSystem\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"ignoring-some-requests","chapter":"24 Configure vcr","heading":"24.3.7 ignoring some requests","text":"ignore_hostsSpecify particular hosts ignore. ignore, mean \nreal HTTP requests ignored host allowed occur, \nothers .ignore_localhostIgnore localhost requestsignore_requestTHIS DOESN’T WORK YETHow ignore requestsFor ignoring requests, can example, real http requests go (ignored vcr) requests handled vcr. example, let’s say want requests google.com ignored:request httpbin.org handled vcr, cassette created request/response url, google.com request ignored cached .Note: ignoring requests works crul package now; work httr later vcr version.","code":"\nvcr_configure(ignore_hosts = \"google.com\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: google.com\n#> ignore localhost?: FALSE\n#> Write disk path:\nvcr_configure(ignore_localhost = TRUE)#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: google.com\n#> ignore localhost?: TRUE\n#> Write disk path:\nvcr_configure(ignore_hosts = \"google.com\")\nuse_cassette(\"foo_bar\", {\n crul::HttpClient$new(\"https://httpbin.org/get\")$get()\n crul::HttpClient$new(\"https://google.com\")$get()\n})"},{"path":"vcr-configuration.html","id":"uri_parse","chapter":"24 Configure vcr","heading":"24.3.8 uri_parse","text":"uri parser useBy default use crul::url_parse, can use different one. Remember\npass function quoted, namespaced.","code":"\nvcr_configure(uri_parser = \"urltools::url_parse\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: urltools::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: google.com\n#> ignore localhost?: TRUE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"preserve_exact_body_bytes","chapter":"24 Configure vcr","heading":"24.3.9 preserve_exact_body_bytes","text":"HTTP servers well-behaved respond invalid data. Set\npreserve_exact_body_bytes TRUE base64 encode result body \norder preserve bytes exactly -. vcr \ndefault, since base64-encoding string removes human readability\ncassette.","code":"\nvcr_configure(preserve_exact_body_bytes = TRUE)#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: urltools::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: TRUE\n#> Logging?: FALSE\n#> ignored hosts: google.com\n#> ignore localhost?: TRUE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"filter_sensitive_data","chapter":"24 Configure vcr","heading":"24.3.10 filter_sensitive_data","text":"named list values replace. Sometimes package script \nworking sensitive tokens/keys, want accidentally\nshare world.recording (writing cassette) replacement \nreading cassette reverse replacement get back\nreal data.recording disk, env var MY_API_KEY retrieved machine,\nfind instances , replace . replaying\ncreate HTTP response object put real value env var\nback place.target specific request response headers see filter_request_headers\nfilter_response_headers.","code":"\nvcr_configure(\n filter_sensitive_data = list(\"\" = Sys.getenv('MY_API_KEY'))\n)"},{"path":"vcr-configuration.html","id":"filter_request_headers","chapter":"24 Configure vcr","heading":"24.3.11 filter_request_headers","text":"Expects character vector named list. character vector, \nunnamed element list, request header removed \nwritten cassette.named list passed, name header value \nvalue replace real value.request header set remove replace removed/replaced\ncassette, requests using cassette, still \ncrul httr response objects real request creates \ncassette.Examples:","code":"\nvcr_configure(\n filter_request_headers = \"Authorization\"\n)\nvcr_configure(\n filter_request_headers = c(\"Authorization\", \"User-Agent\")\n)\nvcr_configure(\n filter_request_headers = list(Authorization = \"<<>>\")\n)"},{"path":"vcr-configuration.html","id":"filter_response_headers","chapter":"24 Configure vcr","heading":"24.3.12 filter_response_headers","text":"Expects character vector named list. character vector, \nunnamed element list, response header removed \nwritten cassette.named list passed, name header value \nvalue replace real value.response header set remove replace removed/replaced\ncassette, requests using cassette, still \ncrul httr response objects real request creates \ncassette.Examples:","code":"\nvcr_configure(\n filter_response_headers = \"server\"\n)\nvcr_configure(\n filter_response_headers = c(\"server\", \"date\")\n)\nvcr_configure(\n filter_response_headers = list(server = \"fake-server\")\n)"},{"path":"vcr-configuration.html","id":"filter_query_parameters","chapter":"24 Configure vcr","heading":"24.3.13 filter_query_parameters","text":"Expects character vector named list. character vector, \nunnamed element list, query parameter removed (parameter\nname value) written cassette.named list passed, name query parameter name \nvalue value replace real value.response header set remove replace removed/replaced\ncassette, requests using cassette, still \ncrul httr response objects real request creates \ncassette.Beware match_requests_on option using filter. \nfilter query parameter ’s probably bad idea match query\ngiven way vcr restore exact http request\ncassette one query parameters removed changed.\nOne way filter query parameter still match query \nleast complete uri use replacement behavior (named list),\ninstead list(=\"b\") use two values list(=c(\"b\",\"c\")), \n“c” string stored cassette. course replace\nvalues values environment variables obscure\nreal values code public.Examples:","code":"\n# completely drop parameter \"user\"\nvcr_configure(\n filter_query_parameters = \"user\"\n)\n# completely drop parameters \"user\" and \"api_key\"\nvcr_configure(\n filter_query_parameters = c(\"user\", \"api_key\")\n)\n# replace the value of parameter \"api_key\" with \"fake-api-key\"\n# NOTE: in this case there's no way to put back any value on\n# subsequent requests, so we have to match by dropping this\n# parameter value before comparing URIs\nvcr_configure(\n filter_query_parameters = list(api_key = \"fake-api-key\")\n)\n# replace the value found at Sys.getenv(\"MY_API_KEY\") of parameter\n# \"api_key\" with the value \"foo\". When using a cassette on subsequent\n# requests, we can replace \"foo\" with the value at Sys.getenv(\"MY_API_KEY\")\n# before doing the URI comparison\nvcr_configure(\n filter_query_parameters = list(api_key = c(Sys.getenv(\"MY_API_KEY\"), \"foo\"))\n)"},{"path":"record-modes.html","id":"record-modes","chapter":"25 Record modes","heading":"25 Record modes","text":"Record modes dictate circumstances http requests/responses \nrecorded cassettes (disk). Set recording mode parameter\nrecord use_cassette() insert_cassette() functions.","code":""},{"path":"record-modes.html","id":"once","chapter":"25 Record modes","heading":"25.1 once","text":"record mode :Replay previously recorded interactions.Record new interactions cassette file.Cause error raised new requests cassette file.similar new_episodes record mode, prevent new,\nunexpected requests made (.e. request URI changed\nwhatever).default record mode, used set one.","code":""},{"path":"record-modes.html","id":"none","chapter":"25 Record modes","heading":"25.2 none","text":"none record mode :Replay previously recorded interactions.Cause error raised new requests.useful code makes potentially dangerous\nHTTP requests. none record mode guarantees \nnew HTTP requests made.","code":""},{"path":"record-modes.html","id":"new_episodes","chapter":"25 Record modes","heading":"25.3 new_episodes","text":"new_episodes record mode :Record new interactions.Replay previously recorded interactions.similar record mode, always record new\ninteractions, even existing recorded one similar\n(identical, based match_request_on option).","code":""},{"path":"record-modes.html","id":"all","chapter":"25 Record modes","heading":"25.4 all","text":"record mode :Record new interactions.Never replay previously recorded interactions.can temporarily used force vcr re-record\ncassette (.e. ensure responses date)\ncan used simply want log HTTP requests.","code":""},{"path":"request-matching.html","id":"request-matching","chapter":"26 Request matching","heading":"26 Request matching","text":"match previously recorded requests, vcr try match new\nHTTP requests previously recorded one. default, match \nHTTP method (e.g., GET) URI (e.g., http://foo.com), following\nRuby’s VCR gem.can customize match requests one \nfollowing options, default, \ncan used together, alone.method: Use method request matcher match requests HTTP method\n(.e. GET, POST, PUT, DELETE, etc). generally want use\nmatcher. method matcher used (along uri matcher)\ndefault specify requests match.uri: Use uri request matcher match requests request URI. \nuri matcher used (along method matcher) default\nspecify requests match.host: Use host request matcher match requests request host.\ncan use (alone, combination path) \nalternative uri non-deterministic portions URI\nconsidered part request matching.path: Use path request matcher match requests path portion\nrequest URI. can use (alone, combination host)\nalternative uri non-deterministic portions URIquery: Use query request matcher match requests query string\nportion request URI. can use (alone, combination \nothers) alternative uri non-deterministic portions \nURI considered part request matching.body: Use body request matcher match requests request body.headers: Use headers request matcher match requests request headers.can set options tweaking match_requests_on parameter \nuse_cassette():","code":"\nlibrary(vcr)\nuse_cassette(name = \"foo_bar\", {\n cli$post(\"post\", body = list(a = 5))\n }, \n match_requests_on = c('method', 'headers', 'body')\n)"},{"path":"request-matching.html","id":"matching","chapter":"26 Request matching","heading":"26.1 Matching","text":"","code":""},{"path":"request-matching.html","id":"headers","chapter":"26 Request matching","heading":"26.1.1 headers","text":"","code":"\nlibrary(crul)\nlibrary(vcr)\ncli <- crul::HttpClient$new(\"https://httpbin.org/get\", \n headers = list(foo = \"bar\"))\nuse_cassette(name = \"nothing_new\", {\n one <- cli$get()\n }, \n match_requests_on = 'headers'\n)\ncli$headers$foo <- \"stuff\"\nuse_cassette(name = \"nothing_new\", {\n two <- cli$get()\n }, \n match_requests_on = 'headers'\n)\none$request_headers\ntwo$request_headers"},{"path":"debugging-your-tests-that-use-vcr.html","id":"debugging-your-tests-that-use-vcr","chapter":"27 Debugging your tests that use vcr","heading":"27 Debugging your tests that use vcr","text":"Sometimes tests using vcr cassette fail want debug .","code":""},{"path":"debugging-your-tests-that-use-vcr.html","id":"an-http-request-has-been-made-that-vcr-does-not-know-how-to-handle","chapter":"27 Debugging your tests that use vcr","heading":"27.1 An HTTP request has been made that vcr does not know how to handle","text":"get error starting “HTTP request made vcr know handle:” running tests,\nmeans code test makes HTTP request\nmatching information cassette using.\nmight added request, changed one slightly.easy fix : delete cassette re-run test re-record cassette.\nRun test second time ensure well.\n, escalate next paragraph.Maybe didn’t actually want change request making.\nMake sure requests contain something random, something related e.g. time now, URI (http://foo.com?time=13).\nmake sure things varying, might want use mocking (e.g. function returning current time), setting random seed, using withr (e.g. setting option certain value test).","code":""},{"path":"debugging-your-tests-that-use-vcr.html","id":"actual-debugging","chapter":"27 Debugging your tests that use vcr","heading":"27.1.1 Actual debugging","text":"Ideally want run code tests run inside tests,\nparticular, using vcr cassette.","code":""},{"path":"debugging-your-tests-that-use-vcr.html","id":"prepare-your-debugging-environment","chapter":"27 Debugging your tests that use vcr","heading":"27.1.2 Prepare your debugging environment","text":"first need load either vcr helper tests/testthat/helper-vcr.R (e.g. via devtools::load_all())\nsource vcr setup file tests/testthat/setup-vcr.R .e. file lines (maybe others)instead vcr::vcr_test_path(\"fixtures\") see \"../fixtures\",\nreplace \"../fixtures\" vcr::vcr_test_path(\"fixtures\"),\nvcr::vcr_test_path() function meant help exactly want:\npath tests/fixtures/ work tests root (running code debug ).one step (loading vcr helper sourcing vcr setup file),\nmaybe two (also replace \"../fixtures\" vcr::vcr_test_path(\"fixtures\")).","code":"\nlibrary(\"vcr\")\ninvisible(vcr::vcr_configure(\n dir = vcr::vcr_test_path(\"fixtures\"),\n filter_sensitive_data = list(\"<>\" = Sys.getenv('GITHUB_PAT'))\n))\nvcr::check_cassette_names()"},{"path":"debugging-your-tests-that-use-vcr.html","id":"debugging-itself","chapter":"27 Debugging your tests that use vcr","heading":"27.1.3 Debugging itself","text":"Now look test whose code trying debug e.g.want run code test,","code":"\nfoo <- function() crul::ok('https://httpbin.org/get')\n\ntest_that(\"foo works\", {\n vcr::use_cassette(\"testing\", {\n x <- foo()\n })\n expect_true(x)\n})\nfoo <- function() crul::ok('https://httpbin.org/get')\nvcr::insert_cassette(\"testing\") # it will be created if needed\nx <- foo()\nx\n# further interactive debugging and fixes\nvcr::eject_cassette(\"testing\")"},{"path":"debugging-your-tests-that-use-vcr.html","id":"logging-1","chapter":"27 Debugging your tests that use vcr","heading":"27.1.4 Logging","text":"can use vcr’s built logging help debugging process. configure logging,\nuse vcr_configure() function, set log=TRUE set options logging \nlog_opts parameter named list. See ?vcr_configure details., setting log file temporary file cleaned end\nR session. , file extension .log, file extension matter.log=TRUE can continue debugging. Open log file set text\neditor location; examine shell/terminal.example, running block aboveIf open log file ’ll see logs step vcr takes handling HTTP request.\nlogs information cassette used, exact time recorded, \nmatchers use, cassette options, request handled.Logging isn’t meant turned time - rather debugging/informational purposes.","code":"\nvcr::vcr_configure(\n dir = vcr::vcr_test_path(\"fixtures\"),\n log = TRUE,\n log_opts = list(file = file.path(tempdir(), \"vcr.log\"))\n)\nfoo <- function() crul::ok('https://httpbin.org/get')\n\ntest_that(\"foo works\", {\n vcr::use_cassette(\"testing\", {\n x <- foo()\n })\n expect_true(x)\n})[Cassette: 'testing'] - 2020-11-24 16:05:17 - Init. HTTPInteractionList w/ request matchers [method, uri] & 0 interaction(s): { }\n[Cassette: 'testing'] - 2020-11-24 16:05:17 - Initialized with options: {name: testing, record: once, serialize_with: yaml, persist_with: FileSystem, match_requests_on: c(\"method\", \"uri\"), update_content_length_header: FALSE, allow_playback_repeats: FALSE, preserve_exact_body_bytes: FALSE}\n[Cassette: 'testing'] - 2020-11-24 16:05:17 - Handling request: head https://httpbin.org/get (disabled: FALSE)\n[Cassette: 'testing'] - 2020-11-24 16:05:17 - Identified request type: (recordable) for head https://httpbin.org/get\n[Cassette: 'testing'] - 2020-11-24 16:05:17 - Recorded HTTP interaction: head https://httpbin.org/get => 200 "},{"path":"debugging-your-tests-that-use-vcr.html","id":"return-to-normal-development","chapter":"27 Debugging your tests that use vcr","heading":"27.1.5 Return to normal development","text":"Make sure ejected cassette using!Unless vcr helper/setup file tweaked things like,\neven need re-start R, , just safe side.","code":""},{"path":"vcr-security.html","id":"vcr-security","chapter":"28 Security with vcr","heading":"28 Security with vcr","text":"Refer security chapter general guidance.","code":""},{"path":"vcr-security.html","id":"api-keys-security","chapter":"28 Security with vcr","heading":"28.1 Keeping secrets safe","text":"keep secrets safe, need use parameters vcr::vcr_configure() tell vcr either secrets (put place), secrets (put place).\nbest know secrets used requests: e.g. API key passed header part query string?\nMaybe need different strategies different secrets (e.g. OAuth2.0 access token set Authorization header OAuth2.0 refresh token might query string).cases, crucial look cassettes putting public web, just sure got configuration right!","code":""},{"path":"vcr-security.html","id":"if-the-secret-is-in-a-request-header","chapter":"28 Security with vcr","heading":"28.1.1 If the secret is in a request header","text":"can use filter_request_headers!different ways use .","code":"\n# Remove one header from the cassettes\nvcr_configure(\n filter_request_headers = \"Authorization\"\n)\n\n# Remove two headers from the cassettes\nvcr_configure(\n filter_request_headers = c(\"Authorization\", \"User-Agent\")\n)\n\n# Replace one header with a given string\nvcr_configure(\n filter_request_headers = list(Authorization = \"<<>>\")\n)"},{"path":"vcr-security.html","id":"if-the-secret-is-in-a-response-header","chapter":"28 Security with vcr","heading":"28.1.2 If the secret is in a response header","text":"can use filter_response_headers works like filter_request_headers.","code":""},{"path":"vcr-security.html","id":"if-the-secret-is-somewhere-else","chapter":"28 Security with vcr","heading":"28.1.3 If the secret is somewhere else","text":"case need tell vcr secret string via filter_sensitive_data.\nwrite secret string directly configuration, ’d defeat purpose protecting !\nsecret environment variable instance tell vcr read .configuration parameter filter_sensitive_data accepts named list.element list following format:thing_to_replace_it_with = thing_to_replaceWe replace instances thing_to_replace thing_to_replace_it_with.recording (writing cassette) replacement \nreading cassette reverse replacement get back\nreal data.want make string replaces sensitive string something \nwon’t easily found elsewhere response body/headers/etc.","code":"\nvcr_configure(\n filter_sensitive_data = list(\"<<>>\" = Sys.getenv('API_KEY'))\n)"},{"path":"vcr-security.html","id":"different-api-keys","chapter":"28 Security with vcr","heading":"28.2 API keys and tests run in varied contexts","text":"real requests real API key needed.requests using cassettes fake API key needed fool package. \ndemo vcr set fake API key test setup file.","code":""},{"path":"vcr-security.html","id":"other-security","chapter":"28 Security with vcr","heading":"28.3 Other security","text":"Let us know security concerns! Surely ’s things haven’t\nconsidered yet.","code":""},{"path":"lightswitch.html","id":"lightswitch","chapter":"29 Turning vcr on and off","heading":"29 Turning vcr on and off","text":"Sometimes may need turn vcr, either individual\nfunction calls, individual test blocks, whole test files, \nentire package. following attempts break \noptions.vcr following four exported functions:turned_off() - Turns vcr duration code blockturn_off() - Turns vcr completely, longer handles every\nHTTP requestturn_on() - turns vcr ; opposite turn_off()turned_on() - Asks vcr turned , returns booleanInstead using four functions, use environment\nvariables achieve thing. way enable/disable\nvcr non-interactive environments continuous integration,\nDocker containers, running R non-interactively command line.\nfull set environment variables vcr uses, accept\nTRUE FALSE:VCR_TURN_OFF: turn vcr altogether; set TRUE skip vcr\nusage; default: FALSEVCR_TURNED_OFF: set turned_off internal package setting; \nturn vcr completely VCR_TURN_OFF , rather\nlooked together VCR_IGNORE_CASSETTESVCR_IGNORE_CASSETTES: set ignore_cassettes internal package\nsetting; looked together VCR_TURNED_OFF","code":""},{"path":"lightswitch.html","id":"turned-off","chapter":"29 Turning vcr on and off","heading":"29.1 turned_off","text":"turned_off() lets temporarily make real HTTP request without completely turning\nvcr , unloading , etc.happens internally turn vcr, run code block, exit\nturn vcr back - vcr turned duration \ncode block. Even code block errors, vcr turned back \ndue use .exit(turn_on())","code":"\nlibrary(vcr)\nlibrary(crul)\nturned_off({\n con <- HttpClient$new(url = \"https://httpbin.org/get\")\n con$get()\n})\n#> \n#> url: https://httpbin.org/get\n#> request_headers:\n#> User-Agent: libcurl/7.54.0 r-curl/4.3 crul/0.9.0\n#> Accept-Encoding: gzip, deflate\n#> Accept: application/json, text/xml, application/xml, */*\n#> response_headers:\n#> status: HTTP/1.1 200 OK\n#> date: Fri, 14 Feb 2020 19:44:46 GMT\n#> content-type: application/json\n#> content-length: 365\n#> connection: keep-alive\n#> server: gunicorn/19.9.0\n#> access-control-allow-origin: *\n#> access-control-allow-credentials: true\n#> status: 200"},{"path":"lightswitch.html","id":"turn-off-on","chapter":"29 Turning vcr on and off","heading":"29.2 turn_off/turn_on","text":"turn_off() different turned_off() turn_off() aimed\nsingle call block, rather turns vcr entire package.\nturn_off() check first turning vcr currently\ncassette use. turn_off() meant make R ignore vcr::insert_cassette()\nvcr::use_cassette() blocks test suite - letting code block\nrun wrapped vcr code - run\ntests cached requests/responses real HTTP requests toggle\nsingle R function environment variable.","code":"\nlibrary(vcr)\nvcr_configure(dir = tempdir())\n# real HTTP request works - vcr is not engaged here\ncrul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n# wrap HTTP request in use_cassette() - vcr is engaged here\nuse_cassette(\"foo_bar\", {\n crul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n})\n# turn off & ignore cassettes - use_cassette is ignored, real HTTP request made\nturn_off(ignore_cassettes = TRUE)\nuse_cassette(\"foo_bar\", {\n crul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n})\n# if you turn off and don't ignore cassettes, error thrown\nturn_off(ignore_cassettes = FALSE)\nuse_cassette(\"foo_bar\", {\n res2=crul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n})\n# vcr back on - now use_cassette behaves as before\nturn_on()\nuse_cassette(\"foo_bar3\", {\n res2=crul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n})"},{"path":"lightswitch.html","id":"turned-on","chapter":"29 Turning vcr on and off","heading":"29.3 turned_on","text":"turned_on() says tin - tells vcr turned \n.","code":"\nlibrary(vcr)\nturn_on()\nturned_on()#> [1] TRUE\nturn_off()\nturned_on()#> [1] FALSE"},{"path":"lightswitch.html","id":"lightswitch-env-vars","chapter":"29 Turning vcr on and off","heading":"29.4 Environment variables","text":"VCR_TURN_OFF environment variable can used within R command line\nturn vcr. example, can run tests package uses vcr, \nignore use_cassette/insert_cassette usage, running command\nline root package:, similarly within R:VCR_TURNED_OFF VCR_IGNORE_CASSETTES environment variables can used\ncombination achieve thing VCR_TURN_OFF:","code":"VCR_TURN_OFF=true Rscript -e \"devtools::test()\"\nSys.setenv(VCR_TURN_OFF = TRUE)\ndevtools::test()VCR_TURNED_OFF=true VCR_IGNORE_CASSETTES=true Rscript -e \"devtools::test()\""},{"path":"managing-cassettes.html","id":"managing-cassettes","chapter":"30 Managing cassettes","heading":"30 Managing cassettes","text":"","code":""},{"path":"managing-cassettes.html","id":"why-edit-cassettes","chapter":"30 Managing cassettes","heading":"30.1 Why edit cassettes?","text":"design vcr good recording HTTP interactions actually took place.\nNow sometimes testing/demo-ing package want use fake HTTP interactions.\ninstance:happens web API returns 503 code? informative error?happens returns 503 200 code? retry work?API returns much data even simple queries want make cassettes smaller?cases, can edit cassettes long aware risks!","code":""},{"path":"managing-cassettes.html","id":"risks-related-to-cassette-editing","chapter":"30 Managing cassettes","heading":"30.2 Risks related to cassette editing","text":"use vcr cassette replace 200 code 503 code, vcr turned ,\ntest fail API probably return error. Use vcr::skip_if_vcr_off().edit cassettes hand can’t re-record easily, ’d need re-record re-apply edits.Therefore ’ll need develop good workflow.","code":""},{"path":"managing-cassettes.html","id":"example-1-test-using-an-edited-cassette-with-a-503","chapter":"30 Managing cassettes","heading":"30.3 Example 1: test using an edited cassette with a 503","text":"First, write test e.g.run tests first time.failIt created cassette tests/fixtures/api-error.yml looks\nsomething likeYou can edit (new status code)run test , pass!\nNote use vcr::skip_if_vcr_off(): vcr turned , real\nAPI request probably request won’t get 503 status code.","code":"vcr::use_cassette(\"api-error\", {\n test_that(\"Errors are handled well\", {\n vcr::skip_if_vcr_off()\n expect_error(call_my_api()), \"error message\")\n })\n})http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '200'\n message: OK\n explanation: Request fulfilled, document follows\n headers:\n status: HTTP/1.1 200 OK\n connection: keep-alive\n body:\n encoding: UTF-8\n string: \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\"\n recorded_at: 2018-04-03 22:55:02 GMT\n recorded_with: vcr/0.1.0, webmockr/0.2.4, crul/0.5.2http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '503'"},{"path":"managing-cassettes.html","id":"the-same-thing-with-webmockr","chapter":"30 Managing cassettes","heading":"30.3.1 The same thing with webmockr","text":"advantage approach involving editing cassettes learn\none thing, vcr.\nNow, using webmockr directly tests, can also test \nbehavior package case errors.\nassume api_url() returns URL call_my_api() calls.big pro approach works even vcr turned .\ncon ’s quite different vcr syntax.","code":"test_that(\"Errors are handled well\", {\n webmockr::enable()\n stub <- webmockr::stub_request(\"get\", api_url())\n webmockr::to_return(stub, status = 503)\n expect_error(call_my_api()), \"error message\")\n webmockr::disable()\n\n})"},{"path":"managing-cassettes.html","id":"example-2-test-using-an-edited-cassette-with-a-503-then-a-200","chapter":"30 Managing cassettes","heading":"30.4 Example 2: test using an edited cassette with a 503 then a 200","text":"assume package contains sort retry.First, write test e.g.run tests first time.failIt created cassette tests/fixtures/api-error.yml looks\nsomething likeYou can duplicate HTTP interaction, make first one return 503 status code.\nvcr first use first interaction, second one, making request.run test , pass!\nNote use vcr::skip_if_vcr_off(): vcr turned , real\nAPI request probably request won’t get 503 status code.","code":"vcr::use_cassette(\"api-error\", {\n test_that(\"Errors are handled well\", {\n vcr::skip_if_vcr_off()\n expect_message(thing <- call_my_api()), \"retry message\")\n expect_s4_class(thing, \"data.frame\")\n })\n})http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '200'\n message: OK\n explanation: Request fulfilled, document follows\n headers:\n status: HTTP/1.1 200 OK\n connection: keep-alive\n body:\n encoding: UTF-8\n string: \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\"\n recorded_at: 2018-04-03 22:55:02 GMT\n recorded_with: vcr/0.1.0, webmockr/0.2.4, crul/0.5.2http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '503'\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '200'\n message: OK\n explanation: Request fulfilled, document follows\n headers:\n status: HTTP/1.1 200 OK\n connection: keep-alive\n body:\n encoding: UTF-8\n string: \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\"\n recorded_at: 2018-04-03 22:55:02 GMT\n recorded_with: vcr/0.1.0, webmockr/0.2.4, crul/0.5.2"},{"path":"managing-cassettes.html","id":"the-same-thing-with-webmockr-1","chapter":"30 Managing cassettes","heading":"30.4.1 The same thing with webmockr","text":"advantage approach involving editing cassettes learn\none thing, vcr.\nNow, using webmockr directly tests, can also test \nbehavior package case errors.\nassume api_url() returns URL call_my_api() calls.pro approach elegance stubbing, two different responses.\nwebmockr function like to_return() even argument times indicating \nnumber times given response returned.con top different vcr, case also needed\ngood response end (one 200 code, actual body), writing \nmock much cumbersome just recording vcr cassette.aware add cassettes either .gitignore /\n.Rbuildignore.","code":"test_that(\"Errors are handled well\", {\n webmockr::enable()\n stub <- webmockr::stub_request(\"get\", api_url())\n stub %>%\n to_return(status = 503) %>%\n to_return(status = 200, body = \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\", headers = list(b = 6))\n expect_message(thing <- call_my_api()), \"retry message\")\n expect_s4_class(thing, \"data.frame\")\n webmockr::disable()\n\n})"},{"path":"managing-cassettes.html","id":"gitignore-cassettes","chapter":"30 Managing cassettes","heading":"30.5 gitignore cassettes","text":".gitignore file lets tell [git][] files \nignore - files tracked git share git\nrepository public web, files .gitignore file\nwon’t shared public version.using vcr may want include cassettes \n.gitignore file. may wan cassettes contain sensitive\ndata don’t want internet & dont want hide\nfilter_sensitive_data.may want cassettes included GitHub repo, \npresent tests run CI, others run tests.’s correct answer whether gitignore cassettes.\nThink security implications whether want CI human\ncontributors use previously created cassettes create/use \n.","code":""},{"path":"managing-cassettes.html","id":"rbuildignore-cassettes","chapter":"30 Managing cassettes","heading":"30.6 Rbuildignore cassettes","text":".Rbuildignore file used tell R ignore\ncertain files/directories.’s clear use case ’d want add vcr cassettes\n.Rbuildignore file, aware affect\nvcr enabled tests.","code":""},{"path":"managing-cassettes.html","id":"sharing-cassettes","chapter":"30 Managing cassettes","heading":"30.7 sharing cassettes","text":"Sometimes may want share re-use cassettes across tests,\nexample reduce size package sources \ntest different functionality package functions\nmake query hood., can use cassette name multiple vcr::use_cassette()\ncalls.\nvcr::check_cassette_names() complain duplicate cassette\nnames, preventing accidentally re-using cassettes, however.\nallow duplicates, can provide character vector cassette names\nwant re-use allowed_duplicates argument \nvcr::check_cassette_names().\nway can use cassette across multiple tests.","code":""},{"path":"managing-cassettes.html","id":"deleting-cassettes","chapter":"30 Managing cassettes","heading":"30.8 deleting cassettes","text":"Removing cassette easy deleting file finder,\ncommand line, within text editor RStudio.delete cassette, next test run cassette \nrecorded .want re-record test cassette, instead \ndeleting file can toggle record modes.","code":""},{"path":"managing-cassettes.html","id":"cassette-file-types","chapter":"30 Managing cassettes","heading":"30.9 cassette file types","text":"right now persistence option yaml. files\n.yml extension.persister options added, additional file types\nmay found. next persister type likely JSON,\nuse option, ’d .json files instead \n.yml files.","code":""},{"path":"gotchas.html","id":"gotchas","chapter":"31 Gotchas","heading":"31 Gotchas","text":"’s things watch using vcr.Security: Don’t put secure API keys, tokens, etc. public web. See Security chapter vcr security chapter.API key issues: Running vcr enabled tests different contexts API keys used can rough edges.Dates: careful using dates tests vcr. e.g. generate todays date, pass function package uses date HTTP request, date different one matching cassette, causing vcr failure.HTTP errors: ’s good idea test failure behavior web service test suite. Sometimes vcr can handle sometimes . Open issues ideally think vcr handle cases HTTP failures.large response bodies: things large response bodies. First, vcr may give trouble large response bodies ’ve see yaml parsing problems already. Second, large response bodies means large cassettes disk - just aware file size ’s something matters . Third, large response bodies take longer load R, may still multi second test run even though test using cached HTTP response.Encoding: haven’t dealt encoding much yet , ’re likely run encoding issues. One blunt instrument now set preserve_exact_body_bytes = TRUE running vcr::use_cassette() vcr::insert_cassette(), stores response body base64.devtools::check vs. devtools::test: See (22.5.1)ignored files: See (30)","code":""},{"path":"gotchas.html","id":"line-identification","chapter":"31 Gotchas","heading":"31.1 Correct line identification","text":"get actual lines failures occur, can wrap test_that block use_cassette() block:put use_cassette() block inside, make sure put testthat expectations outside \nuse_cassette() block:wrap use_cassette() block inside test_that() block testthat expectations inside use_cassette() block, ’ll get line number use_cassette() block starts failures.","code":"\nlibrary(testthat)\nvcr::use_cassette(\"rl_citation\", {\n test_that(\"my test\", {\n aa <- rl_citation()\n\n expect_is(aa, \"character\")\n expect_match(aa, \"IUCN\")\n expect_match(aa, \"www.iucnredlist.org\")\n })\n})\nlibrary(testthat)\ntest_that(\"my test\", {\n vcr::use_cassette(\"rl_citation\", {\n aa <- rl_citation()\n })\n\n expect_is(aa, \"character\")\n expect_match(aa, \"IUCN\")\n expect_match(aa, \"www.iucnredlist.org\")\n})"},{"path":"session-info.html","id":"session-info","chapter":"32 Session info","heading":"32 Session info","text":"","code":""},{"path":"session-info.html","id":"session-info-1","chapter":"32 Session info","heading":"32.1 Session info","text":"None crul, webmockr, vcr, httptest compiled code, underlying dependency , curl . See curl’s README installation instructions case run curl related problems. webfakes compiled code.","code":"\nlibrary(\"magrittr\")\n\ndependencies <- attachment::att_from_rmds(\".\")\ndependencies <- dependencies[!dependencies %in% c(\"attachment\", \"bookdown\", \"knitr\")]\n\nsessioninfo::package_info(\n pkgs = dependencies\n ) %>%\n as.data.frame() %>%\n .[, c(\"package\", \"ondiskversion\")] %>%\n knitr::kable()"},{"path":"session-info.html","id":"full-session-info","chapter":"32 Session info","heading":"32.2 Full session info","text":"Session info bookPage found. Use table contents search bar find way back.","code":"\nsessioninfo::session_info()#> ─ Session info ───────────────────────────────────────────────────────────────\n#> setting value\n#> version R version 4.3.2 (2023-10-31)\n#> os Ubuntu 22.04.3 LTS\n#> system x86_64, linux-gnu\n#> ui X11\n#> language (EN)\n#> collate C.UTF-8\n#> ctype C.UTF-8\n#> tz UTC\n#> date 2024-02-02\n#> pandoc 2.19.2 @ /usr/bin/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────────────────────────\n#> package * version date (UTC) lib source\n#> attachment 0.4.1 2024-01-22 [1] RSPM\n#> base64enc 0.1-3 2015-07-28 [1] RSPM\n#> bookdown 0.37 2023-12-01 [1] RSPM\n#> brio 1.1.4 2023-12-10 [1] RSPM\n#> bslib 0.6.1 2023-11-28 [1] RSPM\n#> cachem 1.0.8 2023-05-01 [1] RSPM\n#> cli 3.6.2 2023-12-11 [1] RSPM\n#> crul * 1.4.0 2023-05-17 [1] RSPM\n#> curl 5.2.0 2023-12-08 [1] RSPM\n#> desc 1.4.3 2023-12-10 [1] RSPM\n#> digest 0.6.34 2024-01-11 [1] RSPM\n#> downlit 0.4.3 2023-06-29 [1] RSPM\n#> evaluate 0.23 2023-11-01 [1] RSPM\n#> fastmap 1.1.1 2023-02-24 [1] RSPM\n#> fauxpas 0.5.2 2023-05-03 [1] RSPM\n#> fs 1.6.3 2023-07-20 [1] RSPM\n#> gh 1.4.0 2023-02-22 [1] RSPM\n#> gitcreds 0.1.2 2022-09-08 [1] RSPM\n#> glue 1.7.0 2024-01-09 [1] RSPM\n#> highr 0.10 2022-12-22 [1] RSPM\n#> htmltools 0.5.7 2023-11-03 [1] RSPM\n#> httpcode 0.3.0 2020-04-10 [1] RSPM\n#> httr * 1.4.7 2023-08-15 [1] RSPM\n#> httr2 1.0.0 2023-11-14 [1] RSPM\n#> jquerylib 0.1.4 2021-04-26 [1] RSPM\n#> jsonlite 1.8.8 2023-12-04 [1] RSPM\n#> knitr 1.45 2023-10-30 [1] RSPM\n#> lifecycle 1.0.4 2023-11-07 [1] RSPM\n#> magrittr * 2.0.3 2022-03-30 [1] RSPM\n#> memoise 2.0.1 2021-11-26 [1] RSPM\n#> pkgload 1.3.4 2024-01-16 [1] RSPM\n#> purrr 1.0.2 2023-08-10 [1] RSPM\n#> R6 2.5.1 2021-08-19 [1] RSPM\n#> rappdirs 0.3.3 2021-01-31 [1] RSPM\n#> Rcpp 1.0.12 2024-01-09 [1] RSPM\n#> rlang 1.1.3 2024-01-10 [1] RSPM\n#> rmarkdown 2.25 2023-09-18 [1] RSPM\n#> roxygen2 7.3.1 2024-01-22 [1] RSPM\n#> rprojroot 2.0.4 2023-11-05 [1] RSPM\n#> rstudioapi 0.15.0 2023-07-07 [1] RSPM\n#> sass 0.4.8 2023-12-06 [1] RSPM\n#> sessioninfo 1.2.2 2021-12-06 [1] RSPM\n#> stringi 1.8.3 2023-12-11 [1] RSPM\n#> stringr 1.5.1 2023-11-14 [1] RSPM\n#> testthat * 3.2.1 2023-12-02 [1] RSPM\n#> triebeard 0.4.1 2023-03-04 [1] RSPM\n#> urltools 1.7.3 2019-04-14 [1] RSPM\n#> vcr * 1.2.2 2023-06-25 [1] RSPM\n#> vctrs 0.6.5 2023-12-01 [1] RSPM\n#> webmockr * 0.9.0 2023-02-28 [1] RSPM\n#> whisker 0.4.1 2022-12-05 [1] RSPM\n#> withr 3.0.0 2024-01-16 [1] RSPM\n#> xfun 0.41 2023-11-01 [1] RSPM\n#> xml2 1.3.6 2023-12-04 [1] RSPM\n#> yaml 2.3.8 2023-12-11 [1] RSPM\n#> \n#> [1] /home/runner/work/_temp/Library\n#> [2] /opt/R/4.3.2/lib/R/site-library\n#> [3] /opt/R/4.3.2/lib/R/library\n#> \n#> ──────────────────────────────────────────────────────────────────────────────"}] +[{"path":"index.html","id":"preamble","chapter":"1 Preamble","heading":"1 Preamble","text":"working R package accessing resources web, cat facts API, scientific data source system Customer relationship management?\npackages, appropriate unit testing can make code robust.\nunit testing package interacting web resources, however, brings special challenges:\ndependence tests good internet connection, testing absence authentication secrets, etc.\ntests fail due resources slow, development CRAN, means time loss everyone involved (slower development, messages CRAN).\nAlthough packages accessing remote resources well tested, lack resources around best practices.book meant free, central reference developers R packages accessing web resources, help faster robust development.\naim develop useful guide go great recent tools vcr, webmockr, httptest, httptest2 webfakes.expect know package development basics, git.Note related previous versions: book intended detailed guide using particular suite packages HTTP mocking testing R code /packages, namely maintained Scott Chamberlain (crul, webmockr, vcr), scope extended generalize explanation concepts similar packages.can also read PDF version epub version book.Thanks contributors book:\nAlex Whan,\nAurèle,\nChristophe Dervieux,\nDaniel Possenriede,\nHugo Gruson,\nJon Harmon,\nLluís Revilla Sancho,\nXavier .\nProject funded rOpenSci (Scott\nChamberlain’s work) & R\nConsortium (Maëlle Salmon’s work).\n","code":""},{"path":"http-in-r-101.html","id":"http-in-r-101","chapter":"2 HTTP in R 101","heading":"2 HTTP in R 101","text":"","code":""},{"path":"http-in-r-101.html","id":"what-is-http","chapter":"2 HTTP in R 101","heading":"2.1 What is HTTP?","text":"HTTP means HyperText Transport Protocol, probably just looking translation abbreviation.\nHTTP way exchange information remote server.\npackage, information going back forth R session internet, using sort HTTP tooling.\npackage making requests receives responses.","code":""},{"path":"http-in-r-101.html","id":"http-requests","chapter":"2 HTTP in R 101","heading":"2.1.1 HTTP requests","text":"HTTP request package makes.\nmethod (fetching information via GET? sending information via POST?), different parts URL (domain, endpoint, query string), headers (containing instance secret identifiers).\ncan contain body. instance, might sending data JSON.\ncase one headers describe content.know request make package?\nHopefully interacting well documented web resource explain methods associated endpoints.","code":""},{"path":"http-in-r-101.html","id":"http-responses","chapter":"2 HTTP in R 101","heading":"2.1.2 HTTP responses","text":"HTTP response remote server provides, package parses.\nresponse status code indicating whether request succeeded, response headers, (optionally) response body.Hopefully documentation web API web resource working shows good examples responses.\ncase ’ll find experimenting different requests see response “looks like”.","code":""},{"path":"http-in-r-101.html","id":"more-resources-about-http","chapter":"2 HTTP in R 101","heading":"2.1.3 More resources about HTTP","text":"get started interacting HTTP R?","code":""},{"path":"http-in-r-101.html","id":"general-http-resources","chapter":"2 HTTP in R 101","heading":"2.1.3.1 General HTTP resources","text":"Mozilla Developer Network docs HTTP (recommended zine mentioned hereafter)(free) Julia Evans’ Zine “HTTP: Learn browser’s language!”docs web API aiming work , search engine understand words new.","code":""},{"path":"http-in-r-101.html","id":"http-with-r","chapter":"2 HTTP in R 101","heading":"2.1.3.2 HTTP with R","text":"docs R package end choosing!Digging source code another package similar things.","code":""},{"path":"http-in-r-101.html","id":"http-requests-in-r-what-package","chapter":"2 HTTP in R 101","heading":"2.2 HTTP requests in R: what package?","text":"R, interact web resources, recommended use curl; higher-level interfaces httr (pronounced hitter h-t-t-r), httr2 crul.use RCurl, actively maintained!writing package interacting web resources, probably use httr2, httr crul.httr popular oldest three packages, supports OAuth.\nhttr docs feature vignette called Best practices API packageshttr popular oldest three packages, supports OAuth.\nhttr docs feature vignette called Best practices API packageshttr2 “ground-rewrite httr provides pipeable API explicit request object solves problems felt packages wrap APIs (e.g. built-rate-limiting, retries, OAuth, secure secrets, )” might good idea adopt rather httr new package. vignette Wrapping APIs.httr2 “ground-rewrite httr provides pipeable API explicit request object solves problems felt packages wrap APIs (e.g. built-rate-limiting, retries, OAuth, secure secrets, )” might good idea adopt rather httr new package. vignette Wrapping APIs.crul support OAuth uses object-oriented interface, might like.\ncrul set clients, ways perform requests, might handy. crul also vignette API package best practices.crul support OAuth uses object-oriented interface, might like.\ncrul set clients, ways perform requests, might handy. crul also vignette API package best practices.try programmatically access status GitHub, open-source platform provided company name.\naccess information httr2 crul\ndecide try low-level curl, feel free contribute example.\ninternet enough examples httr.URL leaves doubt format data provided , JSON!Let’s first use httr2.Now, crul.Hopefully short snippets give idea syntax expect choosing one packages.Note choice package constrain HTTP testing tools can use.\nHowever, general ideas remain .\nswitch package backend , say, crul httr without changing tests, tests test many specificities internals.","code":"\ngithub_url <- \"https://kctbh9vrtdwd.statuspage.io/api/v2/status.json\"\nlibrary(\"magrittr\")\nresponse <- httr2::request(github_url) %>%\n httr2::req_perform()\n\n# Check the response status\nhttr2::resp_status(response)## [1] 200\n# Or in a package you'd write\nhttr2::resp_check_status(response)\n\n# Parse the content\nhttr2::resp_body_json(response)## $page\n## $page$id\n## [1] \"kctbh9vrtdwd\"\n## \n## $page$name\n## [1] \"GitHub\"\n## \n## $page$url\n## [1] \"https://www.githubstatus.com\"\n## \n## $page$time_zone\n## [1] \"Etc/UTC\"\n## \n## $page$updated_at\n## [1] \"2024-02-02T07:44:51.526Z\"\n## \n## \n## $status\n## $status$indicator\n## [1] \"none\"\n## \n## $status$description\n## [1] \"All Systems Operational\"\n# In case you wonder, the format was obtained from a header\nhttr2::resp_header(response, \"content-type\")## [1] \"application/json; charset=utf-8\"\n# Create a client and get a response\nclient <- crul::HttpClient$new(github_url)\nresponse <- client$get()\n\n# Check the response status\nresponse$status_http()## \n## Message: OK\n## Explanation: Request fulfilled, document follows\n# Or in a package you'd write\nresponse$raise_for_status()\n\n# Parse the content\nresponse$parse()## No encoding supplied: defaulting to UTF-8.## [1] \"{\\\"page\\\":{\\\"id\\\":\\\"kctbh9vrtdwd\\\",\\\"name\\\":\\\"GitHub\\\",\\\"url\\\":\\\"https://www.githubstatus.com\\\",\\\"time_zone\\\":\\\"Etc/UTC\\\",\\\"updated_at\\\":\\\"2024-02-02T07:44:51.526Z\\\"},\\\"status\\\":{\\\"indicator\\\":\\\"none\\\",\\\"description\\\":\\\"All Systems Operational\\\"}}\"\njsonlite::fromJSON(response$parse())## No encoding supplied: defaulting to UTF-8.## $page\n## $page$id\n## [1] \"kctbh9vrtdwd\"\n## \n## $page$name\n## [1] \"GitHub\"\n## \n## $page$url\n## [1] \"https://www.githubstatus.com\"\n## \n## $page$time_zone\n## [1] \"Etc/UTC\"\n## \n## $page$updated_at\n## [1] \"2024-02-02T07:44:51.526Z\"\n## \n## \n## $status\n## $status$indicator\n## [1] \"none\"\n## \n## $status$description\n## [1] \"All Systems Operational\""},{"path":"graceful.html","id":"graceful","chapter":"3 Graceful HTTP R packages","heading":"3 Graceful HTTP R packages","text":"Based previous chapter, package interacting web resource dependency curl, httr, httr2 crul. hopefully read docs dependency chose, including, case httr, httr2 crul, vignette best practices HTTP packages. Now, chapter want give tips aimed making HTTP R package graceful, part ’ll learn book!write graceful HTTP R package? First , graceful nice adjective. 💃🕺Second, graceful adjective used CRAN repository policy “Packages use Internet resources fail gracefully informative message resource available changed (give check warning error).” Therefore, let’s review make R package graceful day forward, success failure.","code":""},{"path":"graceful.html","id":"choose-the-http-resource-wisely","chapter":"3 Graceful HTTP R packages","heading":"3.1 Choose the HTTP resource wisely","text":"First , life life package’s users easier web service ’re wrapping well maintained well documented. choice, try rely fragile web service. Moreover, can, try communicate API providers (telling package; reporting feature requests bug reports preferred way).","code":""},{"path":"graceful.html","id":"user-facing-grace-how-your-package-actually-works","chapter":"3 Graceful HTTP R packages","heading":"3.2 User-facing grace (how your package actually works)","text":"can, request API every time user asks something; cache data instead. API call, API call failure! 😉 See R-hub blog post “Caching results functions R package”. remember answers across sessions, see approaches presented R-hub blog post “Persistent config data R packages”. Caching behavior well documented users, probably expiration time caches ’s based often data updated remote service.can, request API every time user asks something; cache data instead. API call, API call failure! 😉 See R-hub blog post “Caching results functions R package”. remember answers across sessions, see approaches presented R-hub blog post “Persistent config data R packages”. Caching behavior well documented users, probably expiration time caches ’s based often data updated remote service.Try send correct requests knowing API expects validating user inputs; correct rate.Try send correct requests knowing API expects validating user inputs; correct rate.instance, don’t even try interacting web API requiring authentication user provide authentication information.limiting rate (sending many requests), automatically wait. API docs allow define ideal maximal rate, set request rate advance using ratelimitr package (, httr2, httr2::req_throttle()).’s status API (separate API indicating whether web resource ), use . tells API , stop() (rlang::abort()) informative error message.’s status API (separate API indicating whether web resource ), use . tells API , stop() (rlang::abort()) informative error message.API indicates error, depending actual error,\nserver seems issues, re-try exponential back-. httr2 httr2::req_retry().\nOtherwise, transform error useful error.\nused retry nothing sent maximal number retries, show informative error message.\nAPI indicates error, depending actual error,server seems issues, re-try exponential back-. httr2 httr2::req_retry().server seems issues, re-try exponential back-. httr2 httr2::req_retry().Otherwise, transform error useful error.Otherwise, transform error useful error.used retry nothing sent maximal number retries, show informative error message.used retry nothing sent maximal number retries, show informative error message.aspects user care . Now, might problematic package’s fate CRAN automatic checks happen submission regularly.","code":""},{"path":"graceful.html","id":"graceful-vignettes-and-examples","chapter":"3 Graceful HTTP R packages","heading":"3.3 Graceful vignettes and examples","text":"Pre-compute vignettes way. Don’t use tests; showcase. course system prevent going stale, maybe even simple reminders (potentially unexported release_questions() function). Don’t let vignettes run system failure bad consequences.Don’t run examples CRAN. Now, first submission, CRAN maintainers might complain example. case, might want add minimal example, e.g.two precautions ensure CRAN checks won’t end WARNINGs, e.g. example failed API .","code":"\nif (crul::ok(\"some-url\")) {\n my_fun() # some eg that uses some-url\n}"},{"path":"graceful.html","id":"graceful-code","chapter":"3 Graceful HTTP R packages","heading":"3.4 Graceful code","text":"simplifying life contributors, make sure re-use code package e.g. defining helper functions making requests, handling responses etc.\nmake easier support interactions parts web API.\nWriting DRY (don’t repeat ) code means less lines code test, less API calls make fake!Also, export function à la gh::gh(), ’ll help users call endpoint web API even haven’t written high-level helper yet.","code":""},{"path":"graceful.html","id":"graceful-tests","chapter":"3 Graceful HTTP R packages","heading":"3.5 Graceful tests","text":"’re getting closer actual topic book!Read rest book! tests ideally run without needing actual internet connection API . tests need interact API skipped CRAN. testthat::skip_on_cran() (skip_if_offline() skips test run offline CRAN) ensure .test “success” behavior! Test behavior package case API errors, shall also covered later book.","code":""},{"path":"graceful.html","id":"conclusion","chapter":"3 Graceful HTTP R packages","heading":"3.6 Conclusion","text":"summary, graceful HTTP package, make current best practice user interface; escape examples vignettes CRAN; make tests independent actual HTTP requests. forget CRAN’s “graceful failure” policy mostly ensuring clean R CMD check result CRAN platforms (0 ERROR, 0 WARNING, 0 NOTE) even web service ’re wrapping hiccups.","code":""},{"path":"pkgs-testing-chapter.html","id":"pkgs-testing-chapter","chapter":"4 Packages for HTTP testing","heading":"4 Packages for HTTP testing","text":"brief presentation packages ’ll “meet” later book!","code":""},{"path":"pkgs-testing-chapter.html","id":"why-do-we-need-special-packages-for-http-testing","chapter":"4 Packages for HTTP testing","heading":"4.1 Why do we need special packages for HTTP testing?","text":"Packages HTTP testing useful challenges HTTP testing.\nPackages HTTP testing help solve challenges, rather letting solve homegrown solutions (can still choose , course).challenges HTTP testing?tests depend internet connection ideal.tests depend secrets authentication hand ideal.tests situations hard trigger (e.g. failure remote server) tricky.","code":""},{"path":"pkgs-testing-chapter.html","id":"webmockr","chapter":"4 Packages for HTTP testing","heading":"4.2 webmockr","text":"webmockr, maintained Scott Chamberlain, R package help “mock” HTTP requests. “mock” mean? Mock refers fact ’re faking response. works:“stub” request. , set rules HTTP request ’d like respond fake response. E.g. rule might method, URL.also can set rules fake response ’d like respond , anything (nothing, give NULL).make HTTP requests, match stub .e. set rules return requested returned.webmockr use, real HTTP interactions allowed. Therefore need stub possible HTTP requests happening via code. ’ll get error messages HTTP requests covered stub.recording interactions disk , just mocked responses given user specifies R session.webmockr works crul package httr package.webmockr quite low-level first tool ’ll use directly day--day HTTP testing.\nmay never use directly use vcr ’s one foundations.webmockr inspired Ruby webmock gem.","code":""},{"path":"pkgs-testing-chapter.html","id":"what-vcr","chapter":"4 Packages for HTTP testing","heading":"4.3 What is vcr?","text":"short version vcr, maintained Scott Chamberlain, helps stub HTTP requests don’t repeat HTTP requests, mostly unit tests.\nuses power webmockr, higher level interface.using vcr tests, first time run test, API response stored YAML JSON file.\nsubsequent runs test use local file instead really calling API.\nTherefore tests work independently internet connection.vcr inspired Ruby vcr gem.vcr works packages using httr crul.Direct link {vcr} (& {webmockr}) demo","code":""},{"path":"pkgs-testing-chapter.html","id":"what-is-httptest","chapter":"4 Packages for HTTP testing","heading":"4.4 What is httptest?","text":"httptest, maintained Neal Richardson, uses mocked API responses (like vcr).\n“enables one test logic R sides API package without requiring access remote service.”Contrary vcr, httptest also lets define mock files hand (copying API docs, dumbing real responses), whereas vcr mock files come recording real interactions (although can choose edit {vcr} mock files recording).httptest works packages using httr.Direct link {httptest} demoThe differences similarities httptest vcr become clearer chapters provide whole games .","code":""},{"path":"pkgs-testing-chapter.html","id":"what-is-httptest2","chapter":"4 Packages for HTTP testing","heading":"4.5 What is httptest2?","text":"httptest2, maintained Neal Richardson, like httptest, httr2.Direct link {httptest2} demoWith vcr, httptest httptest2 tests use sort fake API responses.vcr called fixtures cassettes.\nhttptest httptest2 called mock files.","code":""},{"path":"pkgs-testing-chapter.html","id":"what-is-webfakes","chapter":"4 Packages for HTTP testing","heading":"4.6 What is webfakes?","text":"webfakes, maintained Gábor Csárdi, provides alternative (complementary?) tool HTTP testing.\nlet fake whole web service, potentially outputting responses mock files ’ll created.\nhelp recording fake responses.\nruns fake web service, can even interact said web service browser curl command line.webfakes works packages using HTTP package (.e. works curl, crul, httr, httr2).Direct link {webfakes} demo","code":""},{"path":"pkgs-testing-chapter.html","id":"testthat","chapter":"4 Packages for HTTP testing","heading":"4.7 testthat","text":"testthat, maintained Hadley Wickham, package specifically HTTP testing; package general-purpose unit testing R packages.\nbook assume use, popularity.use alternative like tinytest,httptest won’t work ’s specifically designed complement testthat;httptest won’t work ’s specifically designed complement testthat;vcr might work;vcr might work;webfakes can work.webfakes can work.","code":""},{"path":"pkgs-testing-chapter.html","id":"conclusion-1","chapter":"4 Packages for HTTP testing","heading":"4.8 Conclusion","text":"Now idea tools can use HTTP testing, ’ll now create minimal package amend three versions tested withvcr webmockr;httptest;httptest2;webfakes.minimal package use httr (except httptest2, ’ll use httr2). However, help understand concepts even end using crul curl.1","code":""},{"path":"introduction.html","id":"introduction","chapter":"5 Introduction","heading":"5 Introduction","text":"Similar Whole Game chapter R packages book Hadley Wickham Jenny Bryan, shall go add HTTP tests minimal package.\nHowever, three times present alternative approaches: vcr, httptest, webfakes.\nexercise, shall compare approaches: compare packages involve mocking .e. vcr vs. httptest; three HTTP packages last chapter.\nnext section present single topics “deal authentication” details.","code":""},{"path":"introduction.html","id":"our-example-packages","chapter":"5 Introduction","heading":"5.1 Our example packages","text":"minimal packages, exemplighratia exemplighratia2, access GitHub status API one endpoint GitHub V3 REST API.\nnamed Latin phrase exempli gratia means “instance”, H GH.\nreally need interact GitHub V3 API, recommend gh package.\nalso recommend looking source gh package, docs GitHub V3 API, particular authentication.example packages call web APIs tools concepts applicable packages wrapping web resource, even poorly documented ones.2GitHub V3 API works without authentication , lower rate.\nsake example package requiring authentication shall assume API usable without authentication.\nAuthentication , , setting token HTTP header (quite simple, compared e.g. OAuth).GitHub Status API, contrary, necessitate authentication .shall create two functions, one works without authentication, one works authentication.create packages?\nobviously free use favorite workflow tools, share workflow using usethis package.followed usethis setup article.","code":""},{"path":"introduction.html","id":"exemplighratia2-httr2","chapter":"5 Introduction","heading":"5.1.1 exemplighratia2 (httr2)","text":"ranusethis::create_package(\"path//folder/exemplighratia2\") create open package project;usethis::use_mit_license() add MIT license;usethis::use_package(\"httr2\") add dependency httr2;usethis::use_package(\"purrr\") add dependency purrr;use_r(\"api-status.R\") add first function whose code written ;use_test(\"api-status\") (using testthat latest edition setting Config/testthat/edition: 3 DESCRIPTION) add simple test whose code .use_r(\"organizations.R\") add second function. Note ideal version function sort callback retry, call gh_api_status() function (maybe httr2::req_retry()’s is_transient argument).use_test(\"organizations\") add simple test.","code":"\nstatus_url <- function() {\n \"https://kctbh9vrtdwd.statuspage.io/api/v2/components.json\"\n}\n\n#' GitHub APIs status\n#'\n#' @description Get the status of requests to GitHub APIs\n#'\n#' @importFrom magrittr `%>%`\n#'\n#' @return A character vector, one of \"operational\", \"degraded_performance\",\n#' \"partial_outage\", or \"major_outage.\"\n#'\n#' @details See details in https://www.githubstatus.com/api#components.\n#' @export\n#'\n#' @examples\n#' \\dontrun{\n#' gh_api_status()\n#' }\ngh_api_status <- function() {\n response <- status_url() %>%\n httr2::request() %>%\n httr2::req_perform()\n\n # Check status\n httr2::resp_check_status(response)\n\n # Parse the content\n content <- httr2::resp_body_json(response)\n\n # Extract the part about the API status\n components <- content$components\n api_status <- components[purrr::map_chr(components, \"name\") == \"API Requests\"][[1]]\n\n # Return status\n api_status$status\n\n}\ntest_that(\"gh_api_status() works\", {\n testthat::expect_type(gh_api_status(), \"character\")\n})\ngh_v3_url <- function() {\n \"https://api.github.com/\"\n}\n\n#' GitHub organizations\n#'\n#' @description Get logins of GitHub organizations.\n#'\n#' @param since The integer ID of the last organization that you've seen.\n#'\n#' @return A character vector of at most 30 elements.\n#' @export\n#'\n#' @details Refer to https://developer.github.com/v3/orgs/#list-organizations\n#'\n#' @examples\n#' \\dontrun{\n#' gh_organizations(since = 42)\n#' }\ngh_organizations <- function(since = 1) {\n\n token <- Sys.getenv(\"GITHUB_PAT\")\n\n if (!nchar(token)) {\n stop(\"No token provided! Set up the GITHUB_PAT environment variable please.\")\n }\n\n response <- httr2::request(gh_v3_url()) %>%\n httr2::req_url_path_append(\"organizations\") %>%\n httr2::req_url_query(since = since) %>%\n httr2::req_headers(\"Authorization\" = paste(\"token\", token)) %>%\n httr2::req_retry(max_tries = 3, max_seconds = 120) %>%\n httr2::req_perform()\n\n httr2::resp_check_status(response)\n\n content <- httr2::resp_body_json(response)\n\n purrr::map_chr(content, \"login\")\n\n}\ntest_that(\"gh_organizations works\", {\n testthat::expect_type(gh_organizations(), \"character\")\n})"},{"path":"introduction.html","id":"exemplighratia-httr","chapter":"5 Introduction","heading":"5.1.2 exemplighratia (httr)","text":"ranusethis::create_package(\"path//folder/exemplighratia\") create open package project;usethis::use_mit_license() add MIT license;usethis::use_package(\"httr\") add dependency httr;usethis::use_package(\"purrr\") add dependency purrr;usethis::use_r(\"api-status.R\") add first function whose code written ;use_test(\"api-status\") (using testthat latest edition setting Config/testthat/edition: 3 DESCRIPTION) add simple test whose code .usethis::use_r(\"organizations.R\") add second function. Note ideal version function sort callback retry, call gh_api_status() function (seems easier implement crul’s retry method).use_test(\"organizations\") add simple test.","code":"\nstatus_url <- function() {\n \"https://kctbh9vrtdwd.statuspage.io/api/v2/components.json\"\n}\n\n#' GitHub APIs status\n#'\n#' @description Get the status of requests to GitHub APIs\n#'\n#' @return A character vector, one of \"operational\", \"degraded_performance\",\n#' \"partial_outage\", or \"major_outage.\"\n#'\n#' @details See details in https://www.githubstatus.com/api#components.\n#' @export\n#'\n#' @examples\n#' \\dontrun{\n#' gh_api_status()\n#' }\ngh_api_status <- function() {\n response <- httr::GET(status_url())\n\n # Check status\n httr::stop_for_status(response)\n\n # Parse the content\n content <- httr::content(response)\n\n # Extract the part about the API status\n components <- content$components\n api_status <- components[purrr::map_chr(components, \"name\") == \"API Requests\"][[1]]\n\n # Return status\n api_status$status\n\n}\ntest_that(\"gh_api_status() works\", {\n testthat::expect_type(gh_api_status(), \"character\")\n})\ngh_v3_url <- function() {\n \"https://api.github.com/\"\n}\n\n#' GitHub organizations\n#'\n#' @description Get logins of GitHub organizations.\n#'\n#' @param since The integer ID of the last organization that you've seen.\n#'\n#' @return A character vector of at most 30 elements.\n#' @export\n#'\n#' @details Refer to https://developer.github.com/v3/orgs/#list-organizations\n#'\n#' @examples\n#' \\dontrun{\n#' gh_organizations(since = 42)\n#' }\ngh_organizations <- function(since = 1) {\n url <- httr::modify_url(\n gh_v3_url(),\n path = \"organizations\",\n query = list(since = since)\n )\n\n token <- Sys.getenv(\"GITHUB_PAT\")\n\n if (!nchar(token)) {\n stop(\"No token provided! Set up the GITHUB_PAT environment variable please.\")\n }\n\n response <- httr::RETRY(\n \"GET\",\n url,\n httr::add_headers(\"Authorization\" = paste(\"token\", token))\n )\n\n httr::stop_for_status(response)\n\n content <- httr::content(response)\n\n purrr::map_chr(content, \"login\")\n\n}\ntest_that(\"gh_organizations works\", {\n testthat::expect_type(gh_organizations(), \"character\")\n})"},{"path":"introduction.html","id":"conclusion-2","chapter":"5 Introduction","heading":"5.2 Conclusion","text":"good, now package 100% test coverage passes R CMD Check (granted, tests thorough, remember minimal example).\ntry working without connection?\nfollowing chapters, ’ll add robust testing infrastructure minimal package, four times compare packages/approaches: vcr, httptest, httptest2, webfakes.","code":""},{"path":"vcr.html","id":"vcr","chapter":"6 Use vcr (& webmockr)","heading":"6 Use vcr (& webmockr)","text":"chapter aim adding HTTP testing infrastructure exemplighratia using vcr (& webmockr).Corresponding pull request exemplighratia. Feel free fork repository experiment !","code":""},{"path":"vcr.html","id":"setup","chapter":"6 Use vcr (& webmockr)","heading":"6.1 Setup","text":"working , need install vcr.First, need run vcr::use_vcr() (exemplighratia directory) effects:Adding vcr dependency DESCRIPTION, Suggests just like testthat.Creating example test file us look . useful first times setup vcr another package, might even delete without reading .Adding .gitattributes file line tests/fixtures/**/* -diff hide changes cassettes git diff. makes git diff easier deal . 3Creating setup file tests/testthat/helper-vcr.R,testthat runs tests, files whose name start “helper” always run first.\nalso loaded devtools::load_all(), vcr setup loaded developing testing interactively.\nSee table R-hub blog post “Helper code files testthat tests”.helper file created vcrloads vcr,indicates mocked responses saved (\"../fixtures\" translates, root package, tests/fixtures),checks using name twice cassettes (mock files).tweak vcr setup bit needs.want API token appear mock responses, know ’s used Authorization header requests, use filter_request_headers argument vcr::vcr_configure(). secret filtering one can use filter_response_headers filter_sensitive_data (regular expression purging, whole saved interactions).want API token appear mock responses, know ’s used Authorization header requests, use filter_request_headers argument vcr::vcr_configure(). secret filtering one can use filter_response_headers filter_sensitive_data (regular expression purging, whole saved interactions).need ensure set fake API key API token around.\n? remember well, code function gh_organizations() checks presence token.\nmock responses around, don’t need token still need fool package contexts token (e.g. continuous integration checks fork GitHub repository).need ensure set fake API key API token around.\n? remember well, code function gh_organizations() checks presence token.\nmock responses around, don’t need token still need fool package contexts token (e.g. continuous integration checks fork GitHub repository).updated setup file saved tests/testthat/helper-vcr.R.just setup, now adapting tests!","code":"\nlibrary(\"vcr\")\ninvisible(vcr::vcr_configure(\n dir = vcr::vcr_test_path(\"fixtures\")\n))\nvcr::check_cassette_names()\nlibrary(\"vcr\")\n\nvcr_dir <- vcr::vcr_test_path(\"fixtures\")\n\nif (!nzchar(Sys.getenv(\"GITHUB_PAT\"))) {\n if (dir.exists(vcr_dir)) {\n # Fake API token to fool our package\n Sys.setenv(\"GITHUB_PAT\" = \"foobar\")\n } else {\n # If there's no mock files nor API token, impossible to run tests\n stop(\"No API key nor cassettes, tests cannot be run.\",\n call. = FALSE)\n }\n}\n\ninvisible(vcr::vcr_configure(\n dir = vcr_dir,\n # Filter the request header where the token is sent, make sure you know\n # how authentication works in your case and read the Security chapter :-)\n filter_request_headers = list(Authorization = \"My bearer token is safe\")\n))"},{"path":"vcr.html","id":"actual-testing","chapter":"6 Use vcr (& webmockr)","heading":"6.2 Actual testing","text":"important function vcr::use_cassette(\"cassette-informative--unique-name\", {code-block}) tells vcr create mock file store API responses API calls occurring code block.Let’s tweak test gh_api_status, now becomesWe wrap code involving interactions API, status <- gh_api_status(), vcr::use_cassette().run test (RStudio clicking “Run test”),first time, vcr creates cassette (mock file) tests/testthat/fixtures/gh_api_status.yml stores API response.\ncontains information related requests responses, headers included.times , unless delete mock file, vcr simply uses mock files instead actually calling API.Let’s tweak test, gh_organizations().\nthings get exciting complicated, also set adding test error behavior.\ninspired us change error behavior bit slightly specific error message .e. httr::stop_for_status(response) became httr::stop_for_status(response, task = \"get data API, oops\").test file tests/testthat/test-organizations.R now:first test similar gh_api_status().\nsecond test unpack.enable use webmockr beginning webmockr::enable(). webmockr? can help mock failure scenario.explicitly write request https://api.github.com/organizations?since=1 return status 502.test error message expect_error(gh_organizations(), \"oops\").disable webmockr webmockr::disable().Instead using webmockr creating fake API eror, haverecorded normal cassette;edited replace status code.Read pros cons approach vcr vignette edit vcr cassettes?, especially don’t find webmockr approach enjoyable.Without HTTP testing infrastructure, testing behavior package case API errors difficult.Regarding secret API token, first time run test file, vcr creates cassette notice linesOur API token replaced string indicated vcr::vcr_configure(), bearer token safe.","code":"\ntest_that(\"gh_api_status() works\", {\n vcr::use_cassette(\"gh_api_status\", {\n status <- gh_api_status()\n })\n testthat::expect_type(status, \"character\")\n})http_interactions:\n- request:\n method: get\n uri: https://kctbh9vrtdwd.statuspage.io/api/v2/components.json\n body:\n encoding: ''\n string: ''\n headers:\n Accept: application/json, text/xml, application/xml, */*\n response:\n status:\n status_code: 200\n category: Success\n reason: OK\n message: 'Success: (200) OK'\n headers:\n vary: Accept,Accept-Encoding,Fastly-SSL\n cache-control: max-age=0, private, must-revalidate\n x-cache: MISS\n content-type: application/json; charset=utf-8\n content-encoding: gzip\n strict-transport-security: max-age=259200\n date: Thu, 15 Oct 2020 11:59:23 GMT\n x-request-id: d9888435-3f04-4401-be5c-b9d1bfdfa015\n x-download-options: noopen\n x-xss-protection: 1; mode=block\n x-runtime: '0.037254'\n x-permitted-cross-domain-policies: none\n access-control-allow-origin: '*'\n accept-ranges: bytes\n x-content-type-options: nosniff\n etag: W/\"gz[a479c9894f51b7db286dc31cd922e7bf]\"\n x-statuspage-skip-logging: 'true'\n x-statuspage-version: fd137a4bb14c20ce721393e5b6540ea6eebff3a3\n referrer-policy: strict-origin-when-cross-origin\n age: '0'\n body:\n encoding: UTF-8\n file: no\n string: '{\"page\":{\"id\":\"kctbh9vrtdwd\",\"name\":\"GitHub\",\"url\":\"https://www.githubstatus.com\",\"time_zone\":\"Etc/UTC\",\"updated_at\":\"2020-10-15T08:57:35.302Z\"},\"components\":[{\"id\":\"8l4ygp009s5s\",\"name\":\"Git\n Operations\",\"status\":\"operational\",\"created_at\":\"2017-01-31T20:05:05.370Z\",\"updated_at\":\"2020-09-24T02:32:00.916Z\",\"position\":1,\"description\":\"Performance\n of git clones, pulls, pushes, and associated operations\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"brv1bkgrwx7q\",\"name\":\"API\n Requests\",\"status\":\"operational\",\"created_at\":\"2017-01-31T20:01:46.621Z\",\"updated_at\":\"2020-09-30T19:00:29.476Z\",\"position\":2,\"description\":\"Requests\n for GitHub APIs\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"4230lsnqdsld\",\"name\":\"Webhooks\",\"status\":\"operational\",\"created_at\":\"2019-11-13T18:00:24.256Z\",\"updated_at\":\"2020-10-13T14:51:17.928Z\",\"position\":3,\"description\":\"Real\n time HTTP callbacks of user-generated and system events\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"0l2p9nhqnxpd\",\"name\":\"Visit\n www.githubstatus.com for more information\",\"status\":\"operational\",\"created_at\":\"2018-12-05T19:39:40.838Z\",\"updated_at\":\"2020-04-02T21:56:21.954Z\",\"position\":4,\"description\":null,\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"kr09ddfgbfsf\",\"name\":\"Issues\",\"status\":\"operational\",\"created_at\":\"2017-01-31T20:01:46.638Z\",\"updated_at\":\"2020-10-10T00:02:16.199Z\",\"position\":5,\"description\":\"Requests\n for Issues on GitHub.com\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"hhtssxt0f5v2\",\"name\":\"Pull\n Requests\",\"status\":\"operational\",\"created_at\":\"2020-09-02T15:39:06.329Z\",\"updated_at\":\"2020-10-10T00:02:49.033Z\",\"position\":6,\"description\":\"Requests\n for Pull Requests on GitHub.com\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"br0l2tvcx85d\",\"name\":\"GitHub\n Actions\",\"status\":\"operational\",\"created_at\":\"2019-11-13T18:02:19.432Z\",\"updated_at\":\"2020-10-13T20:23:36.040Z\",\"position\":7,\"description\":\"Workflows,\n Compute and Orchestration for GitHub Actions\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"st3j38cctv9l\",\"name\":\"GitHub\n Packages\",\"status\":\"operational\",\"created_at\":\"2019-11-13T18:02:40.064Z\",\"updated_at\":\"2020-09-08T15:50:32.845Z\",\"position\":8,\"description\":\"API\n requests and webhook delivery for GitHub Packages\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false},{\"id\":\"vg70hn9s2tyj\",\"name\":\"GitHub\n Pages\",\"status\":\"operational\",\"created_at\":\"2017-01-31T20:04:33.923Z\",\"updated_at\":\"2020-10-10T00:02:38.220Z\",\"position\":9,\"description\":\"Frontend\n application and API servers for Pages builds\",\"showcase\":false,\"start_date\":null,\"group_id\":null,\"page_id\":\"kctbh9vrtdwd\",\"group\":false,\"only_show_if_degraded\":false}]}'\n recorded_at: 2020-10-15 11:59:23 GMT\n recorded_with: vcr/0.5.4, webmockr/0.7.0\ntest_that(\"gh_organizations works\", {\n vcr::use_cassette(\"gh_organizations\", {\n orgs <- gh_organizations()\n })\n testthat::expect_type(orgs, \"character\")\n})\n\ntest_that(\"gh_organizations errors when the API doesn't behave\", {\n webmockr::enable()\n stub <- webmockr::stub_request(\"get\", \"https://api.github.com/organizations?since=1\")\n webmockr::to_return(stub, status = 502)\n expect_error(gh_organizations(), \"oops\")\n webmockr::disable()\n})\n stub <- webmockr::stub_request(\"get\", \"https://api.github.com/organizations?since=1\")\n webmockr::to_return(stub, status = 502)http_interactions:\n- request:\n method: get\n uri: https://api.github.com/organizations?since=1\n body:\n encoding: ''\n string: ''\n headers:\n Accept: application/json, text/xml, application/xml, */*\n Content-Type: ''\n Authorization: My bearer token is safe"},{"path":"vcr.html","id":"also-testing-for-real-interactions","chapter":"6 Use vcr (& webmockr)","heading":"6.3 Also testing for real interactions","text":"API responses change?\nHopefully ’d notice thanks following API news.\nHowever, sometimes web APIs change without notice.\nTherefore important run tests real web service .vcr package provides various methods turn vcr use allow real requests .e. ignoring mock files.\nSee ?vcr::lightswitch.case exemplighratia, added GitHub Actions workflow run schedule week, one build vcr turned via VCR_TURN_OFF environment variable.\nchose one build vcr turned otherwise configuration make easier assess broke case failure (builds fail, web API probably culprit).\nCompared continuous integration builds vcr turned , one build needs access GITHUB_PAT secret environment variable. Furthermore, slower.One imagine strategies:Always one continuous integration build vcr turned skipping contexts isn’t token (pull requests forks instance?);running tests vcr turned locally .","code":""},{"path":"vcr.html","id":"summary","chapter":"6 Use vcr (& webmockr)","heading":"6.4 Summary","text":"set vcr usage package exemplighratia running use_vcr() tweaking setup file protect secret API key fool package needs API token.Inside test_that() blocks, wrapped parts code vcr::use_cassette() ran tests first time generate mock files hold information API interactions.one tests, used webmockr create environment fake requests allowed. defined request gh_organizations() makes get 502 status. therefore able test error message gh_organizations() returns cases.Now, make sure works?Turn wifi, run tests . works! Turn wifi .Open .Renviron (usethis::edit_r_environ()), edit “GITHUB_PAT” “byeGITHUB_PAT”, re-start R, run tests . works! Fix “GITHUB_PAT” token .Renviron.now tests longer rely internet connection API credentials.also added continuous integration workflow build using real interactions every week, important regularly make sure package still works latest API responses.full list changes applied exemplighratia chapter, see pull request diff GitHub.get packages? Let’s try httptest next chapter!","code":""},{"path":"vcr.html","id":"ps-where-to-put-use_cassette","chapter":"6 Use vcr (& webmockr)","heading":"6.5 PS: Where to put use_cassette()","text":"put vcr::use_cassette() call?\nWell, written manual page function, ’s ways get correct line numbers failed tests one way get correct line numbers:\n’s correct?Wrapping whole testthat::test_that() call (test contains instance `skip_on_cran()``);Wrapping lines inside testthat::test_that() excluding expectations expect_blabla()’s incorrect?used solution wrapping lines containing API calls vcr::use_cassette(), choose prefer.","code":"\nvcr::use_cassette(\"thing\", {\n testthat::test_that(\"thing\", {\n lala <- get_foo()\n expect_true(lala)\n })\n})\ntestthat::test_that(\"thing\", {\n vcr::use_cassette(\"thing\", {\n lala <- get_foo()\n })\n expect_true(lala)\n})\ntestthat::test_that(\"thing\", {\n vcr::use_cassette(\"thing\", {\n lala <- get_foo()\n expect_true(lala)\n })\n})"},{"path":"httptest.html","id":"httptest","chapter":"7 Use httptest","heading":"7 Use httptest","text":"chapter aim adding HTTP testing infrastructure exemplighratia using httptest.\n, start initial state exemplighratia . Back square one!Note httptest::with_mock_dir() function available httptest version >= 4.0.0 (released CRAN 2021-02-01).Corresponding pull request exemplighratia Feel free fork repository experiment !","code":""},{"path":"httptest.html","id":"setup-1","chapter":"7 Use httptest","heading":"7.1 Setup","text":"working , need install httptest.First, need run httptest::use_httptest() effects:Adding httptest dependency DESCRIPTION, Suggests just like testthat.Creating setup file tests/testthat/setup,testthat runs tests, files whose name starts “setup” always run first.\nsetup file added httptest loads httptest.shall tweak bit fool package believing API token around contexts . Since tests use recorded responses recording, need actual API token recording, need gh_organizations() stop Sys.getenv(\"GITHUB_PAT\") returns nothing.just setup, now adapting tests!","code":"\nlibrary(httptest)\nlibrary(httptest)\n\n# for contexts where the package needs to be fooled\n# (CRAN, forks)\n# this is ok because the package will used recorded responses\n# so no need for a real secret\nif (!nzchar(Sys.getenv(\"GITHUB_PAT\"))) {\n Sys.setenv(GITHUB_PAT = \"foobar\")\n}"},{"path":"httptest.html","id":"actual-testing-1","chapter":"7 Use httptest","heading":"7.2 Actual testing","text":"key function httptest::with_mock_dir(\"dir\", {code-block}) tells httptest create mock files tests/testthat/dir store API responses API calls occurring code block.\nallowed tweak mock files hand, cases.Let’s tweak test file gh_status_api, becomesWe wrap whole test httptest::with_mock_dir().run test (RStudio clicking “Run test”),first time, httptest creates mock file tests/testthat/gh_api_status/kctbh9vrtdwd.statuspage.io/api/v2/components.json.json stores API response.\nhowever dumbed hand, toall times , httptest simply uses mock file instead actually calling API.Let’s tweak test, gh_organizations().things get exciting complicated, also set adding test error behavior.\ninspired us change error behavior bit slightly specific error message .e. httr::stop_for_status(response) became httr::stop_for_status(response, task = \"get data API, oops\").test file tests/testthat/test-organizations.R now:first test similar gh_api_status() except didn’t touch mock file time, laziness.\nsecond test unpack: get mock file corresponding error?first run test . fails error, expected. Note simplify = FALSE means mock file also contains headers response.replaced 200L 502L removed body, end simple mock file tests/testthat/gh_organizations_error/api.github.com/organizations-5377e8.RWe re-run tests. got expected error message.Without HTTP testing infrastructure, testing behavior package case API errors difficult.Regarding secret API token, since httptest doesn’t save requests, since responses don’t contain token, safe without making effort.demo used httptest::with_mock_dir() ways use httptest, e.g. using httptest::with_mock_api() require naming directory (’d still need use separate directory mocking error response).Find main httptest vignette.","code":"\nwith_mock_dir(\"gh_api_status\", {\n test_that(\"gh_api_status() works\", {\n testthat::expect_type(gh_api_status(), \"character\")\n testthat::expect_equal(gh_api_status(), \"operational\")\n })\n}){\"components\":[{\"name\":\"API Requests\",\"status\":\"operational\"}]}\nwith_mock_dir(\"gh_organizations\", {\n test_that(\"gh_organizations works\", {\n testthat::expect_type(gh_organizations(), \"character\")\n })\n})\n\nwith_mock_dir(\"gh_organizations_error\", {\n test_that(\"gh_organizations errors if the API doesn't behave\", {\n testthat::expect_error(gh_organizations())\n })\n},\nsimplify = FALSE)\nstructure(list(url = \"https://api.github.com/organizations?since=1\",\n status_code = 502L, headers = NULL), class = \"response\")"},{"path":"httptest.html","id":"httptest-real","chapter":"7 Use httptest","heading":"7.3 Also testing for real interactions","text":"API responses change?\nHopefully ’d notice thanks following API news.\nHowever, sometimes web APIs change without notice.\nTherefore important run tests real web service .vcr setup GitHub Actions workflow runs week tests real web service.\ndifference tests .\ntests custom made mock files can specific (e.g. testing actual values, whereas latest responses API different values), instead turning mock files usage, use old original tests put folder called real-tests.\ntime real-tests .Rbuildignored scheduled run, checking package replace content tests real-tests.\nalternative use testthat::test_dir() directory case failures get artifacts R CMD check (least without effort)., one imagine strategies, cases important keep checking package real web service fairly regularly.","code":""},{"path":"httptest.html","id":"summary-1","chapter":"7 Use httptest","heading":"7.4 Summary","text":"set httptest usage package exemplighratia running use_httptest() tweaking setup file fool package needs API token.wrapped test_that() httptest::with_mock_dir() ran tests first time generate mock files hold information API responses. cases modified mock files make smaller make correspond API error.Now, make sure works?Turn wifi, run tests . works! Turn wifi .Open .Renviron (usethis::edit_r_environ()), edit “GITHUB_PAT” “byeGITHUB_PAT”, re-start R, run tests . works! Fix “GITHUB_PAT” token .Renviron.now tests longer rely internet connection API credentials.also added continuous integration workflow build using real interactions every week, important regularly make sure package still works latest API responses.full list changes applied exemplighratia chapter, see pull request diff GitHub.get yet another package? ’ll try webfakes first let’s compare vcr httptest use mocking.","code":""},{"path":"mocking-pkgs-comparison.html","id":"mocking-pkgs-comparison","chapter":"8 vcr and httptest","heading":"8 vcr and httptest","text":"just followed similar processes add HTTP testing infrastructure involving mock files exemplighratiaAdding package Suggests dependency;Creating helper file particular loads package test;Tweaking tests, cases wrapping tests functions allows record API responses mock files play back said mock files; cases (httptest), creating mock files .Now, differences.\nwon’t end advocating one package particular since merits, hope help differentiate two packages.","code":""},{"path":"mocking-pkgs-comparison.html","id":"setting-up-the-infrastructure","chapter":"8 vcr and httptest","heading":"8.1 Setting up the infrastructure","text":"set HTTP testing infrastructure, one case need run vcr::use_vcr() another case need run httptest::use_httptest(). hard remember.","code":""},{"path":"mocking-pkgs-comparison.html","id":"calling-mock-files","chapter":"8 vcr and httptest","heading":"8.2 Calling mock files","text":"mentioned , vcr httptest use mock files call differently.vcr called fixtures cassettes.\nhttptest called mock files.\nNote fixtures specific cassettes mock files: cassettes mock files fixtures, anything (csv file input instance) use consistently test package fixture.","code":""},{"path":"mocking-pkgs-comparison.html","id":"naming-mock-files","chapter":"8 vcr and httptest","heading":"8.3 Naming mock files","text":"vcr use_cassette() call needs include name used create filename mock file.\nhelp ?use_cassette explains criteria naming , fact cassette names need unique.\nNow wrap whole test_that() block might just well use name similar test name, already make meaningful, right?httptest mock filepaths translated requests according several rules incorporate request method, URL, query parameters, body.\nuse with_mock_dir() need name directory mock files saved, can make meaningful.Also note vcr one file can () contain several HTTP interactions (requests responses) whereas httptest one file contains one response (filename helps matching request).","code":""},{"path":"mocking-pkgs-comparison.html","id":"matching-requests","chapter":"8 vcr and httptest","heading":"8.4 Matching requests","text":"httptest mock file name includes everything ’s potentially varying request, mock file corresponds one request .vcr, different possible configurations matching request saved interaction default can mostly expect one saved interaction corresponds one request .","code":""},{"path":"mocking-pkgs-comparison.html","id":"handling-secrets","chapter":"8 vcr and httptest","heading":"8.5 Handling secrets","text":"vcr, since everything HTTP interactions recorded, always need add sort configuration sure wipe API tokens mock files.httptest, responses saved, often, bodies.\noften, responses don’t contain secrets e.g. don’t contain API token.\nresponse contains secrets, refer httptest’s article “Redacting sensitive information”.","code":""},{"path":"mocking-pkgs-comparison.html","id":"recording-playing-back","chapter":"8 vcr and httptest","heading":"8.6 Recording, playing back","text":"using mock files testing, first need record responses mock files; want use mock files instead real HTTP interactions (’s whole point).vcr, recording vs playing back modes happen automatically depending existence cassette. write vcr::use_cassette(\"blabla\", ) ’s cassette called blabla, vcr create . Note change HTTP interactions code block, ’ll re-record cassette simple deleting running test. Note can also change way vcr behaves looking ?vcr::vcr_configure’s “Cassette Options”.httptest, lot flexibility around record mock files. httptest doesn’t assume every API mock came real request real server; maybe copy mocks directly API docs.Note nothing prevents editing vcr cassettes hand, ’ll careful re-recording mistake.httptest flexiblity comes original design principles httptest“[httptest] doesn’t assume every API mock came real request real server, designed able see modify test fixtures.\nAmong considerations:1. many cases, API responses contain way content necessary test R code around : 100 records 2 suffice, request metadata don’t care can’t meaningfully assert things , . interest minimally reproducible examples, making tests readable, often makes sense take actual API response delete lot content, even fabricate one entirely.2. ’s good keep API mock fixed know exactly . re-recorded Twitter API response , say, recent 10 tweets #rstats, specific content change every time record , tests can’t say much response without rewrite every time .3. conditions (rate limiting, server errors, e.g.) difficult test real responses, can hand-create API mock , say, 503 response status code test code handles , can confidence package respond rare event happens real API.4. Re-recording responses can make huge code diff, can blow repository size make code review harder.”Now, creating mock files hand (inventing custom scripts create ) involves elbow grease, ’s compromise.","code":""},{"path":"mocking-pkgs-comparison.html","id":"testing-for-api-errors","chapter":"8 vcr and httptest","heading":"8.7 Testing for API errors","text":"test suite probably want check things go server returns 502 , trigger response record .httptest, test API errors, need create one several fake mock file(s).\neasiest way might use httptest::with_mock_dir() create mock files expected filenames locations, can tweak.\nreading error message httptest::with_mock_ap() helps know create mock file.vcr, eitheruse webmockr showed demo. one hand ’s compact creating fake mock file, hand ’s way test ’s different vcr cassette.edit cassette hand similar testing API errors httptest. , ’d need skip test vcr , vcr real requests made. can use vcr::skip_if_vcr_off().","code":"\ntest_that(\"gh_organizations errors when the API doesn't behave\", {\n webmockr::enable()\n stub <- webmockr::stub_request(\"get\", \"https://api.github.com/organizations?since=1\")\n webmockr::to_return(stub, status = 502)\n expect_error(gh_organizations(), \"oops\")\n webmockr::disable()\n})"},{"path":"mocking-pkgs-comparison.html","id":"conclusion-3","chapter":"8 vcr and httptest","heading":"8.8 Conclusion","text":"vcr httptest similar packages use mock files allowing easier HTTP testing.\nbit different design philosophy features, might help choose one .now, make things even complex, fun, shall explore third HTTP testing package mock requests instead spins local fake web service.","code":""},{"path":"httptest2.html","id":"httptest2","chapter":"9 Use httptest2","heading":"9 Use httptest2","text":"chapter aim adding HTTP testing infrastructure exemplighratia2 using httptest2.\n, start initial state exemplighratia2 . Back square one!Corresponding pull request exemplighratia2 Feel free fork repository experiment !","code":""},{"path":"httptest2.html","id":"setup-2","chapter":"9 Use httptest2","heading":"9.1 Setup","text":"working , need install httptest2.First, need run httptest2::use_httptest2() effects:Adding httptest2 dependency DESCRIPTION, Suggests just like testthat.Creating setup file tests/testthat/setup,testthat runs tests, files whose name starts “setup” always run first.\nsetup file added httptest2 loads httptest2.shall tweak bit fool package believing API token around contexts . Since tests use recorded responses recording, need actual API token recording, need gh_organizations() stop Sys.getenv(\"GITHUB_PAT\") returns nothing.just setup, now adapting tests!","code":"\nlibrary(httptest2)\nlibrary(httptest2)\n\n# for contexts where the package needs to be fooled\n# (CRAN, forks)\n# this is ok because the package will used recorded responses\n# so no need for a real secret\nif (!nzchar(Sys.getenv(\"GITHUB_PAT\"))) {\n Sys.setenv(GITHUB_PAT = \"foobar\")\n}"},{"path":"httptest2.html","id":"actual-testing-2","chapter":"9 Use httptest2","heading":"9.2 Actual testing","text":"key function httptest2::with_mock_dir(\"dir\", {code-block}) tells httptest create mock files tests/testthat/dir store API responses API calls occurring code block.\nallowed tweak mock files hand, cases.Let’s tweak test file gh_status_api, becomesWe wrap whole test httptest2::with_mock_dir().run test (RStudio clicking “Run test”),first time, httptest2 creates mock file tests/testthat/gh_api_status/kctbh9vrtdwd.statuspage.io/api/v2/components.json.json stores API response.\nhowever dumbed hand, toall times , httptest2 simply uses mock file instead actually calling API.Let’s tweak test, gh_organizations().things get exciting complicated, also set adding test error behavior.\ninspired us change error behavior bit slightly specific error message .e. httr2::resp_check_status(response) became httr2::resp_check_status(response, info = \"Oops, try later?\").test file tests/testthat/test-organizations.R now:first test similar gh_api_status() except didn’t touch mock file time, laziness.\nsecond test unpack: get mock file corresponding error?first run test . fails error, expected. Note simplify = FALSE means mock file also contains headers response.replaced 200L 502L removed body, end simpler mock file tests/testthat/gh_organizations_error/api.github.com/organizations-5377e8.RWe re-run tests. got expected error message.Without HTTP testing infrastructure, testing behavior package case API errors difficult.Regarding secret API token, since httptest2 doesn’t save requests4, since responses don’t contain token, safe without making effort.demo used httptest2::with_mock_dir() ways use httptest2, e.g. using httptest2::with_mock_api() require naming directory (’d still need use separate directory mocking error response).Find main httptest2 vignette.","code":"\nwith_mock_dir(\"gh_api_status\", {\n test_that(\"gh_api_status() works\", {\n testthat::expect_type(gh_api_status(), \"character\")\n testthat::expect_equal(gh_api_status(), \"operational\")\n })\n}){\"components\":[{\"name\":\"API Requests\",\"status\":\"operational\"}]}\nwith_mock_dir(\"gh_organizations\", {\n test_that(\"gh_organizations works\", {\n testthat::expect_type(gh_organizations(), \"character\")\n })\n})\n\nwith_mock_dir(\"gh_organizations_error\", {\n test_that(\"gh_organizations errors if the API doesn't behave\", {\n testthat::expect_snapshot_error(gh_organizations())\n })\n},\nsimplify = FALSE)\nstructure(list(method = \"GET\", url = \"https://api.github.com/organizations?since=1\",\n status_code = 502L, headers = structure(list(server = \"GitHub.com\",\n date = \"Thu, 17 Feb 2022 12:40:29 GMT\", `content-type` = \"application/json; charset=utf-8\",\n `cache-control` = \"private, max-age=60, s-maxage=60\",\n vary = \"Accept, Authorization, Cookie, X-GitHub-OTP\",\n etag = \"W/\\\"d56e867402a909d66653b6cb53d83286ba9a16eef993dc8f3cb64c43b66389f4\\\"\",\n `x-oauth-scopes` = \"gist, repo, user, workflow\", `x-accepted-oauth-scopes` = \"\",\n `x-github-media-type` = \"github.v3; format=json\", link = \"; rel=\\\"next\\\", ; rel=\\\"first\\\"\",\n `x-ratelimit-limit` = \"5000\", `x-ratelimit-remaining` = \"4986\",\n `x-ratelimit-reset` = \"1645104327\", `x-ratelimit-used` = \"14\",\n `x-ratelimit-resource` = \"core\", `access-control-expose-headers` = \"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset\",\n `access-control-allow-origin` = \"*\", `strict-transport-security` = \"max-age=31536000; includeSubdomains; preload\",\n `x-frame-options` = \"deny\", `x-content-type-options` = \"nosniff\",\n `x-xss-protection` = \"0\", `referrer-policy` = \"origin-when-cross-origin, strict-origin-when-cross-origin\",\n `content-security-policy` = \"default-src 'none'\", vary = \"Accept-Encoding, Accept, X-Requested-With\",\n `content-encoding` = \"gzip\", `x-github-request-id` = \"A4BA:12D5C:178438:211160:620E423C\"), class = \"httr2_headers\"),\n body = charToRaw(\"\")), class = \"httr2_response\")"},{"path":"httptest2.html","id":"httptest2-real","chapter":"9 Use httptest2","heading":"9.3 Also testing for real interactions","text":"API responses change?\nHopefully ’d notice thanks following API news.\nHowever, sometimes web APIs change without notice.\nTherefore important run tests real web service .One use strategy one demonstrated httptest .e. different test folder., one imagine strategies, cases important keep checking package real web service fairly regularly.","code":""},{"path":"httptest2.html","id":"summary-2","chapter":"9 Use httptest2","heading":"9.4 Summary","text":"set httptest2 usage package exemplighratia running use_httptest2() tweaking setup file fool package needs API token.wrapped test_that() httptest2::with_mock_dir() ran tests first time generate mock files hold information API responses. cases modified mock files make smaller make correspond API error.Now, make sure works?Turn wifi, run tests . works! Turn wifi .Open .Renviron (usethis::edit_r_environ()), edit “GITHUB_PAT” “byeGITHUB_PAT”, re-start R, run tests . works! Fix “GITHUB_PAT” token .Renviron.now tests longer rely internet connection API credentials.also added continuous integration workflow build using real interactions every week, important regularly make sure package still works latest API responses.full list changes applied exemplighratia chapter, see pull request diff GitHub.get yet another package? ’ll try webfakes.","code":""},{"path":"webfakes.html","id":"webfakes","chapter":"10 Use webfakes","heading":"10 Use webfakes","text":"chapter aim adding HTTP testing infrastructure exemplighratia using webfakes.","code":""},{"path":"webfakes.html","id":"setup-3","chapter":"10 Use webfakes","heading":"10.1 Setup","text":"working , need install webfakes, install.packages(\"webfakes\")., need add webfakes Suggests dependency package, potentially via running usethis::use_package(\"webfakes\", type = \"Suggests\").Last least, create setup file tests/testthat/setup.R.\ntestthat runs tests, files whose name starts “setup” always run first.\nneed ensure set fake API key API token around.\n? remember well, code function gh_organizations() checks presence token.\nusing fake web service, obviously don’t really need token still need fool package contexts token (e.g. continuous integration checks fork GitHub repository).setup file also load webfakes, demo namespace webfakes functions instead.","code":"\nif(!nzchar(Sys.getenv(\"REAL_REQUESTS\"))) {\n Sys.setenv(\"GITHUB_PAT\" = \"foobar\")\n}"},{"path":"webfakes.html","id":"actual-testing-3","chapter":"10 Use webfakes","heading":"10.2 Actual testing","text":"webfakes spinning local fake web services want package interact instead real APIs.\nTherefore, first need amend code functions returning URLs services able change via environment variable.\nbecome:andHaving two switches crucial., let’s tweak test gh_api_status().’s happening ?’re asking requests real service made (Sys.getenv(\"REAL_REQUESTS\")), prepare new app via webfakes::new_app(). ’s simple one, returns, GET requests, list corresponding ’re used getting status API, except ) ’s much smaller b) “operational” status hard-coded.create local app process via webfakes::local_app_process(, start = TRUE). start right away thanks start=TRUE chosen start later via calling e.g. web$url() (see ?webfakes::local_app_process); importantly stopped automatically test. mess made!set EXEMPLIGHRATIA_GITHUB_STATUS_URL variable URL local app process. connects code fake web service.might seem like lot overhead code butIt means real requests made ultimate goal.get used .can write helper code testthat helper file repeat test files; even app shared test files depending package.Now, let’s add test error behavior.\ninspired us change error behavior bit slightly specific error message .e. httr::stop_for_status(response) became httr::stop_for_status(response, task = \"get API status, ouch!\").’s similar process earlier test:setting new app;return something chose, case 502 status;launching local app process;connecting code via setting EXEMPLIGHRATIA_GITHUB_STATUS_URL environment variable URL fake service;test.Last least let’s convert test gh_organizations(),wecreate new app;returned something chose GET request /organizations endpoint. case, return content JSON file created tests/testthat/responses/organizations.json copy-pasting real response API;launch local app process;set URL EXEMPLIGHRATIA_GITHUB_API_URL environment variable;test.","code":"\nstatus_url <- function() {\n\n env_url <- Sys.getenv(\"EXEMPLIGHRATIA_GITHUB_STATUS_URL\")\n\n if (nzchar(env_url)) {\n return(env_url)\n }\n\n \"https://kctbh9vrtdwd.statuspage.io/api/v2/components.json\"\n}\ngh_v3_url <- function() {\n\n api_url <- Sys.getenv(\"EXEMPLIGHRATIA_GITHUB_API_URL\")\n\n if (nzchar(api_url)) {\n return(api_url)\n }\n\n \"https://api.github.com/\"\n}\ntest_that(\"gh_api_status() works\", {\n if (!nzchar(Sys.getenv(\"REAL_REQUESTS\"))) {\n app <- webfakes::new_app()\n app$get(\"/\", function(req, res) {\n res$send_json(\n list( components =\n list(\n list(\n name = \"API Requests\",\n status = \"operational\"\n )\n )\n ),\n auto_unbox = TRUE\n )\n })\n web <- webfakes::local_app_process(app, start = TRUE)\n web$local_env(list(EXEMPLIGHRATIA_GITHUB_STATUS_URL = \"{url}\"))\n }\n\n testthat::expect_type(gh_api_status(), \"character\")\n})\ntest_that(\"gh_api_status() errors when the API does not behave\", {\n app <- webfakes::new_app()\n app$get(\"/\", function(req, res) {\n res$send_status(502L)\n })\n web <- webfakes::local_app_process(app, start = TRUE)\n web$local_env(list(EXEMPLIGHRATIA_GITHUB_STATUS_URL = \"{url}\"))\n testthat::expect_error(gh_api_status(), \"ouch\")\n})\ntest_that(\"gh_organizations works\", {\n\n if (!nzchar(Sys.getenv(\"REAL_REQUESTS\"))) {\n app <- webfakes::new_app()\n app$get(\"/organizations\", function(req, res) {\n res$send_json(\n jsonlite::read_json(\n testthat::test_path(\n file.path(\"responses\", \"organizations.json\")\n )\n ),\n auto_unbox = TRUE\n )\n })\n web <- webfakes::local_app_process(app, start = TRUE)\n web$local_env(list(EXEMPLIGHRATIA_GITHUB_API_URL = \"{url}\"))\n }\n\n testthat::expect_type(gh_organizations(), \"character\")\n})"},{"path":"webfakes.html","id":"also-testing-for-real-interactions-1","chapter":"10 Use webfakes","heading":"10.3 Also testing for real interactions","text":"API responses change?\nHopefully ’d notice thanks following API news.\nHowever, sometimes web APIs change without notice.\nTherefore important run tests real web service .tests used conditionbefore launching app using URL URL service.\ntests generic enough, can add CI build environment variable REAL_REQUESTS set true.\ngeneric enough, can use theapproach exemplified chapter httptest.set folder real-tests tests interacting real web service;add Rbuildignore;CI build, delete tests/testthat replace real-tests, running R CMD check.","code":"if (!nzchar(Sys.getenv(\"REAL_REQUESTS\"))) {"},{"path":"webfakes.html","id":"summary-3","chapter":"10 Use webfakes","heading":"10.4 Summary","text":"set webfakes usage package exemplighratia adding dependency webfakes adding setup file fool package needs API token.created launched fake apps test files.Now, make sure works?Turn wifi, run tests . works! Turn wifi .Open .Renviron (usethis::edit_r_environ()), edit “GITHUB_PAT” “byeGITHUB_PAT”, re-start R, run tests . works! Fix “GITHUB_PAT” token .Renviron.now tests longer rely internet connection API credentials.full list changes applied exemplighratia chapter, see pull request diff GitHub.next chapter, shall compare three approaches HTTP testing ’ve demo-ed.","code":""},{"path":"pkgs-comparison.html","id":"pkgs-comparison","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11 vcr (& webmockr), httptest, webfakes","text":"’re now nice stage made demo usage HTTP testing packages, exemplighratia package.\ncourse, choice strategy demo bit subjective, hope showed best tool.first message ’s important us: ’re learning HTTP testing using branch package sounds daunting, create minimal package playing!","code":""},{"path":"pkgs-comparison.html","id":"what-http-client-can-you-use-curl-httr-crul","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.1 What HTTP client can you use (curl, httr, crul)","text":"httptest works httr (popular HTTP R client);vcr (& webmockr) works httr crul (two “high-level” HTTP R clients);webfakes works R HTTP client, even base R wish.","code":""},{"path":"pkgs-comparison.html","id":"sustainability-of-the-packages","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.2 Sustainability of the packages","text":"packages (vcr, webmockr, httptest, webfakes) actively maintained.\nwriting book, issues pull requests tackled rather quickly, always nice way.","code":""},{"path":"pkgs-comparison.html","id":"test-writing-experience","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.3 Test writing experience","text":"cases HTTP tests, .e. tests work independently internet connection, depends onsetup, mainly adding dependency HTTP testing packages DESCRIPTION, setup helper file;providing responses API.difference packages, test writing experience depends can provide responses API, real ones fake ones.vcr httptest tests testing normal behavior, set (helper function), testing just function away (vcr::use_cassette(), httptest::with_mock_dir(), httptest::with_mock_api()).\nRecording happens automatically first run tests.\nmight also provide fake recorded response dumb existing ones.\ncreating API errors, API sequence responses (e.g. 502 200), end either using webmockr, amending mock files, see vcr httptest related docs.5With webfakes need create app.\none per test, per test file whole test suite.\nmight seem like overhead code able share app different tests reduces effort.\ncan test API sequence responses (e.g. 502 200) following -.\none thing ’s supported webfakes yet smooth workflow recording responses, time writing might need write workflow recording responses.general setup&test writing might easier packages mocking (vcr httptest) might able replicate complex behavior webfakes (OAuth dance).","code":""},{"path":"pkgs-comparison.html","id":"the-special-case-of-secrets","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.3.1 The special case of secrets","text":"webfakes authentication needed point, less chance exposing secret.httptest body responses saved, unless contains secrets, effort needed. need redact mock files, see corresponding vignette.vcr HTTP interactions, including request URLs headers, saved disk, often use filter_sensitive_data, filter_request_header /filter_response_header arguments vcr::vcr_configure().","code":""},{"path":"pkgs-comparison.html","id":"how-about-making-real-requests","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.3.2 How about making real requests","text":"three cases, switching back real requests might environment variable away (turning vcr , setting URL real web service URL connected instead webfakes fake web service).\nHowever, tests using fixed/fake responses / fake web service might work real requests can’t trigger API error, might test specific values tests using mock files whereas API returns something different every day.\nTherefore, ’s challenge common three packages, might need choose distinct tests integration tests/contract tests.\nSee also chapter making real requests.","code":""},{"path":"pkgs-comparison.html","id":"test-debugging-experience","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.4 Test debugging experience","text":"Sadly sometimes one needs run code tests interactive session, either debug tests making code change, learn write HTTP tests.webfakes, debugging works way: load helper test file wherethe app created,environment variable connecting package code fake web service changed.run code. debug webfakes apps, follow guidance.vcr, refer debugging vignette: ’ll load helper file source setup file making sure paths use work tests/testthat/ package root (see ?vcr::vcr_test_path), use vcr::inject_cassette(); don’t forget run vcr::eject_cassette() afterwards.\nwebmockr debugging quite natural, run code ’s test, particular webmockr::enable() webmockr::disable().httptest, process similar vcr except key functions areuse_mock_api().mockPaths.","code":""},{"path":"pkgs-comparison.html","id":"conclusion-4","chapter":"11 vcr (& webmockr), httptest, webfakes","heading":"11.5 Conclusion","text":"chapter compared three R packages make HTTP testing easier.\nstill unsure one pick, first try packages without commitment, branches , choose one commit lock-.“Every piece code written given language framework step away language, five minutes ’ll spend migrating something else. ’s fine. just decide ’re willing locked .(…)Code days becomes obsolete quickly, regardless ’s chosen. time needs change, time latest framework obsolete, code rotten anyway(…)dangerous feature articles examining cloud lock-introduce kind paralysis teams result applications never completely fleshed finished.”Vicki Boykis, “Commit lock-”.","code":""},{"path":"real-requests-chapter.html","id":"real-requests-chapter","chapter":"12 Making real requests","heading":"12 Making real requests","text":"touched upon Whole Games section, ’s good tests real API.\nIndeed, web resource can change.","code":""},{"path":"real-requests-chapter.html","id":"what-can-change","chapter":"12 Making real requests","heading":"12.1 What can change?","text":"can happen?API introducing rate-limiting;web resource disappearing;etc.","code":""},{"path":"real-requests-chapter.html","id":"how-to-make-real-requests","chapter":"12 Making real requests","heading":"12.2 How to make real requests","text":"Maybe can just run tests without using mock files.vcr, behavior one environment variable away (namely, VCR_TURN_OFF).httptest httptest2 can create kind behavior.webfakes can also create behavior.Now means assuming tests work real requests.tests won’t work real requests (say fixture mimicking API error, specific answer today given date) can skip tests mocking/faking web service . vcr means using vcr::skip_if_vcr_off(); httptest webfakes ’d create custom skipper.tests won’t work real requests, creating different folder tests making real requests makes sense. might less unit-y view tests integration/contract tests. Maybe use testthat’s snapshot testing (view ’s different API).","code":""},{"path":"real-requests-chapter.html","id":"when-to-make-real-requests","chapter":"12 Making real requests","heading":"12.2.1 When to make real requests?","text":"Locally, might want make real requests , particular CRAN release.continuous integration learn trigger workflows configure build matrices e.g.one build build matrix using real requests commit (might much, see next section);one scheduled workflow day week using real requests.","code":""},{"path":"real-requests-chapter.html","id":"why-not-make-only-or-too-many-real-requests","chapter":"12 Making real requests","heading":"12.3 Why not make only or too many real requests?","text":"reasons can’t make real requests tests reasons reading book:slower;can’t test API errors;etc.Now matter setup don’t want make many real requests can bad web resource bad (e.g. using allowed requests!).\nRegarding allowed requests, possible however increase requesting sort special development account thing exists API working .","code":""},{"path":"real-requests-chapter.html","id":"a-complement-to-real-requests-api-news","chapter":"12 Making real requests","heading":"12.4 A complement to real requests: API news!","text":"Running real requests important notice something changes API (expected requests, responses).\nNow, can also follow news web resource using case something place.Subscribe API newsletter ’s one;Read API changelogs public;particular, API developed GitHub/GitLab/etc. watch repo subscribe releases, might automatically get notified.","code":""},{"path":"cran-preparedness.html","id":"cran-preparedness","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13 CRAN- (and Bioconductor) preparedness for your tests","text":"one right answer manage tests CRAN, except \nwant clean check result CRAN times.\nprobably applies Bioconductor .\nfollowing \ndiscussion various considerations - give enough\ninformation make educated decision.","code":""},{"path":"cran-preparedness.html","id":"running-tests-on-cran","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13.1 Running tests on CRAN?","text":"can run vcr/httptest/httptest2/webfakes enabled tests CRAN.\nCRAN okay files associated tests,\ngeneral CRAN can run tests use cassettes, mock files recorded responses CRAN.\nAnother aspect presence dependencies: make sure HTTP testing package use listed Suggests dependency DESCRIPTION!\nwebfakes might mean also listing optional dependencies DESCRIPTION.\nwebfakes, tests run CRAN assume availability given port.running HTTP tests CRAN, aware things:tests require secret environment variables R options (apart “foobar” ones used fool package using saved response),\nwon’t available CRAN. cases likely want skip \ntests testthat::skip_on_cran().tests cassettes, mock files recorded responses sensitive information ,\nprobably want cassettes internet, case\nwon’t running vcr enabled tests CRAN either. case sensitive\ninformation, might want Rbuildignore cassettes, mock files recorded responses (gitignore make package development repository private).maximal size package sources want cassettes, mock files recorded responses big. three ways limit size\nMake requests generate huge response (e.g. tweak time range);\nEdit recorded responses (even copy-paste responses API docs often short) — see vcr docs editing cassettes pros cons;\nShare cassettes / mock files / recorded responses tests.\nMake requests generate huge response (e.g. tweak time range);Edit recorded responses (even copy-paste responses API docs often short) — see vcr docs editing cassettes pros cons;Share cassettes / mock files / recorded responses tests.compress cassettes, mock files recorded responses: CRAN submissions already compressed; compressed files make git diffs hard use.","code":""},{"path":"cran-preparedness.html","id":"skipping-a-few-tests-on-cran","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13.2 Skipping a few tests on CRAN?","text":"worried problems HTTP tests CRAN can use\ntestthat::skip_on_cran() skip specific tests.\nMake sure tests run somewhere else (continuous integration) regularly!’d recommend running tests making real requests CRAN, even interacting API without authentication.","code":""},{"path":"cran-preparedness.html","id":"skipping-all-tests-on-cran","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13.3 Skipping all tests on CRAN?","text":"good continuous integration setup (several operating systems, scheduled runs, etc.) skip tests CRAN?","code":""},{"path":"cran-preparedness.html","id":"stress-test-your-package","chapter":"13 CRAN- (and Bioconductor) preparedness for your tests","heading":"13.4 Stress-test your package","text":"stress-test package CRAN submission, use rhub::check_for_cran() without passing environment variable function, use WinBuilder.","code":""},{"path":"security-chapter.html","id":"security-chapter","chapter":"14 Security","heading":"14 Security","text":"developing package uses secrets (API keys, OAuth tokens) produces (OAuth tokens, sensitive data),want secrets usable , collaborators CI services, without readable anyone else;want tests checks (e.g. vignette building) use secrets turned environments secrets won’t available (CRAN, forks development repository).general attitude think :secrets (API key, OAuth2.0 access token refresh token, etc.) /exactly used (query part URL? header? header, Authentication something else?) – packages like httr httr2 might abstract complexity need really know secrets used leaked,go wrong (e.g. token ending published),prevent (save unedited token outside package, make sure printed logs present package check artefacts),fix mistakes (deactivate token check one used meantime).","code":""},{"path":"security-chapter.html","id":"managing-secrets-securely","chapter":"14 Security","heading":"14.1 Managing secrets securely","text":"","code":""},{"path":"security-chapter.html","id":"follow-best-practice-when-developing-your-package","chapter":"14 Security","heading":"14.1.1 Follow best practice when developing your package","text":"book testing security starts develop package.\nbetter protect users’ secret,might best let users pass API keys parameters. ’s best save .Renviron e.g. using keyring package. way, API keys scripts. opencage package might provide inspiration.might best let users pass API keys parameters. ’s best save .Renviron e.g. using keyring package. way, API keys scripts. opencage package might provide inspiration.API working lets pass keys either request headers query string, prefer use request headers.API working lets pass keys either request headers query string, prefer use request headers.","code":""},{"path":"security-chapter.html","id":"share-secrets-with-continuous-integration-services","chapter":"14 Security","heading":"14.1.2 Share secrets with continuous integration services","text":"need share secrets continuous integration services… real requests !\ntests using vcr, httptest, httptest2 webfakes, need fake secret, e.g. “foobar” API key – except recording cassettes mock files, something locally.GitHub repositories, storing new secret, save quotes.\n.e. secret “blabla”, field contain blabla, \"blabla\" 'blabla'.","code":"\nknitr::include_graphics(\"secret.png\")"},{"path":"security-chapter.html","id":"api-keys","chapter":"14 Security","heading":"14.1.2.1 API keys","text":"API keys, can use something like GitHub repo secrets use GitHub Actions.\nsecret accessible environment variable workflow GitHub Actions explained gargle docs need add line like","code":"env:\n PACKAGE_PASSWORD: ${{ secrets.PACKAGE_PASSWORD }}"},{"path":"security-chapter.html","id":"more-complex-objects","chapter":"14 Security","heading":"14.1.2.2 More complex objects","text":"secret OAuth token, might able re-create pieces, pieces strings can store repo secrets much like ’d API key.\nE.g. secret OAuth token, actual secrets access token refresh token.Therefore re-create using e.g. credentials argument httr::oauth2.0_token().\nre-creation using environment variables Sys.getenv(\"ACCESS_TOKEN\") Sys.getenv(\"REFRESH_TOKEN\") happen testthat helper file.","code":"env:\n ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}\n REFRESH_TOKEN: ${{ secrets.REFRESH_TOKEN }}"},{"path":"security-chapter.html","id":"secret-files","chapter":"14 Security","heading":"14.1.3 Secret files","text":"files, need use encryption store text-version encryption key/passwords GitHub repo secret use GitHub Actions.\nRead documentation continuous integration service using find secrets protected can use builds.See gargle vignette securely managing tokens.approach :Create OAuth token locally, either outside package folder, inside really want , gitignored Rbuildignored.Encrypt using e.g. user-friendly cyphr package. Save code step file e.g. inst/secrets.R need re-create token even refresh tokens expire.encrypting need sort password. want save securely text user-level .Renviron GitHub repo secrets (equivalent secret place CI services). E.g. create key via sodium_key <- sodium::keygen() get text equivalent via sodium::bin2hex(sodium_key). E.g. latter command might give e46b7faf296e3f0624e6240a6efafe3dfb17b92ae0089c7e51952934b60749f2 save .RenvironExample script creating encrypting OAuth token (Meetup API).tests setup / helper file code like .Now happens contexts MEETUPR_PWD available?\nWell tests using !\nSee chapter making real requests.","code":"MEETUPR_PWD=\"e46b7faf296e3f0624e6240a6efafe3dfb17b92ae0089c7e51952934b60749f2\"\n# thanks Jenny Bryan https://github.com/r-lib/gargle/blob/4fcf142fde43d107c6a20f905052f24859133c30/R/secret.R\n\ntoken_path <- testthat::test_path(\".meetup_token.rds\")\nuse_build_ignore(token_path)\nuse_git_ignore(token_path)\n\nmeetupr::meetup_auth(\n token = NULL,\n cache = TRUE,\n set_renv = FALSE,\n token_path = token_path\n)\n\n# sodium_key <- sodium::keygen()\n# set_renv(\"MEETUPR_PWD\" = sodium::bin2hex(sodium_key))\n# set_renv being an internal function taken from rtweet\n# that saves something to .Renviron\n\n# get key from environment variable\nkey <- cyphr::key_sodium(sodium::hex2bin(Sys.getenv(\"MEETUPR_PWD\")))\n\ncyphr::encrypt_file(\n token_path,\n key = key,\n dest = testthat::test_path(\"secret.rds\")\n)\nkey <- cyphr::key_sodium(sodium::hex2bin(Sys.getenv(\"MEETUPR_PWD\")))\n\ntemptoken <- tempfile(fileext = \".rds\")\n\ncyphr::decrypt_file(\n testthat::test_path(\"secret.rds\"),\n key = key,\n dest = temptoken\n)"},{"path":"security-chapter.html","id":"do-not-store-secrets-in-the-cassettes-mock-files-recorded-responses","chapter":"14 Security","heading":"14.1.4 Do not store secrets in the cassettes, mock files, recorded responses","text":"vcr make sure configure vcr correctly.httptest httptest2 response body (headers, default) recorded. contains secrets, refer documentation redacting sensitive information (httptest2).webfakes creating recorded responses , make sure process leak secrets. test something related authentication, use fake secrets.API interacting uses OAuth instance, make sure leaking access tokens refresh tokens.","code":""},{"path":"security-chapter.html","id":"escape-tests-that-require-secrets","chapter":"14 Security","heading":"14.1.5 Escape tests that require secrets","text":"depends setup testing real requests.\nsure test requiring secrets run CRAN instance.","code":""},{"path":"security-chapter.html","id":"sensitive-recorded-responses","chapter":"14 Security","heading":"14.2 Sensitive recorded responses?","text":"case might want gitignore cassettes / mock files / recorded responses,\nskip tests using continuous integration (e.g. testthat::skip_on_ci() something involved).\n’d also Rbuildignore cassettes / mock files / recorded responses, want release CRAN.","code":""},{"path":"security-chapter.html","id":"further-resources","chapter":"14 Security","heading":"14.3 Further resources","text":"tools might help detect leaks prevent .shhgit’s goal “Find secrets code. Secrets detection GitHub, GitLab Bitbucket repositories”.Yelp’s detect-secret “enterprise friendly way detecting preventing secrets code.”.git-secret “bash tool store private data inside git repo”.","code":""},{"path":"errors-chapter.html","id":"errors-chapter","chapter":"15 Faking HTTP errors","heading":"15 Faking HTTP errors","text":"HTTP testing can test behavior package case API error without actually trigger API error.\nimportant testing package’s gracefulness (informative error message user) robustness (e.g. use retrying case API errors).","code":""},{"path":"errors-chapter.html","id":"how-to-test-for-api-errors-e.g.-503","chapter":"15 Faking HTTP errors","heading":"15.1 How to test for API errors (e.g. 503)","text":"Different possibilities:Use webmockr demo.Edit vcr cassette; careful skip test vcr vcr::skip_if_vcr_is_off().httptest httptest2, edit mock file demo, create scratch.webfakes, choose return, specific app test, see demo.","code":""},{"path":"errors-chapter.html","id":"how-to-test-for-sequence-of-responses-e.g.-503-then-200","chapter":"15 Faking HTTP errors","heading":"15.2 How to test for sequence of responses (e.g. 503 then 200)","text":"Different possibilities:Use webmockr.Edit vcr cassette; careful skip test vcr vcr::skip_if_vcr_is_off()httptest, easy yet (httptest2 issue)webfakes, follow docs. Also specific app test behavior want tests.`","code":""},{"path":"contributor-friendliness.html","id":"contributor-friendliness","chapter":"16 Contributor friendliness","heading":"16 Contributor friendliness","text":"make package wrapping HTTP resource contributor-friendly?rOpenSci general advice contributor-friendliness.Now, aspects dealing HTTP testing.","code":""},{"path":"contributor-friendliness.html","id":"taking-notes-about-encryption","chapter":"16 Contributor friendliness","heading":"16.1 Taking notes about encryption","text":"contributing guide, make sure note e.g. created encrypted token tests. Link script one run re-create . Good future contributors including !","code":""},{"path":"contributor-friendliness.html","id":"providing-a-sandbox","chapter":"16 Contributor friendliness","heading":"16.2 Providing a sandbox","text":"might neat provide sandbox, even just .interacting say Twitter API might want create Twitter account dedicated .don’t use live API credentials tests 🤣. Leaving humorous replies https://t.co/x0COfvt2QDIf interacting sort web platform might want create account special storing test data.interacting sort web platform might want create account special storing test data.web APIs provide test API key, test account one can request access .web APIs provide test API key, test account one can request access .Make sure take notes create / request access sandbox, contributing guide.","code":""},{"path":"contributor-friendliness.html","id":"switching-between-accounts-depending-on-the-development-mode","chapter":"16 Contributor friendliness","heading":"16.3 Switching between accounts depending on the development mode","text":"package might behaviour load default token instance, placed app dir.\nNow, testing, might want load another token, probably also want token choice automatic possible.rtweet package logic.detects testing/dev mode.environment variables present able create testing token.testing token default token loaded depending development mode.","code":"\nis_testing <- function() {\n identical(Sys.getenv(\"TESTTHAT\"), \"true\") \n}\nis_dev_mode <- function() {\n exists(\".__DEVTOOLS__\", .getNamespace(\"rtweet\"))\n}\nrtweet_test <- function() {\n access_token <- Sys.getenv(\"RTWEET_ACCESS_TOKEN\")\n access_secret <- Sys.getenv(\"RTWEET_ACCESS_SECRET\")\n \n if (identical(access_token, \"\") || identical(access_secret, \"\")) {\n return()\n }\n\n rtweet_bot(\n \"7rX1CfEYOjrtZenmBhjljPzO3\",\n \"rM3HOLDqmjWzr9UN4cvscchlkFprPNNg99zJJU5R8iYtpC0P0q\",\n access_token,\n access_secret\n )\n}"},{"path":"contributor-friendliness.html","id":"documenting-http-testing","chapter":"16 Contributor friendliness","heading":"16.4 Documenting HTTP testing","text":"Contributors package might familiar HTTP testing package(s) use (true non-trivial test setup). Make sure contributing guide mentions pre-requisites link resources (maybe even book?).","code":""},{"path":"conclusion-5.html","id":"conclusion-5","chapter":"17 Conclusion","heading":"17 Conclusion","text":"get read basic HTTP (testing) concepts R,\ndiscovered five great packages demos (httptest2, vcr&webmockr, httptest, webfakes), \ndived advanced topics like security.’s next? Applying tools package(s), course!Pick one several HTTP testing package(s) package. Examples combinations:\nvcr testing normal behavior, webmockr testing behavior case web resource errors.\nvcr httptest2 tests, webfakes advanced things like OAuth2.0 flows slow internet connection.\nPick one several HTTP testing package(s) package. Examples combinations:vcr testing normal behavior, webmockr testing behavior case web resource errors.vcr httptest2 tests, webfakes advanced things like OAuth2.0 flows slow internet connection.Read docs HTTP testing package(s) choose – worthy use time. vcr webmockr can even stay book take advantage “vcr details” “webmockr details” sections.Read docs HTTP testing package(s) choose – worthy use time. vcr webmockr can even stay book take advantage “vcr details” “webmockr details” sections.examples, also look reverse dependencies HTTP testing package(s) use see used developers.examples, also look reverse dependencies HTTP testing package(s) use see used developers.Follow developments HTTP testing package(s) choose. five packages presented developed GitHub, e.g. release-watch repositories. also distributed CRAN, might use usual channel learning CRAN updates.Follow developments HTTP testing package(s) choose. five packages presented developed GitHub, e.g. release-watch repositories. also distributed CRAN, might use usual channel learning CRAN updates.Participate development HTTP testing package(s) choose? bug reports, feature requests, contributions might helpful. Make sure read contributing guide look current activity repositories.Participate development HTTP testing package(s) choose? bug reports, feature requests, contributions might helpful. Make sure read contributing guide look current activity repositories.Report feedback book, experience HTTP testing, tips, etc.\nGitHub repository book,\nrOpenSci forum.\nReport feedback book, experience HTTP testing, tips, etc.GitHub repository book,rOpenSci forum.Happy HTTP testing!","code":""},{"path":"mocking.html","id":"mocking","chapter":"18 Mocking HTTP Requests","heading":"18 Mocking HTTP Requests","text":"short version : webmockr helps stub HTTP requests \ndon’t repeat .","code":""},{"path":"mocking.html","id":"webmockr-pkgdown","chapter":"18 Mocking HTTP Requests","heading":"18.1 Package documentation","text":"Check https://docs.ropensci.org/webmockr/ documentation webmockr functions.","code":""},{"path":"mocking.html","id":"webmockr-features","chapter":"18 Mocking HTTP Requests","heading":"18.2 Features","text":"Stubbing HTTP requests low http client lib levelSetting verifying expectations HTTP requestsMatching requests based method, URI, headers bodySupport testthat via vcrCan used testing outside testing context","code":""},{"path":"mocking.html","id":"webmockr-detail","chapter":"18 Mocking HTTP Requests","heading":"18.3 How webmockr works in detail","text":"tell webmockr HTTP request want match sees \nrequest matching criteria doesn’t actually HTTP request. Instead,\ngives back object gotten back real request, \nbits knows . example, can’t give back actual\ndata ’d get real HTTP request request wasn’t performed.addition, set expectation webmockr return, \nreturn . example, expect request return 418 error\n(’m Teapot), ’s ’ll get.can match againstHTTP method (required)Plus single combination following:URI\nRight now, can match directly URI’s, regex URI patterns.\nEventually, support RFC 6570 URI templates.\nnormalize URI paths URL encoded things match\nURL un-encoded things (e.g. hello world hello%20world)\nRight now, can match directly URI’s, regex URI patterns.\nEventually, support RFC 6570 URI templates.normalize URI paths URL encoded things match\nURL un-encoded things (e.g. hello world hello%20world)Query parameters\nnormalize query parameter values URL encoded things match\nURL un-encoded things (e.g. message = hello world \nmessage = hello%20world)\nnormalize query parameter values URL encoded things match\nURL un-encoded things (e.g. message = hello world \nmessage = hello%20world)Request headers\nnormalize headers treat forms headers equal. \nexample, following two sets headers equal:\nlist(H1 = \"value1\", content_length = 123, X_CuStOm_hEAder = \"foo\")\nlist(h1 = \"value1\", \"Content-Length\" = 123, \"x-cuSTOM-HeAder\" = \"foo\")\n\nnormalize headers treat forms headers equal. \nexample, following two sets headers equal:\nlist(H1 = \"value1\", content_length = 123, X_CuStOm_hEAder = \"foo\")\nlist(h1 = \"value1\", \"Content-Length\" = 123, \"x-cuSTOM-HeAder\" = \"foo\")\nlist(H1 = \"value1\", content_length = 123, X_CuStOm_hEAder = \"foo\")list(h1 = \"value1\", \"Content-Length\" = 123, \"x-cuSTOM-HeAder\" = \"foo\")Request bodyReal HTTP requestsThere’s scenarios think using webmockr:doingwebmockr loaded turned . point webmockr doesn’t\nchange anything.turn webmockr likewebmockr now default allow real HTTP requests http\nlibraries adapters loaded (crul httr).can optionally allow real requests via webmockr_allow_net_connect(), \ndisallow real requests via webmockr_disable_net_connect(). can check\nwhether allowing real requests webmockr_net_connect_allowed().Certain kinds real HTTP requests allowed: don’t suppoprt yet,\ncan allow localhost HTTP requests allow_localhost parameter\nwebmockr_configure() function.Storing actual HTTP responseswebmockr doesn’t . Check vcr","code":"\nlibrary(webmockr)\nwebmockr::enable()"},{"path":"mocking.html","id":"webmockr-basic-usage","chapter":"18 Mocking HTTP Requests","heading":"18.4 Basic usage","text":"Stubbed request based uri default response","code":"\nlibrary(\"webmockr\")\n# enable webmockr\nwebmockr::enable()\nstub_request(\"get\", \"https://httpbin.org/get\")#> \n#> method: get\n#> uri: https://httpbin.org/get\n#> with: \n#> query: \n#> body: \n#> request_headers: \n#> to_return:\nlibrary(\"crul\")\nx <- HttpClient$new(url = \"https://httpbin.org\")\nx$get('get')#> \n#> url: https://httpbin.org/get\n#> request_headers: \n#> User-Agent: libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\n#> Accept-Encoding: gzip, deflate\n#> Accept: application/json, text/xml, application/xml, */*\n#> response_headers: \n#> status: 200"},{"path":"webmockr-stubs.html","id":"webmockr-stubs","chapter":"19 stubs","heading":"19 stubs","text":"set return objectsStubbing requests based method, uri query paramsStubbing requests set expectation timeoutStubbing requests set HTTP error expectation","code":"\nlibrary(\"webmockr\")\nstub_request(\"get\", \"https://httpbin.org/get\") %>%\n wi_th(\n query = list(hello = \"world\")) %>%\n to_return(status = 418)#> \n#> method: get\n#> uri: https://httpbin.org/get\n#> with: \n#> query: hello=world\n#> body: \n#> request_headers: \n#> to_return: \n#> - status: 418\n#> body: \n#> response_headers: \n#> should_timeout: FALSE\n#> should_raise: FALSE\nx$get('get', query = list(hello = \"world\"))#> \n#> url: https://httpbin.org/get\n#> request_headers: \n#> User-Agent: libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\n#> Accept-Encoding: gzip, deflate\n#> Accept: application/json, text/xml, application/xml, */*\n#> response_headers: \n#> status: 418\nstub_request(\"get\", \"https://httpbin.org/get\") %>%\n wi_th(query = list(hello = \"world\"), \n headers = list('User-Agent' = 'libcurl/7.51.0 r-curl/2.6 crul/0.3.6', \n 'Accept-Encoding' = \"gzip, deflate\"))#> \n#> method: get\n#> uri: https://httpbin.org/get\n#> with: \n#> query: hello=world\n#> body: \n#> request_headers: User-Agent=libcurl/7.51.0 r-cur..., Accept-Encoding=gzip, deflate\n#> to_return:\nstub_registry()#> \n#> Registered Stubs\n#> GET: https://httpbin.org/get \n#> GET: https://httpbin.org/get?hello=world | to_return: with status 418 \n#> 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\"}\nx <- HttpClient$new(url = \"https://httpbin.org\")\nx$get('get', query = list(hello = \"world\"))#> \n#> url: https://httpbin.org/get\n#> request_headers: \n#> User-Agent: libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\n#> Accept-Encoding: gzip, deflate\n#> Accept: application/json, text/xml, application/xml, */*\n#> response_headers: \n#> status: 418\nstub_request(\"post\", \"https://httpbin.org/post\") %>% to_timeout()\nx <- HttpClient$new(url = \"https://httpbin.org\")\nx$post('post')\n#> Error: Request Timeout (HTTP 408).\n#> - The client did not produce a request within the time that the server was prepared\n#> to wait. The client MAY repeat the request without modifications at any later time.\nlibrary(fauxpas)\nstub_request(\"get\", \"https://httpbin.org/get?a=b\") %>% to_raise(HTTPBadRequest)\nx <- HttpClient$new(url = \"https://httpbin.org\")\nx$get('get', query = list(a = \"b\"))\n#> Error: Bad Request (HTTP 400).\n#> - The request could not be understood by the server due to malformed syntax.\n#> The client SHOULD NOT repeat the request without modifications."},{"path":"webmockr-stubs.html","id":"webmockr-disk","chapter":"19 stubs","heading":"19.1 Writing to disk","text":"two ways deal mocking writing disk. First, can create file\ndata ’d like file, tell crul httr file .\nSecond, can simply give webmockr file path (doesn’t exist yet) \ndata, webmockr can take care putting data file.’s first method, put data file mock, pass \nfile connection (file()) to_return().second method, use webmockr::mock_file() webmockr handle file\ncontents.webmockr also supports httr::write_disk(), letting webmockr handle \nmock file creation:","code":"\n## make a temp file\nf <- tempfile(fileext = \".json\")\n## write something to the file\ncat(\"{\\\"hello\\\":\\\"world\\\"}\\n\", file = f)\n## make the stub\ninvisible(stub_request(\"get\", \"https://httpbin.org/get\") %>% \n to_return(body = file(f)))\n## make a request\nout <- HttpClient$new(\"https://httpbin.org/get\")$get(disk = f)\n## view stubbed file content\nreadLines(file(f))#> [1] \"{\\\"hello\\\":\\\"world\\\"}\"\ng <- tempfile(fileext = \".json\")\n## make the stub\ninvisible(stub_request(\"get\", \"https://httpbin.org/get?a=b\") %>% \n to_return(body = mock_file(path = g, payload = \"{\\\"hello\\\":\\\"mars\\\"}\\n\")))\n## make a request\nout <- crul::HttpClient$new(\"https://httpbin.org/get?a=b\")$get(disk = g)\n## view stubbed file content\nreadLines(out$content)#> [1] \"{\\\"hello\\\":\\\"mars\\\"}\" \"\"\nlibrary(httr)\nhttr_mock()\n## make a temp file\nf <- tempfile(fileext = \".json\")\n## make the stub\ninvisible(stub_request(\"get\", \"https://httpbin.org/get?cheese=swiss\") %>% \n to_return(\n body = mock_file(path = f, payload = \"{\\\"foo\\\": \\\"bar\\\"}\"),\n headers = list('content-type' = \"application/json\")\n ))\n## make a request\nout <- GET(\"https://httpbin.org/get?cheese=swiss\", write_disk(f, TRUE))\n## view stubbed file content\nreadLines(out$content)#> [1] \"{\\\"foo\\\": \\\"bar\\\"}\""},{"path":"webmockr-testing.html","id":"webmockr-testing","chapter":"20 testing","heading":"20 testing","text":"","code":"\nlibrary(\"webmockr\")\nlibrary(\"crul\")\nlibrary(\"testthat\")\n\nstub_registry_clear()\n\n# make a stub\nstub_request(\"get\", \"https://httpbin.org/get\") %>%\n to_return(body = \"success!\", status = 200)#> \n#> method: get\n#> uri: https://httpbin.org/get\n#> with: \n#> query: \n#> body: \n#> request_headers: \n#> to_return: \n#> - status: 200\n#> body: success!\n#> response_headers: \n#> should_timeout: FALSE\n#> should_raise: FALSE\n# check that it's in the stub registry\nstub_registry()#> \n#> Registered Stubs\n#> GET: https://httpbin.org/get | to_return: with body \"success!\" with status 200\n# make the request\nz <- crul::HttpClient$new(url = \"https://httpbin.org\")$get(\"get\")\n\n# run tests (nothing returned means it passed)\nexpect_is(z, \"HttpResponse\")\nexpect_equal(z$status_code, 200)\nexpect_equal(z$parse(\"UTF-8\"), \"success!\")"},{"path":"webmockr-utilities.html","id":"webmockr-utilities","chapter":"21 utilities","heading":"21 utilities","text":"","code":"\nlibrary(\"webmockr\")"},{"path":"webmockr-utilities.html","id":"webmockr-","chapter":"21 utilities","heading":"21.1 Managing stubs","text":"enable()enabled()disable()httr_mock()","code":""},{"path":"webmockr-utilities.html","id":"webmockr-managing-stubs","chapter":"21 utilities","heading":"21.2 Managing stubs","text":"stub_registry()stub_registry_clear()remove_request_stub()","code":""},{"path":"webmockr-utilities.html","id":"webmockr-managing-requests","chapter":"21 utilities","heading":"21.3 Managing requests","text":"request_registry()","code":""},{"path":"vcr-intro.html","id":"vcr-intro","chapter":"22 Caching HTTP requests","heading":"22 Caching HTTP requests","text":"Record HTTP calls replay ","code":""},{"path":"vcr-intro.html","id":"vcr-pkgdown","chapter":"22 Caching HTTP requests","heading":"22.1 Package documentation","text":"Check https://docs.ropensci.org/vcr/ documentation vcr functions.","code":""},{"path":"vcr-intro.html","id":"terminology","chapter":"22 Caching HTTP requests","heading":"22.2 Terminology","text":"vcr: name comes idea want record something play back later, like vcrcassette: thing record HTTP interactions . Right now option file system (writing files), future things, e.g. key-value store like Redisfixture: fixture something used consistently test piece software. case, cassette (just defined ) fixture - used unit tests. use setup function vcr_setup() default directory created hold cassettes called fixtures/ signal folder contains.Persisters: save requests - currently option file systemserialize: translating data format can stored; , translate HTTP request response data representation disk read back laterSerializers: serialize HTTP response - currently option YAML; options future include e.g. JSONinsert cassette: create cassette (HTTP interactions recorded cassette)eject cassette: eject cassette (longer recording cassette)replay: refers using cached result http request recorded earlier","code":""},{"path":"vcr-intro.html","id":"design","chapter":"22 Caching HTTP requests","heading":"22.3 Design","text":"section explains vcr’s internal design architecture.","code":""},{"path":"vcr-intro.html","id":"where-vcr-comes-from-and-why-r6","chapter":"22 Caching HTTP requests","heading":"22.3.1 Where vcr comes from and why R6","text":"vcr “ported” Ruby gem (aka, library) name6.\nported Ruby, object-oriented programming language\nthought easier use object system R \nclosely resemble used Ruby (least opinion). \nthinking lead choosing R6. exported functions users interact\nR6 classes, rather normal R functions. However,\ninternal code package uses R6. Thus, familiarity\nR6 important people may want contribute vcr,\nrequired vcr users.","code":""},{"path":"vcr-intro.html","id":"principles","chapter":"22 Caching HTTP requests","heading":"22.3.2 Principles","text":"","code":""},{"path":"vcr-intro.html","id":"an-easy-to-use-interface-hides-complexity","chapter":"22 Caching HTTP requests","heading":"22.3.2.1 An easy to use interface hides complexity","text":"described , vcr uses R6 internally, users interact \nnormal R functions. Internal functions quite complicated \nlargely R6 exported, simpler functions users interact \nnormal R functions.","code":""},{"path":"vcr-intro.html","id":"classfunction-names-are-inherited-from-ruby-vcr","chapter":"22 Caching HTTP requests","heading":"22.3.2.2 Class/function names are inherited from Ruby vcr","text":"Since R vcr ported Ruby, kept names \nfunctions/classes variables. ’re wondering \nfunction, class, variable particular name, derivation\ncan found package, part .","code":""},{"path":"vcr-intro.html","id":"hooks-into-http-clients","chapter":"22 Caching HTTP requests","heading":"22.3.2.3 Hooks into HTTP clients","text":"Perhaps fundamental thing package work \nknows HTTP requests made. stumped \nquite long time. looking Ruby vcr, first thought \nmust “listening” HTTP requests somehow. found \nmonkey patching; ’s ’s achieved Ruby. , Ruby\nvcr package literally overrides certain methods Ruby HTTP clients,\nhijacking internals HTTP clients.However, monkey patching allowed R. Thus, R \nsomehow “hooks” HTTP clients R. Fortunately, Scott \nmaintainer one HTTP clients, crul, able quickly\ncreate hook. fortunately, already hook mechanism\nhttr package.actual hooks vcr, webmockr. vcr depends \nwebmockr hooking HTTP clients httr crul.","code":""},{"path":"vcr-intro.html","id":"internal-classes","chapter":"22 Caching HTTP requests","heading":"22.3.3 Internal classes","text":"overview important aspects vcr.","code":""},{"path":"vcr-intro.html","id":"configuration","chapter":"22 Caching HTTP requests","heading":"22.3.3.1 Configuration","text":"internal object (vcr_c) created vcr loaded \ndefault vcr configuration options inside R6 class VCRConfig -\nsee https://github.com/ropensci/vcr/blob/main/R/onLoad.R. \nclass keeps track default user specified configuration options.\ncan access vcr_c using triple namespace :::, though \nintended general use. Whenever make calls vcr_configure()\nconfiguration functions, vcr_c affected.","code":""},{"path":"vcr-intro.html","id":"cassette-class","chapter":"22 Caching HTTP requests","heading":"22.3.3.2 Cassette class","text":"Cassette R6 class handles internals/state cassette.\ntime run use_cassette() class used. class quite\nmethods , ’s lot going class. Ideally\nclass separated subclasses handle similar sets\nlogic, ’s easy way R6.note Cassette called, within initialize()\ncall webmockr used create webmockr stubs.","code":""},{"path":"vcr-intro.html","id":"how-http-requests-are-handled","chapter":"22 Caching HTTP requests","heading":"22.3.3.3 How HTTP requests are handled","text":"Within webmockr, calls vcr class RequestHandler, \nchild classes RequestHandlerCrul RequestHandlerHttr \ncrul httr, respectively. classes determine \nHTTP request. options HTTP request include:Ignored can ignore HTTP requests certain rules using \nconfiguration options ignore_hosts ignore_localhostStubbed vcr HTTP request match found\ncassette defined use_cassette()/insert_cassette() call.\ncase matching request/response cassette returned\nreal HTTP request allowed.Recordable HTTP request match found\ncassette defined use_cassette()/insert_cassette() call.\ncase real HTTP request allowed, request/response \nrecorded cassette.Unhandled group cases, cause error\nthrown message trying help user figure \nfix problem.use vcr logging ’ll see categories logs.","code":""},{"path":"vcr-intro.html","id":"serializers","chapter":"22 Caching HTTP requests","heading":"22.3.3.4 Serializers","text":"Serializers handle format cassettes written files disk.\ncurrent options YAML (default) JSON. YAML implemented first\nvcr ’s default option Ruby vcr.R6 class Serializer parent class serializer types;\nYAML JSON R6 classes inherit Serializer. \nYAML JSON define just two methods: serialize() deserialize()\nconverting R structures yaml json, converting yaml json back\nR structures, respectively.","code":""},{"path":"vcr-intro.html","id":"environments","chapter":"22 Caching HTTP requests","heading":"22.3.4 Environments","text":"","code":""},{"path":"vcr-intro.html","id":"logging","chapter":"22 Caching HTTP requests","heading":"22.3.4.1 Logging","text":"internal environment (vcr_log_env) used use logging.\npoint keeps track one variable - file - able\nrefer file used logging across many classes/functions\nneed write log file.","code":""},{"path":"vcr-intro.html","id":"a-bit-of-housekeeping","chapter":"22 Caching HTTP requests","heading":"22.3.4.2 A bit of housekeeping","text":"Another internal environment (vcr__env) used keep track \nitems, including current cassette use, last vcr error.","code":""},{"path":"vcr-intro.html","id":"lightswitch","chapter":"22 Caching HTTP requests","heading":"22.3.4.3 Lightswitch","text":"Another internal environment (light_switch) used keep track users\nturning vcr. See ?lightswitch.","code":""},{"path":"vcr-intro.html","id":"vcr-basic-usage","chapter":"22 Caching HTTP requests","heading":"22.4 Basic usage","text":"","code":""},{"path":"vcr-intro.html","id":"in-tests","chapter":"22 Caching HTTP requests","heading":"22.4.1 In tests","text":"tests, whichever tests want use vcr, wrap vcr::use_cassette() call like:put vcr::use_cassette() block inside, put testthat expectations outside \nvcr::use_cassette() block:Don’t wrap use_cassette() block inside test_that() block testthat expectations inside use_cassette() block, ’ll get line number use_cassette() block starts failures.first time run tests, “cassette” .e. file recorded HTTP interactions, created tests/fixtures/rl_citation.yml.\ntimes , cassette used.\nchange code HTTP interactions needed code wrapped vcr::use_cassette(\"rl_citation\", delete tests/fixtures/rl_citation.yml run tests re-recording cassette.","code":"\nlibrary(testthat)\nvcr::use_cassette(\"rl_citation\", {\n test_that(\"my test\", {\n aa <- rl_citation()\n\n expect_is(aa, \"character\")\n expect_match(aa, \"IUCN\")\n expect_match(aa, \"www.iucnredlist.org\")\n })\n})\nlibrary(testthat)\ntest_that(\"my test\", {\n vcr::use_cassette(\"rl_citation\", {\n aa <- rl_citation()\n })\n\n expect_is(aa, \"character\")\n expect_match(aa, \"IUCN\")\n expect_match(aa, \"www.iucnredlist.org\")\n})"},{"path":"vcr-intro.html","id":"outside-of-tests","chapter":"22 Caching HTTP requests","heading":"22.4.2 Outside of tests","text":"want get feel vcr works, although don’t need .request gets recorded, subsequent requests form used cached HTTP response, much fasterImportantly, unit test deals inputs outputs - behind scenes use cached HTTP response - thus, tests run faster.cached response looks something like (condensed brevity):components request response preserved, HTTP client (case crul) can reconstruct response just wasn’t using vcr.","code":"\nlibrary(vcr)\nlibrary(crul)\n\ncli <- crul::HttpClient$new(url = \"https://eu.httpbin.org\")\nsystem.time(\n use_cassette(name = \"helloworld\", {\n cli$get(\"get\")\n })\n)\nsystem.time(\n use_cassette(name = \"helloworld\", {\n cli$get(\"get\")\n })\n)http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '200'\n message: OK\n explanation: Request fulfilled, document follows\n headers:\n status: HTTP/1.1 200 OK\n connection: keep-alive\n body:\n encoding: UTF-8\n string: \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\"\n recorded_at: 2018-04-03 22:55:02 GMT\n recorded_with: vcr/0.1.0, webmockr/0.2.4, crul/0.5.2"},{"path":"vcr-intro.html","id":"less-basic-usage","chapter":"22 Caching HTTP requests","heading":"22.4.3 Less basic usage","text":"tweaking things needs, make sure read docs configuration (e.g., fixtures saved? can re-recorded automatically regulary?) request matching (vcr match request recorded interaction?)components request response preserved, HTTP client (case crul) can reconstruct response just wasn’t using vcr.","code":""},{"path":"vcr-intro.html","id":"vcr-enabled-testing","chapter":"22 Caching HTTP requests","heading":"22.5 vcr enabled testing","text":"","code":""},{"path":"vcr-intro.html","id":"check-vs-test","chapter":"22 Caching HTTP requests","heading":"22.5.1 check vs. test","text":"TLDR: Run devtools::test() running devtools::check() recording cassettes.running tests checks whole package, note ’ll get different results \ndevtools::check() (check button RStudio build pane) vs. devtools::test() (test button RStudio build pane). arises devtools::check() runs \ntemporary directory files created (vcr cassettes) temporary directory \nthus don’t persist devtools::check() exits.However, devtools::test() run temporary directory, files created (vcr\ncassettes) whatever directory ’re running .Alternatively, can run devtools::test_file() (“Run test” button RStudio) create vcr cassettes one test file time.","code":""},{"path":"vcr-intro.html","id":"vcr-ci","chapter":"22 Caching HTTP requests","heading":"22.5.2 CI sites: GitHub Actions, Appveyor, etc.","text":"Refer security chapter.","code":""},{"path":"vcr-usage.html","id":"vcr-usage","chapter":"23 Advanced vcr usage","heading":"23 Advanced vcr usage","text":"Now ’ve covered basic vcr usage, ’s time advanced usage topics.","code":"\nlibrary(\"vcr\")"},{"path":"vcr-usage.html","id":"vcr-disk","chapter":"23 Advanced vcr usage","heading":"23.1 Mocking writing to disk","text":"http requests write response disk, \nuse vcr_configure() set write_disk_path option. See \nwrite_disk_path configuration option., create temporary directory, set fixturesThen pass file path (doesn’t exist yet) crul’s disk parameter.\nvcr take care handling writing response file \naddition cassette.also works httr. difference write disk\nfunction httr::write_disk(path) rather parameter.Note write disk using vcr, cassette slightly\nchanged. Instead holding http response body , cassette\nfile path response body.file response body otherwise string\nyaml field :","code":"\ntmpdir <- tempdir()\nvcr_configure(\n dir = file.path(tmpdir, \"fixtures\"),\n write_disk_path = file.path(tmpdir, \"files\")\n)#> \n#> Cassette Dir: /tmp/Rtmpw6cK9v/fixtures\n#> Record: once\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path: /tmp/Rtmpw6cK9v/files\nlibrary(crul)\n## make a temp file\nf <- tempfile(fileext = \".json\")\n## make a request\ncas <- use_cassette(\"test_write_to_disk\", {\n out <- HttpClient$new(\"https://httpbin.org/get\")$get(disk = f)\n})\nfile.exists(out$content)#> [1] TRUE\nout$parse()#> [1] \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json, text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\": \\\"libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\\\", \\n \\\"X-Amzn-Trace-Id\\\": \\\"Root=1-65bcaf74-72f548fe428c1e5833ef0cde\\\"\\n }, \\n \\\"origin\\\": \\\"40.65.196.147\\\", \\n \\\"url\\\": \\\"https://httpbin.org/get\\\"\\n}\\n\"http_interactions:\n- request:\n method: get\n uri: https://httpbin.org/get\n response:\n headers:\n status: HTTP/1.1 200 OK\n access-control-allow-credentials: 'true'\n body:\n encoding: UTF-8\n file: yes\n string: /private/var/folders/fc/n7g_vrvn0sx_st0p8lxb3ts40000gn/T/Rtmp5W4olr/files/file177e2e5d97ec.json{\n \"args\": {}, \n \"headers\": {\n \"Accept\": \"application/json, text/xml, application/xml, */*\", \n \"Accept-Encoding\": \"gzip, deflate\", \n \"Host\": \"httpbin.org\", \n \"User-Agent\": \"libcurl/7.54.0 r-curl/4.3 crul/0.9.0\"\n }, \n \"origin\": \"24.21.229.59, 24.21.229.59\", \n \"url\": \"https://httpbin.org/get\"\n}"},{"path":"vcr-configuration.html","id":"vcr-configuration","chapter":"24 Configure vcr","heading":"24 Configure vcr","text":"can also get default configuration variables via vcr_config_defaults()defaults set load vcr - can override described .","code":"\nlibrary(\"vcr\")\nvcr_config_defaults()#> $warn_on_empty_cassette\n#> [1] TRUE\n#> \n#> $quiet\n#> [1] TRUE\n#> \n#> $verbose_errors\n#> [1] FALSE\n#> \n#> $write_disk_path\n#> NULL\n#> \n#> $filter_query_parameters\n#> NULL\n#> \n#> $filter_response_headers\n#> NULL\n#> \n#> $filter_request_headers\n#> NULL\n#> \n#> $filter_sensitive_data_regex\n#> NULL\n#> \n#> $filter_sensitive_data\n#> NULL\n#> \n#> $log_opts\n#> $log_opts$file\n#> [1] \"vcr.log\"\n#> \n#> $log_opts$log_prefix\n#> [1] \"Cassette\"\n#> \n#> $log_opts$date\n#> [1] TRUE\n#> \n#> \n#> $log\n#> [1] FALSE\n#> \n#> $linked_context\n#> NULL\n#> \n#> $cassettes\n#> list()\n#> \n#> $allow_http_connections_when_no_cassette\n#> [1] FALSE\n#> \n#> $clean_outdated_http_interactions\n#> [1] FALSE\n#> \n#> $re_record_interval\n#> NULL\n#> \n#> $turned_off\n#> [1] FALSE\n#> \n#> $preserve_exact_body_bytes\n#> [1] FALSE\n#> \n#> $uri_parser\n#> [1] \"crul::url_parse\"\n#> \n#> $ignore_request\n#> NULL\n#> \n#> $ignore_localhost\n#> [1] FALSE\n#> \n#> $ignore_hosts\n#> NULL\n#> \n#> $persist_with\n#> [1] \"FileSystem\"\n#> \n#> $json_pretty\n#> [1] FALSE\n#> \n#> $serialize_with\n#> [1] \"yaml\"\n#> \n#> $allow_unused_http_interactions\n#> [1] TRUE\n#> \n#> $match_requests_on\n#> [1] \"method\" \"uri\" \n#> \n#> $record\n#> [1] \"once\"\n#> \n#> $dir\n#> [1] \".\""},{"path":"vcr-configuration.html","id":"set-configuration-variables","chapter":"24 Configure vcr","heading":"24.1 Set configuration variables","text":"Use vcr_configure() set configuration variables.example, set single variable:many :","code":"\nvcr_configure(\n dir = \"foobar/vcr_cassettes\"\n)#> \n#> Cassette Dir: foobar/vcr_cassettes\n#> Record: once\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:\nvcr_configure(\n dir = \"foobar/vcr_cassettes\",\n record = \"all\"\n)#> \n#> Cassette Dir: foobar/vcr_cassettes\n#> Record: all\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"re-set-to-defaults","chapter":"24 Configure vcr","heading":"24.2 Re-set to defaults","text":"","code":"\nvcr_configure_reset()"},{"path":"vcr-configuration.html","id":"details-on-some-of-the-config-options","chapter":"24 Configure vcr","heading":"24.3 Details on some of the config options","text":"","code":""},{"path":"vcr-configuration.html","id":"dir","chapter":"24 Configure vcr","heading":"24.3.1 dir","text":"Directory cassettes stored","code":"\nvcr_configure(dir = \"new/path\")#> \n#> Cassette Dir: new/path\n#> Record: once\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"record","chapter":"24 Configure vcr","heading":"24.3.2 record","text":"record modeOne : ‘’, ‘none’, ‘new_episodes’, ‘’. See ?recording info options","code":"\nvcr_configure(record = \"new_episodes\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: method, uri\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"match_requests_on","chapter":"24 Configure vcr","heading":"24.3.3 match_requests_on","text":"Customize vcr matches requests","code":"\nvcr_configure(match_requests_on = c('query', 'headers'))#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"allow_unused_http_interactions","chapter":"24 Configure vcr","heading":"24.3.4 allow_unused_http_interactions","text":"Allow HTTP connections cassetteDefault TRUE, thus error http interactions unused. \ncan set FALSE case vcr errors cassette ejected \nhttp interactions used.","code":"\nvcr_configure(allow_unused_http_interactions = FALSE)#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"serialize_with","chapter":"24 Configure vcr","heading":"24.3.5 serialize_with","text":"serializer use: “yaml” “json”. Note can \nmultiple cassettes name long use different\nserializers; want one cassette given cassette name,\nmake sure switch serializers, clean files longer need.","code":"\nvcr_configure(serialize_with = \"yaml\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"persist_with","chapter":"24 Configure vcr","heading":"24.3.6 persist_with","text":"persister use. Right now option “FileSystem”","code":"\nvcr_configure(persist_with = \"FileSystem\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: \n#> ignore localhost?: FALSE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"ignoring-some-requests","chapter":"24 Configure vcr","heading":"24.3.7 ignoring some requests","text":"ignore_hostsSpecify particular hosts ignore. ignore, mean \nreal HTTP requests ignored host allowed occur, \nothers .ignore_localhostIgnore localhost requestsignore_requestTHIS DOESN’T WORK YETHow ignore requestsFor ignoring requests, can example, real http requests go (ignored vcr) requests handled vcr. example, let’s say want requests google.com ignored:request httpbin.org handled vcr, cassette created request/response url, google.com request ignored cached .Note: ignoring requests works crul package now; work httr later vcr version.","code":"\nvcr_configure(ignore_hosts = \"google.com\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: google.com\n#> ignore localhost?: FALSE\n#> Write disk path:\nvcr_configure(ignore_localhost = TRUE)#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: crul::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: google.com\n#> ignore localhost?: TRUE\n#> Write disk path:\nvcr_configure(ignore_hosts = \"google.com\")\nuse_cassette(\"foo_bar\", {\n crul::HttpClient$new(\"https://httpbin.org/get\")$get()\n crul::HttpClient$new(\"https://google.com\")$get()\n})"},{"path":"vcr-configuration.html","id":"uri_parse","chapter":"24 Configure vcr","heading":"24.3.8 uri_parse","text":"uri parser useBy default use crul::url_parse, can use different one. Remember\npass function quoted, namespaced.","code":"\nvcr_configure(uri_parser = \"urltools::url_parse\")#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: urltools::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: FALSE\n#> Logging?: FALSE\n#> ignored hosts: google.com\n#> ignore localhost?: TRUE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"preserve_exact_body_bytes","chapter":"24 Configure vcr","heading":"24.3.9 preserve_exact_body_bytes","text":"HTTP servers well-behaved respond invalid data. Set\npreserve_exact_body_bytes TRUE base64 encode result body \norder preserve bytes exactly -. vcr \ndefault, since base64-encoding string removes human readability\ncassette.","code":"\nvcr_configure(preserve_exact_body_bytes = TRUE)#> \n#> Cassette Dir: new/path\n#> Record: new_episodes\n#> Serialize with: yaml\n#> URI Parser: urltools::url_parse\n#> Match Requests on: query, headers\n#> Preserve Bytes?: TRUE\n#> Logging?: FALSE\n#> ignored hosts: google.com\n#> ignore localhost?: TRUE\n#> Write disk path:"},{"path":"vcr-configuration.html","id":"filter_sensitive_data","chapter":"24 Configure vcr","heading":"24.3.10 filter_sensitive_data","text":"named list values replace. Sometimes package script \nworking sensitive tokens/keys, want accidentally\nshare world.recording (writing cassette) replacement \nreading cassette reverse replacement get back\nreal data.recording disk, env var MY_API_KEY retrieved machine,\nfind instances , replace . replaying\ncreate HTTP response object put real value env var\nback place.target specific request response headers see filter_request_headers\nfilter_response_headers.","code":"\nvcr_configure(\n filter_sensitive_data = list(\"\" = Sys.getenv('MY_API_KEY'))\n)"},{"path":"vcr-configuration.html","id":"filter_request_headers","chapter":"24 Configure vcr","heading":"24.3.11 filter_request_headers","text":"Expects character vector named list. character vector, \nunnamed element list, request header removed \nwritten cassette.named list passed, name header value \nvalue replace real value.request header set remove replace removed/replaced\ncassette, requests using cassette, still \ncrul httr response objects real request creates \ncassette.Examples:","code":"\nvcr_configure(\n filter_request_headers = \"Authorization\"\n)\nvcr_configure(\n filter_request_headers = c(\"Authorization\", \"User-Agent\")\n)\nvcr_configure(\n filter_request_headers = list(Authorization = \"<<>>\")\n)"},{"path":"vcr-configuration.html","id":"filter_response_headers","chapter":"24 Configure vcr","heading":"24.3.12 filter_response_headers","text":"Expects character vector named list. character vector, \nunnamed element list, response header removed \nwritten cassette.named list passed, name header value \nvalue replace real value.response header set remove replace removed/replaced\ncassette, requests using cassette, still \ncrul httr response objects real request creates \ncassette.Examples:","code":"\nvcr_configure(\n filter_response_headers = \"server\"\n)\nvcr_configure(\n filter_response_headers = c(\"server\", \"date\")\n)\nvcr_configure(\n filter_response_headers = list(server = \"fake-server\")\n)"},{"path":"vcr-configuration.html","id":"filter_query_parameters","chapter":"24 Configure vcr","heading":"24.3.13 filter_query_parameters","text":"Expects character vector named list. character vector, \nunnamed element list, query parameter removed (parameter\nname value) written cassette.named list passed, name query parameter name \nvalue value replace real value.response header set remove replace removed/replaced\ncassette, requests using cassette, still \ncrul httr response objects real request creates \ncassette.Beware match_requests_on option using filter. \nfilter query parameter ’s probably bad idea match query\ngiven way vcr restore exact http request\ncassette one query parameters removed changed.\nOne way filter query parameter still match query \nleast complete uri use replacement behavior (named list),\ninstead list(=\"b\") use two values list(=c(\"b\",\"c\")), \n“c” string stored cassette. course replace\nvalues values environment variables obscure\nreal values code public.Examples:","code":"\n# completely drop parameter \"user\"\nvcr_configure(\n filter_query_parameters = \"user\"\n)\n# completely drop parameters \"user\" and \"api_key\"\nvcr_configure(\n filter_query_parameters = c(\"user\", \"api_key\")\n)\n# replace the value of parameter \"api_key\" with \"fake-api-key\"\n# NOTE: in this case there's no way to put back any value on\n# subsequent requests, so we have to match by dropping this\n# parameter value before comparing URIs\nvcr_configure(\n filter_query_parameters = list(api_key = \"fake-api-key\")\n)\n# replace the value found at Sys.getenv(\"MY_API_KEY\") of parameter\n# \"api_key\" with the value \"foo\". When using a cassette on subsequent\n# requests, we can replace \"foo\" with the value at Sys.getenv(\"MY_API_KEY\")\n# before doing the URI comparison\nvcr_configure(\n filter_query_parameters = list(api_key = c(Sys.getenv(\"MY_API_KEY\"), \"foo\"))\n)"},{"path":"record-modes.html","id":"record-modes","chapter":"25 Record modes","heading":"25 Record modes","text":"Record modes dictate circumstances http requests/responses \nrecorded cassettes (disk). Set recording mode parameter\nrecord use_cassette() insert_cassette() functions.","code":""},{"path":"record-modes.html","id":"once","chapter":"25 Record modes","heading":"25.1 once","text":"record mode :Replay previously recorded interactions.Record new interactions cassette file.Cause error raised new requests cassette file.similar new_episodes record mode, prevent new,\nunexpected requests made (.e. request URI changed\nwhatever).default record mode, used set one.","code":""},{"path":"record-modes.html","id":"none","chapter":"25 Record modes","heading":"25.2 none","text":"none record mode :Replay previously recorded interactions.Cause error raised new requests.useful code makes potentially dangerous\nHTTP requests. none record mode guarantees \nnew HTTP requests made.","code":""},{"path":"record-modes.html","id":"new_episodes","chapter":"25 Record modes","heading":"25.3 new_episodes","text":"new_episodes record mode :Record new interactions.Replay previously recorded interactions.similar record mode, always record new\ninteractions, even existing recorded one similar\n(identical, based match_request_on option).","code":""},{"path":"record-modes.html","id":"all","chapter":"25 Record modes","heading":"25.4 all","text":"record mode :Record new interactions.Never replay previously recorded interactions.can temporarily used force vcr re-record\ncassette (.e. ensure responses date)\ncan used simply want log HTTP requests.","code":""},{"path":"request-matching.html","id":"request-matching","chapter":"26 Request matching","heading":"26 Request matching","text":"match previously recorded requests, vcr try match new\nHTTP requests previously recorded one. default, match \nHTTP method (e.g., GET) URI (e.g., http://foo.com), following\nRuby’s VCR gem.can customize match requests one \nfollowing options, default, \ncan used together, alone.method: Use method request matcher match requests HTTP method\n(.e. GET, POST, PUT, DELETE, etc). generally want use\nmatcher. method matcher used (along uri matcher)\ndefault specify requests match.uri: Use uri request matcher match requests request URI. \nuri matcher used (along method matcher) default\nspecify requests match.host: Use host request matcher match requests request host.\ncan use (alone, combination path) \nalternative uri non-deterministic portions URI\nconsidered part request matching.path: Use path request matcher match requests path portion\nrequest URI. can use (alone, combination host)\nalternative uri non-deterministic portions URIquery: Use query request matcher match requests query string\nportion request URI. can use (alone, combination \nothers) alternative uri non-deterministic portions \nURI considered part request matching.body: Use body request matcher match requests request body.headers: Use headers request matcher match requests request headers.can set options tweaking match_requests_on parameter \nuse_cassette():","code":"\nlibrary(vcr)\nuse_cassette(name = \"foo_bar\", {\n cli$post(\"post\", body = list(a = 5))\n }, \n match_requests_on = c('method', 'headers', 'body')\n)"},{"path":"request-matching.html","id":"matching","chapter":"26 Request matching","heading":"26.1 Matching","text":"","code":""},{"path":"request-matching.html","id":"headers","chapter":"26 Request matching","heading":"26.1.1 headers","text":"","code":"\nlibrary(crul)\nlibrary(vcr)\ncli <- crul::HttpClient$new(\"https://httpbin.org/get\", \n headers = list(foo = \"bar\"))\nuse_cassette(name = \"nothing_new\", {\n one <- cli$get()\n }, \n match_requests_on = 'headers'\n)\ncli$headers$foo <- \"stuff\"\nuse_cassette(name = \"nothing_new\", {\n two <- cli$get()\n }, \n match_requests_on = 'headers'\n)\none$request_headers\ntwo$request_headers"},{"path":"debugging-your-tests-that-use-vcr.html","id":"debugging-your-tests-that-use-vcr","chapter":"27 Debugging your tests that use vcr","heading":"27 Debugging your tests that use vcr","text":"Sometimes tests using vcr cassette fail want debug .","code":""},{"path":"debugging-your-tests-that-use-vcr.html","id":"an-http-request-has-been-made-that-vcr-does-not-know-how-to-handle","chapter":"27 Debugging your tests that use vcr","heading":"27.1 An HTTP request has been made that vcr does not know how to handle","text":"get error starting “HTTP request made vcr know handle:” running tests,\nmeans code test makes HTTP request\nmatching information cassette using.\nmight added request, changed one slightly.easy fix : delete cassette re-run test re-record cassette.\nRun test second time ensure well.\n, escalate next paragraph.Maybe didn’t actually want change request making.\nMake sure requests contain something random, something related e.g. time now, URI (http://foo.com?time=13).\nmake sure things varying, might want use mocking (e.g. function returning current time), setting random seed, using withr (e.g. setting option certain value test).","code":""},{"path":"debugging-your-tests-that-use-vcr.html","id":"actual-debugging","chapter":"27 Debugging your tests that use vcr","heading":"27.1.1 Actual debugging","text":"Ideally want run code tests run inside tests,\nparticular, using vcr cassette.","code":""},{"path":"debugging-your-tests-that-use-vcr.html","id":"prepare-your-debugging-environment","chapter":"27 Debugging your tests that use vcr","heading":"27.1.2 Prepare your debugging environment","text":"first need load either vcr helper tests/testthat/helper-vcr.R (e.g. via devtools::load_all())\nsource vcr setup file tests/testthat/setup-vcr.R .e. file lines (maybe others)instead vcr::vcr_test_path(\"fixtures\") see \"../fixtures\",\nreplace \"../fixtures\" vcr::vcr_test_path(\"fixtures\"),\nvcr::vcr_test_path() function meant help exactly want:\npath tests/fixtures/ work tests root (running code debug ).one step (loading vcr helper sourcing vcr setup file),\nmaybe two (also replace \"../fixtures\" vcr::vcr_test_path(\"fixtures\")).","code":"\nlibrary(\"vcr\")\ninvisible(vcr::vcr_configure(\n dir = vcr::vcr_test_path(\"fixtures\"),\n filter_sensitive_data = list(\"<>\" = Sys.getenv('GITHUB_PAT'))\n))\nvcr::check_cassette_names()"},{"path":"debugging-your-tests-that-use-vcr.html","id":"debugging-itself","chapter":"27 Debugging your tests that use vcr","heading":"27.1.3 Debugging itself","text":"Now look test whose code trying debug e.g.want run code test,","code":"\nfoo <- function() crul::ok('https://httpbin.org/get')\n\ntest_that(\"foo works\", {\n vcr::use_cassette(\"testing\", {\n x <- foo()\n })\n expect_true(x)\n})\nfoo <- function() crul::ok('https://httpbin.org/get')\nvcr::insert_cassette(\"testing\") # it will be created if needed\nx <- foo()\nx\n# further interactive debugging and fixes\nvcr::eject_cassette(\"testing\")"},{"path":"debugging-your-tests-that-use-vcr.html","id":"logging-1","chapter":"27 Debugging your tests that use vcr","heading":"27.1.4 Logging","text":"can use vcr’s built logging help debugging process. configure logging,\nuse vcr_configure() function, set log=TRUE set options logging \nlog_opts parameter named list. See ?vcr_configure details., setting log file temporary file cleaned end\nR session. , file extension .log, file extension matter.log=TRUE can continue debugging. Open log file set text\neditor location; examine shell/terminal.example, running block aboveIf open log file ’ll see logs step vcr takes handling HTTP request.\nlogs information cassette used, exact time recorded, \nmatchers use, cassette options, request handled.Logging isn’t meant turned time - rather debugging/informational purposes.","code":"\nvcr::vcr_configure(\n dir = vcr::vcr_test_path(\"fixtures\"),\n log = TRUE,\n log_opts = list(file = file.path(tempdir(), \"vcr.log\"))\n)\nfoo <- function() crul::ok('https://httpbin.org/get')\n\ntest_that(\"foo works\", {\n vcr::use_cassette(\"testing\", {\n x <- foo()\n })\n expect_true(x)\n})[Cassette: 'testing'] - 2020-11-24 16:05:17 - Init. HTTPInteractionList w/ request matchers [method, uri] & 0 interaction(s): { }\n[Cassette: 'testing'] - 2020-11-24 16:05:17 - Initialized with options: {name: testing, record: once, serialize_with: yaml, persist_with: FileSystem, match_requests_on: c(\"method\", \"uri\"), update_content_length_header: FALSE, allow_playback_repeats: FALSE, preserve_exact_body_bytes: FALSE}\n[Cassette: 'testing'] - 2020-11-24 16:05:17 - Handling request: head https://httpbin.org/get (disabled: FALSE)\n[Cassette: 'testing'] - 2020-11-24 16:05:17 - Identified request type: (recordable) for head https://httpbin.org/get\n[Cassette: 'testing'] - 2020-11-24 16:05:17 - Recorded HTTP interaction: head https://httpbin.org/get => 200 "},{"path":"debugging-your-tests-that-use-vcr.html","id":"return-to-normal-development","chapter":"27 Debugging your tests that use vcr","heading":"27.1.5 Return to normal development","text":"Make sure ejected cassette using!Unless vcr helper/setup file tweaked things like,\neven need re-start R, , just safe side.","code":""},{"path":"vcr-security.html","id":"vcr-security","chapter":"28 Security with vcr","heading":"28 Security with vcr","text":"Refer security chapter general guidance.","code":""},{"path":"vcr-security.html","id":"api-keys-security","chapter":"28 Security with vcr","heading":"28.1 Keeping secrets safe","text":"keep secrets safe, need use parameters vcr::vcr_configure() tell vcr either secrets (put place), secrets (put place).\nbest know secrets used requests: e.g. API key passed header part query string?\nMaybe need different strategies different secrets (e.g. OAuth2.0 access token set Authorization header OAuth2.0 refresh token might query string).cases, crucial look cassettes putting public web, just sure got configuration right!","code":""},{"path":"vcr-security.html","id":"if-the-secret-is-in-a-request-header","chapter":"28 Security with vcr","heading":"28.1.1 If the secret is in a request header","text":"can use filter_request_headers!different ways use .","code":"\n# Remove one header from the cassettes\nvcr_configure(\n filter_request_headers = \"Authorization\"\n)\n\n# Remove two headers from the cassettes\nvcr_configure(\n filter_request_headers = c(\"Authorization\", \"User-Agent\")\n)\n\n# Replace one header with a given string\nvcr_configure(\n filter_request_headers = list(Authorization = \"<<>>\")\n)"},{"path":"vcr-security.html","id":"if-the-secret-is-in-a-response-header","chapter":"28 Security with vcr","heading":"28.1.2 If the secret is in a response header","text":"can use filter_response_headers works like filter_request_headers.","code":""},{"path":"vcr-security.html","id":"if-the-secret-is-somewhere-else","chapter":"28 Security with vcr","heading":"28.1.3 If the secret is somewhere else","text":"case need tell vcr secret string via filter_sensitive_data.\nwrite secret string directly configuration, ’d defeat purpose protecting !\nsecret environment variable instance tell vcr read .configuration parameter filter_sensitive_data accepts named list.element list following format:thing_to_replace_it_with = thing_to_replaceWe replace instances thing_to_replace thing_to_replace_it_with.recording (writing cassette) replacement \nreading cassette reverse replacement get back\nreal data.want make string replaces sensitive string something \nwon’t easily found elsewhere response body/headers/etc.","code":"\nvcr_configure(\n filter_sensitive_data = list(\"<<>>\" = Sys.getenv('API_KEY'))\n)"},{"path":"vcr-security.html","id":"different-api-keys","chapter":"28 Security with vcr","heading":"28.2 API keys and tests run in varied contexts","text":"real requests real API key needed.requests using cassettes fake API key needed fool package. \ndemo vcr set fake API key test setup file.","code":""},{"path":"vcr-security.html","id":"other-security","chapter":"28 Security with vcr","heading":"28.3 Other security","text":"Let us know security concerns! Surely ’s things haven’t\nconsidered yet.","code":""},{"path":"lightswitch.html","id":"lightswitch","chapter":"29 Turning vcr on and off","heading":"29 Turning vcr on and off","text":"Sometimes may need turn vcr, either individual\nfunction calls, individual test blocks, whole test files, \nentire package. following attempts break \noptions.vcr following four exported functions:turned_off() - Turns vcr duration code blockturn_off() - Turns vcr completely, longer handles every\nHTTP requestturn_on() - turns vcr ; opposite turn_off()turned_on() - Asks vcr turned , returns booleanInstead using four functions, use environment\nvariables achieve thing. way enable/disable\nvcr non-interactive environments continuous integration,\nDocker containers, running R non-interactively command line.\nfull set environment variables vcr uses, accept\nTRUE FALSE:VCR_TURN_OFF: turn vcr altogether; set TRUE skip vcr\nusage; default: FALSEVCR_TURNED_OFF: set turned_off internal package setting; \nturn vcr completely VCR_TURN_OFF , rather\nlooked together VCR_IGNORE_CASSETTESVCR_IGNORE_CASSETTES: set ignore_cassettes internal package\nsetting; looked together VCR_TURNED_OFF","code":""},{"path":"lightswitch.html","id":"turned-off","chapter":"29 Turning vcr on and off","heading":"29.1 turned_off","text":"turned_off() lets temporarily make real HTTP request without completely turning\nvcr , unloading , etc.happens internally turn vcr, run code block, exit\nturn vcr back - vcr turned duration \ncode block. Even code block errors, vcr turned back \ndue use .exit(turn_on())","code":"\nlibrary(vcr)\nlibrary(crul)\nturned_off({\n con <- HttpClient$new(url = \"https://httpbin.org/get\")\n con$get()\n})\n#> \n#> url: https://httpbin.org/get\n#> request_headers:\n#> User-Agent: libcurl/7.54.0 r-curl/4.3 crul/0.9.0\n#> Accept-Encoding: gzip, deflate\n#> Accept: application/json, text/xml, application/xml, */*\n#> response_headers:\n#> status: HTTP/1.1 200 OK\n#> date: Fri, 14 Feb 2020 19:44:46 GMT\n#> content-type: application/json\n#> content-length: 365\n#> connection: keep-alive\n#> server: gunicorn/19.9.0\n#> access-control-allow-origin: *\n#> access-control-allow-credentials: true\n#> status: 200"},{"path":"lightswitch.html","id":"turn-off-on","chapter":"29 Turning vcr on and off","heading":"29.2 turn_off/turn_on","text":"turn_off() different turned_off() turn_off() aimed\nsingle call block, rather turns vcr entire package.\nturn_off() check first turning vcr currently\ncassette use. turn_off() meant make R ignore vcr::insert_cassette()\nvcr::use_cassette() blocks test suite - letting code block\nrun wrapped vcr code - run\ntests cached requests/responses real HTTP requests toggle\nsingle R function environment variable.","code":"\nlibrary(vcr)\nvcr_configure(dir = tempdir())\n# real HTTP request works - vcr is not engaged here\ncrul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n# wrap HTTP request in use_cassette() - vcr is engaged here\nuse_cassette(\"foo_bar\", {\n crul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n})\n# turn off & ignore cassettes - use_cassette is ignored, real HTTP request made\nturn_off(ignore_cassettes = TRUE)\nuse_cassette(\"foo_bar\", {\n crul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n})\n# if you turn off and don't ignore cassettes, error thrown\nturn_off(ignore_cassettes = FALSE)\nuse_cassette(\"foo_bar\", {\n res2=crul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n})\n# vcr back on - now use_cassette behaves as before\nturn_on()\nuse_cassette(\"foo_bar3\", {\n res2=crul::HttpClient$new(url = \"https://eu.httpbin.org/get\")$get()\n})"},{"path":"lightswitch.html","id":"turned-on","chapter":"29 Turning vcr on and off","heading":"29.3 turned_on","text":"turned_on() says tin - tells vcr turned \n.","code":"\nlibrary(vcr)\nturn_on()\nturned_on()#> [1] TRUE\nturn_off()\nturned_on()#> [1] FALSE"},{"path":"lightswitch.html","id":"lightswitch-env-vars","chapter":"29 Turning vcr on and off","heading":"29.4 Environment variables","text":"VCR_TURN_OFF environment variable can used within R command line\nturn vcr. example, can run tests package uses vcr, \nignore use_cassette/insert_cassette usage, running command\nline root package:, similarly within R:VCR_TURNED_OFF VCR_IGNORE_CASSETTES environment variables can used\ncombination achieve thing VCR_TURN_OFF:","code":"VCR_TURN_OFF=true Rscript -e \"devtools::test()\"\nSys.setenv(VCR_TURN_OFF = TRUE)\ndevtools::test()VCR_TURNED_OFF=true VCR_IGNORE_CASSETTES=true Rscript -e \"devtools::test()\""},{"path":"managing-cassettes.html","id":"managing-cassettes","chapter":"30 Managing cassettes","heading":"30 Managing cassettes","text":"","code":""},{"path":"managing-cassettes.html","id":"why-edit-cassettes","chapter":"30 Managing cassettes","heading":"30.1 Why edit cassettes?","text":"design vcr good recording HTTP interactions actually took place.\nNow sometimes testing/demo-ing package want use fake HTTP interactions.\ninstance:happens web API returns 503 code? informative error?happens returns 503 200 code? retry work?API returns much data even simple queries want make cassettes smaller?cases, can edit cassettes long aware risks!","code":""},{"path":"managing-cassettes.html","id":"risks-related-to-cassette-editing","chapter":"30 Managing cassettes","heading":"30.2 Risks related to cassette editing","text":"use vcr cassette replace 200 code 503 code, vcr turned ,\ntest fail API probably return error. Use vcr::skip_if_vcr_off().edit cassettes hand can’t re-record easily, ’d need re-record re-apply edits.Therefore ’ll need develop good workflow.","code":""},{"path":"managing-cassettes.html","id":"example-1-test-using-an-edited-cassette-with-a-503","chapter":"30 Managing cassettes","heading":"30.3 Example 1: test using an edited cassette with a 503","text":"First, write test e.g.run tests first time.failIt created cassette tests/fixtures/api-error.yml looks\nsomething likeYou can edit (new status code)run test , pass!\nNote use vcr::skip_if_vcr_off(): vcr turned , real\nAPI request probably request won’t get 503 status code.","code":"vcr::use_cassette(\"api-error\", {\n test_that(\"Errors are handled well\", {\n vcr::skip_if_vcr_off()\n expect_error(call_my_api()), \"error message\")\n })\n})http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '200'\n message: OK\n explanation: Request fulfilled, document follows\n headers:\n status: HTTP/1.1 200 OK\n connection: keep-alive\n body:\n encoding: UTF-8\n string: \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\"\n recorded_at: 2018-04-03 22:55:02 GMT\n recorded_with: vcr/0.1.0, webmockr/0.2.4, crul/0.5.2http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '503'"},{"path":"managing-cassettes.html","id":"the-same-thing-with-webmockr","chapter":"30 Managing cassettes","heading":"30.3.1 The same thing with webmockr","text":"advantage approach involving editing cassettes learn\none thing, vcr.\nNow, using webmockr directly tests, can also test \nbehavior package case errors.\nassume api_url() returns URL call_my_api() calls.big pro approach works even vcr turned .\ncon ’s quite different vcr syntax.","code":"test_that(\"Errors are handled well\", {\n webmockr::enable()\n stub <- webmockr::stub_request(\"get\", api_url())\n webmockr::to_return(stub, status = 503)\n expect_error(call_my_api()), \"error message\")\n webmockr::disable()\n\n})"},{"path":"managing-cassettes.html","id":"example-2-test-using-an-edited-cassette-with-a-503-then-a-200","chapter":"30 Managing cassettes","heading":"30.4 Example 2: test using an edited cassette with a 503 then a 200","text":"assume package contains sort retry.First, write test e.g.run tests first time.failIt created cassette tests/fixtures/api-error.yml looks\nsomething likeYou can duplicate HTTP interaction, make first one return 503 status code.\nvcr first use first interaction, second one, making request.run test , pass!\nNote use vcr::skip_if_vcr_off(): vcr turned , real\nAPI request probably request won’t get 503 status code.","code":"vcr::use_cassette(\"api-error\", {\n test_that(\"Errors are handled well\", {\n vcr::skip_if_vcr_off()\n expect_message(thing <- call_my_api()), \"retry message\")\n expect_s4_class(thing, \"data.frame\")\n })\n})http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '200'\n message: OK\n explanation: Request fulfilled, document follows\n headers:\n status: HTTP/1.1 200 OK\n connection: keep-alive\n body:\n encoding: UTF-8\n string: \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\"\n recorded_at: 2018-04-03 22:55:02 GMT\n recorded_with: vcr/0.1.0, webmockr/0.2.4, crul/0.5.2http_interactions:\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '503'\n- request:\n method: get\n uri: https://eu.httpbin.org/get\n body:\n encoding: ''\n string: ''\n headers:\n User-Agent: libcurl/7.54.0 r-curl/3.2 crul/0.5.2\n response:\n status:\n status_code: '200'\n message: OK\n explanation: Request fulfilled, document follows\n headers:\n status: HTTP/1.1 200 OK\n connection: keep-alive\n body:\n encoding: UTF-8\n string: \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\"\n recorded_at: 2018-04-03 22:55:02 GMT\n recorded_with: vcr/0.1.0, webmockr/0.2.4, crul/0.5.2"},{"path":"managing-cassettes.html","id":"the-same-thing-with-webmockr-1","chapter":"30 Managing cassettes","heading":"30.4.1 The same thing with webmockr","text":"advantage approach involving editing cassettes learn\none thing, vcr.\nNow, using webmockr directly tests, can also test \nbehavior package case errors.\nassume api_url() returns URL call_my_api() calls.pro approach elegance stubbing, two different responses.\nwebmockr function like to_return() even argument times indicating \nnumber times given response returned.con top different vcr, case also needed\ngood response end (one 200 code, actual body), writing \nmock much cumbersome just recording vcr cassette.aware add cassettes either .gitignore /\n.Rbuildignore.","code":"test_that(\"Errors are handled well\", {\n webmockr::enable()\n stub <- webmockr::stub_request(\"get\", api_url())\n stub %>%\n to_return(status = 503) %>%\n to_return(status = 200, body = \"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"application/json,\n text/xml, application/xml, */*\\\", \\n \\\"Accept-Encoding\\\": \\\"gzip, deflate\\\",\n \\n \\\"Connection\\\": \\\"close\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\":\n \\\"libcurl/7.54.0 r-curl/3.2 crul/0.5.2\\\"\\n }, \\n \\\"origin\\\": \\\"111.222.333.444\\\",\n \\n \\\"url\\\": \\\"https://eu.httpbin.org/get\\\"\\n}\\n\", headers = list(b = 6))\n expect_message(thing <- call_my_api()), \"retry message\")\n expect_s4_class(thing, \"data.frame\")\n webmockr::disable()\n\n})"},{"path":"managing-cassettes.html","id":"gitignore-cassettes","chapter":"30 Managing cassettes","heading":"30.5 gitignore cassettes","text":".gitignore file lets tell [git][] files \nignore - files tracked git share git\nrepository public web, files .gitignore file\nwon’t shared public version.using vcr may want include cassettes \n.gitignore file. may wan cassettes contain sensitive\ndata don’t want internet & dont want hide\nfilter_sensitive_data.may want cassettes included GitHub repo, \npresent tests run CI, others run tests.’s correct answer whether gitignore cassettes.\nThink security implications whether want CI human\ncontributors use previously created cassettes create/use \n.","code":""},{"path":"managing-cassettes.html","id":"rbuildignore-cassettes","chapter":"30 Managing cassettes","heading":"30.6 Rbuildignore cassettes","text":".Rbuildignore file used tell R ignore\ncertain files/directories.’s clear use case ’d want add vcr cassettes\n.Rbuildignore file, aware affect\nvcr enabled tests.","code":""},{"path":"managing-cassettes.html","id":"sharing-cassettes","chapter":"30 Managing cassettes","heading":"30.7 sharing cassettes","text":"Sometimes may want share re-use cassettes across tests,\nexample reduce size package sources \ntest different functionality package functions\nmake query hood., can use cassette name multiple vcr::use_cassette()\ncalls.\nvcr::check_cassette_names() complain duplicate cassette\nnames, preventing accidentally re-using cassettes, however.\nallow duplicates, can provide character vector cassette names\nwant re-use allowed_duplicates argument \nvcr::check_cassette_names().\nway can use cassette across multiple tests.","code":""},{"path":"managing-cassettes.html","id":"deleting-cassettes","chapter":"30 Managing cassettes","heading":"30.8 deleting cassettes","text":"Removing cassette easy deleting file finder,\ncommand line, within text editor RStudio.delete cassette, next test run cassette \nrecorded .want re-record test cassette, instead \ndeleting file can toggle record modes.","code":""},{"path":"managing-cassettes.html","id":"cassette-file-types","chapter":"30 Managing cassettes","heading":"30.9 cassette file types","text":"right now persistence option yaml. files\n.yml extension.persister options added, additional file types\nmay found. next persister type likely JSON,\nuse option, ’d .json files instead \n.yml files.","code":""},{"path":"gotchas.html","id":"gotchas","chapter":"31 Gotchas","heading":"31 Gotchas","text":"’s things watch using vcr.Security: Don’t put secure API keys, tokens, etc. public web. See Security chapter vcr security chapter.API key issues: Running vcr enabled tests different contexts API keys used can rough edges.Dates: careful using dates tests vcr. e.g. generate todays date, pass function package uses date HTTP request, date different one matching cassette, causing vcr failure.HTTP errors: ’s good idea test failure behavior web service test suite. Sometimes vcr can handle sometimes . Open issues ideally think vcr handle cases HTTP failures.large response bodies: things large response bodies. First, vcr may give trouble large response bodies ’ve see yaml parsing problems already. Second, large response bodies means large cassettes disk - just aware file size ’s something matters . Third, large response bodies take longer load R, may still multi second test run even though test using cached HTTP response.Encoding: haven’t dealt encoding much yet , ’re likely run encoding issues. One blunt instrument now set preserve_exact_body_bytes = TRUE running vcr::use_cassette() vcr::insert_cassette(), stores response body base64.devtools::check vs. devtools::test: See (22.5.1)ignored files: See (30)","code":""},{"path":"gotchas.html","id":"line-identification","chapter":"31 Gotchas","heading":"31.1 Correct line identification","text":"get actual lines failures occur, can wrap test_that block use_cassette() block:put use_cassette() block inside, make sure put testthat expectations outside \nuse_cassette() block:wrap use_cassette() block inside test_that() block testthat expectations inside use_cassette() block, ’ll get line number use_cassette() block starts failures.","code":"\nlibrary(testthat)\nvcr::use_cassette(\"rl_citation\", {\n test_that(\"my test\", {\n aa <- rl_citation()\n\n expect_is(aa, \"character\")\n expect_match(aa, \"IUCN\")\n expect_match(aa, \"www.iucnredlist.org\")\n })\n})\nlibrary(testthat)\ntest_that(\"my test\", {\n vcr::use_cassette(\"rl_citation\", {\n aa <- rl_citation()\n })\n\n expect_is(aa, \"character\")\n expect_match(aa, \"IUCN\")\n expect_match(aa, \"www.iucnredlist.org\")\n})"},{"path":"session-info.html","id":"session-info","chapter":"32 Session info","heading":"32 Session info","text":"","code":""},{"path":"session-info.html","id":"session-info-1","chapter":"32 Session info","heading":"32.1 Session info","text":"None crul, webmockr, vcr, httptest compiled code, underlying dependency , curl . See curl’s README installation instructions case run curl related problems. webfakes compiled code.","code":"\nlibrary(\"magrittr\")\n\ndependencies <- attachment::att_from_rmds(\".\")\ndependencies <- dependencies[!dependencies %in% c(\"attachment\", \"bookdown\", \"knitr\")]\n\nsessioninfo::package_info(\n pkgs = dependencies\n ) %>%\n as.data.frame() %>%\n .[, c(\"package\", \"ondiskversion\")] %>%\n knitr::kable()"},{"path":"session-info.html","id":"full-session-info","chapter":"32 Session info","heading":"32.2 Full session info","text":"Session info bookPage found. Use table contents search bar find way back.","code":"\nsessioninfo::session_info()#> ─ Session info ───────────────────────────────────────────────────────────────\n#> setting value\n#> version R version 4.3.2 (2023-10-31)\n#> os Ubuntu 22.04.3 LTS\n#> system x86_64, linux-gnu\n#> ui X11\n#> language (EN)\n#> collate C.UTF-8\n#> ctype C.UTF-8\n#> tz UTC\n#> date 2024-02-02\n#> pandoc 2.19.2 @ /usr/bin/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────────────────────────\n#> package * version date (UTC) lib source\n#> attachment 0.4.1 2024-01-22 [1] RSPM\n#> base64enc 0.1-3 2015-07-28 [1] RSPM\n#> bookdown 0.37 2023-12-01 [1] RSPM\n#> brio 1.1.4 2023-12-10 [1] RSPM\n#> bslib 0.6.1 2023-11-28 [1] RSPM\n#> cachem 1.0.8 2023-05-01 [1] RSPM\n#> cli 3.6.2 2023-12-11 [1] RSPM\n#> crul * 1.4.0 2023-05-17 [1] RSPM\n#> curl 5.2.0 2023-12-08 [1] RSPM\n#> desc 1.4.3 2023-12-10 [1] RSPM\n#> digest 0.6.34 2024-01-11 [1] RSPM\n#> downlit 0.4.3 2023-06-29 [1] RSPM\n#> evaluate 0.23 2023-11-01 [1] RSPM\n#> fastmap 1.1.1 2023-02-24 [1] RSPM\n#> fauxpas 0.5.2 2023-05-03 [1] RSPM\n#> fs 1.6.3 2023-07-20 [1] RSPM\n#> gh 1.4.0 2023-02-22 [1] RSPM\n#> gitcreds 0.1.2 2022-09-08 [1] RSPM\n#> glue 1.7.0 2024-01-09 [1] RSPM\n#> highr 0.10 2022-12-22 [1] RSPM\n#> htmltools 0.5.7 2023-11-03 [1] RSPM\n#> httpcode 0.3.0 2020-04-10 [1] RSPM\n#> httr * 1.4.7 2023-08-15 [1] RSPM\n#> httr2 1.0.0 2023-11-14 [1] RSPM\n#> jquerylib 0.1.4 2021-04-26 [1] RSPM\n#> jsonlite 1.8.8 2023-12-04 [1] RSPM\n#> knitr 1.45 2023-10-30 [1] RSPM\n#> lifecycle 1.0.4 2023-11-07 [1] RSPM\n#> magrittr * 2.0.3 2022-03-30 [1] RSPM\n#> memoise 2.0.1 2021-11-26 [1] RSPM\n#> pkgload 1.3.4 2024-01-16 [1] RSPM\n#> purrr 1.0.2 2023-08-10 [1] RSPM\n#> R6 2.5.1 2021-08-19 [1] RSPM\n#> rappdirs 0.3.3 2021-01-31 [1] RSPM\n#> Rcpp 1.0.12 2024-01-09 [1] RSPM\n#> rlang 1.1.3 2024-01-10 [1] RSPM\n#> rmarkdown 2.25 2023-09-18 [1] RSPM\n#> roxygen2 7.3.1 2024-01-22 [1] RSPM\n#> rprojroot 2.0.4 2023-11-05 [1] RSPM\n#> rstudioapi 0.15.0 2023-07-07 [1] RSPM\n#> sass 0.4.8 2023-12-06 [1] RSPM\n#> sessioninfo 1.2.2 2021-12-06 [1] RSPM\n#> stringi 1.8.3 2023-12-11 [1] RSPM\n#> stringr 1.5.1 2023-11-14 [1] RSPM\n#> testthat * 3.2.1 2023-12-02 [1] RSPM\n#> triebeard 0.4.1 2023-03-04 [1] RSPM\n#> urltools 1.7.3 2019-04-14 [1] RSPM\n#> vcr * 1.2.2 2023-06-25 [1] RSPM\n#> vctrs 0.6.5 2023-12-01 [1] RSPM\n#> webmockr * 0.9.0 2023-02-28 [1] RSPM\n#> whisker 0.4.1 2022-12-05 [1] RSPM\n#> withr 3.0.0 2024-01-16 [1] RSPM\n#> xfun 0.41 2023-11-01 [1] RSPM\n#> xml2 1.3.6 2023-12-04 [1] RSPM\n#> yaml 2.3.8 2023-12-11 [1] RSPM\n#> \n#> [1] /home/runner/work/_temp/Library\n#> [2] /opt/R/4.3.2/lib/R/site-library\n#> [3] /opt/R/4.3.2/lib/R/library\n#> \n#> ──────────────────────────────────────────────────────────────────────────────"}] diff --git a/vcr-usage.html b/vcr-usage.html index 6b01be6..602a496 100644 --- a/vcr-usage.html +++ b/vcr-usage.html @@ -113,7 +113,7 @@

write_disk_path = file.path(tmpdir, "files") )
#> <vcr configuration>
-#>   Cassette Dir: /tmp/RtmporE3ZE/fixtures
+#>   Cassette Dir: /tmp/Rtmpw6cK9v/fixtures
 #>   Record: once
 #>   Serialize with: yaml
 #>   URI Parser: crul::url_parse
@@ -122,7 +122,7 @@ 

#> Logging?: FALSE #> ignored hosts: #> ignore localhost?: FALSE -#> Write disk path: /tmp/RtmporE3ZE/files

+#> Write disk path: /tmp/Rtmpw6cK9v/files

Then pass a file path (that doesn’t exist yet) to crul’s disk parameter. vcr will take care of handling writing the response to that file in addition to the cassette.

@@ -138,7 +138,7 @@

#> [1] TRUE
 out$parse()
-
#> [1] "{\n  \"args\": {}, \n  \"headers\": {\n    \"Accept\": \"application/json, text/xml, application/xml, */*\", \n    \"Accept-Encoding\": \"gzip, deflate\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\", \n    \"X-Amzn-Trace-Id\": \"Root=1-65bcaca2-7a50cb5878c03def0bb9e5dd\"\n  }, \n  \"origin\": \"13.90.131.63\", \n  \"url\": \"https://httpbin.org/get\"\n}\n"
+
#> [1] "{\n  \"args\": {}, \n  \"headers\": {\n    \"Accept\": \"application/json, text/xml, application/xml, */*\", \n    \"Accept-Encoding\": \"gzip, deflate\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"libcurl/7.81.0 r-curl/5.2.0 crul/1.4.0\", \n    \"X-Amzn-Trace-Id\": \"Root=1-65bcaf74-72f548fe428c1e5833ef0cde\"\n  }, \n  \"origin\": \"40.65.196.147\", \n  \"url\": \"https://httpbin.org/get\"\n}\n"

This also works with httr. The only difference is that you write to disk with a function httr::write_disk(path) rather than a parameter.

Note that when you write to disk when using vcr, the cassette is slightly