Giter Site home page Giter Site logo

httptest's Introduction

httptest: A Test Environment for HTTP Requests in R

Build Status codecov cran CII Best Practices

httptest makes it easy to write tests for code and packages that wrap web APIs. Testing code that communicates with remote servers can otherwise be painful: things like authentication, server state, and network flakiness can make testing seem too costly to bother with. The httptest package enables you to test all of the logic on the R sides of the API in your package without requiring access to the remote service.

Importantly, it provides multiple contexts that mock the network connection and tools for recording real requests for future offline use as fixtures, both in tests and in vignettes. The package also includes additional expectations to assert that HTTP requests were---or were not---made.

Using these tools, you can test that code is making the intended requests and that it handles the expected responses correctly, all without depending on a connection to a remote API. The ability to save responses and load them offline also enables you to write package vignettes and other dynamic documents that can be distributed without access to a live server.

This package bridges the gap between two others: (1) testthat, which provides a useful (and fun) framework for unit testing in R but doesn't come with tools for testing across web APIs; and (2) httr, which makes working with HTTP in R easy but doesn't make it simple to test the code that uses it. httptest brings the fun and simplicity together.

Installing

httptest can be installed from CRAN with

install.packages("httptest")

The pre-release version of the package can be pulled from GitHub using the remotes package:

# install.packages("remotes")
remotes::install_github("nealrichardson/httptest")

Using

To start using httptest with your package, run use_httptest() in the root of your package directory. This will

  • add httptest to "Suggests" in the DESCRIPTION file
  • add library(httptest) to tests/testthat/setup.R, which testthat loads before running tests

Then, you're ready to start using the tools that httptest provides. Here's an overview of how to get started. For a longer discussion and examples, see vignette("httptest"), and check out vignette("faq") for some common questions. See also the package reference for a list of all of the test contexts and expectations provided in the package.

In your test suite

The package includes several contexts, which you wrap around test code that would otherwise make network requests through httr. They intercept the requests and prevent actual network traffic from occurring.

with_mock_api() maps requests---URLs along with request bodies and query parameters---to file paths. If the file exists, its contents are returned as the response object, as if the API server had returned it. This allows you to test complex R code that makes requests and does something with the response, simulating how the API should respond to specific requests.

Requests that do not have a corresponding fixture file raise errors that print the request method, URL, and body payload, if provided. expect_GET(), expect_POST(), and the rest of the HTTP-request-method expectations look for those errors and check that the requests match the expectations. These are useful for asserting that a function call would make a correctly-formed HTTP request without the need to generate a mock, as well as for asserting that a function does not make a request (because if it did, it would raise an error in this context).

Adding with_mock_api() to your tests is straightforward. Given a very basic test that makes network requests:

test_that("Requests happen", {
  expect_s3_class(GET("http://httpbin.org/get"), "response")
  expect_s3_class(
    GET("http://httpbin.org/response-headers",
      query = list(`Content-Type` = "application/json")),
    "response"
  )
})

if we wrap the code in with_mock_api(), actual requests won't happen.

with_mock_api({
  test_that("Requests happen", {
    expect_s3_class(GET("http://httpbin.org/get"), "response")
    expect_s3_class(
      GET("http://httpbin.org/response-headers",
        query = list(`Content-Type` = "application/json")),
      "response"
    )
  })
})

Those requests will now raise errors unless we either (1) wrap them in expect_GET() and assert that we expect those requests to happen, or (2) supply mocks in the file paths that match those requests. We might get those mocks from the documentation for the API we're using, or we could record them ourselves---and httptest provides tools for recording.

Another context, capture_requests(), collects the responses from requests you make and stores them as mock files. This enables you to perform a series of requests against a live server once and then build your test suite using those mocks, running your tests in with_mock_api.

In our example, running this once:

capture_requests({
  GET("http://httpbin.org/get")
  GET("http://httpbin.org/response-headers",
    query = list(`Content-Type` = "application/json"))
})

would make the actual requests over the network and store the responses where with_mock_api() will find them.

For convenience, you may find it easier in an interactive session to call start_capturing(), make requests, and then stop_capturing() when you're done, as in:

start_capturing()
GET("http://httpbin.org/get")
GET("http://httpbin.org/response-headers",
  query = list(`Content-Type` = "application/json"))
stop_capturing()

Mocks stored by capture_requests are written out as plain-text files. By storing fixtures as human-readable files, you can more easily confirm that your mocks look correct, and you can more easily maintain them if the API changes subtly without having to re-record them (though it is easy enough to delete and recapture). Text files also play well with version control systems, such as git.

When recording requests, httptest redacts the standard ways that auth credentials are passed, so you won't accidentally publish your personal tokens. The redacting behavior is fully customizable: you can programmatically sanitize or alter other parts of the request and response. See vignette("redacting") for details.

In your vignettes

Package vignettes are a valuable way to show how to use your code, but when communicating with a remote API, it has been difficult to write useful vignettes. With httptest, however, by adding as little as one line of code to your vignette, you can safely record API responses from a live session, using your secret credentials. These API responses are scrubbed of sensitive personal information and stored in a subfolder in your vignettes directory. Subsequent vignette builds, including on continuous-integration services, CRAN, and your package users' computers, use these recorded responses, allowing the document to regenerate without a network connection or API credentials. To record fresh API responses, delete the subfolder of cached responses and re-run.

To use httptest in your vignettes, add a code chunk with start_vignette() at the beginning, and for many use cases, that's the only thing you need. If you need to handle changes of server state, as when you make an API request that creates a record on the server, add a call to change_state(). See vignette("vignettes") for more discussion and links to examples.

Contributing

Suggestions and pull requests are more than welcome!

For developers

The repository includes a Makefile to facilitate some common tasks from the command line, if you're into that sort of thing.

Running tests

$ make test. You can also specify a specific test file or files to run by adding a "file=" argument, like $ make test file=offline. test_package will do a regular-expression pattern match within the file names. See its documentation in the testthat package.

Updating documentation

$ make doc. Requires the roxygen2 package.

httptest's People

Contributors

brooklynbagel avatar byapparov avatar dmenne avatar erdaradungaztea avatar jonkeane avatar kforner avatar lionel- avatar maelle avatar natbprice avatar nealrichardson avatar stephenashton-dhsc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

httptest's Issues

Problem with with_mock_dir()/gsub_request()

๐Ÿ‘‹ here!

I'm trying to apply https://enpiar.com/r/httptest/index.html#how-do-i-fix-non-portable-file-paths

I've made a minimal example to show what does not work: https://github.com/maelle/teeest

  • The tests uses with_mock_dir().
  • I've a inst/httptest/request.R.

However the fixture still lives in a series of subfolders that ignore the call to httptest::set_requester().

I've set a browser() in that call when running the test interactively to check how the request was amended. It is amended correctly! So something else is going wrong further down the line at the stage of creating the files? I'm a bit lost. ๐Ÿ˜…

httptest does not work with future multiprocess

I have a function that requests some data from an API using this piece of code:

df %>%
        mutate(new_column = future_map(id, function(x) {
          GET(create_request(x), authenticate(username, password, type = "basic"))
        },
        .progress = TRUE))

where create_request(x) creates a string with endpoint to call e.g. https://my_api.com/endpoint1/x.
The code like that works just fine in with_mock_api function but, if I put plan(multiprocess) at the beginning I get an error

Could not resolve host: my_api.com

Is there any way to make it work with httptest in parallel with furrr package?

how to test for sequence of responses

e.g. when the API returns 502, 502, 200.

Suggestion: making httptest able to loop through mockfile if they are named

  • filename-corresponding-to-the-request-1
  • filename-corresponding-to-the-request-2
  • filename-corresponding-to-the-request-3
  • ...

naive question: why do the redactor and request functions live in inst/httptest?

When setting up httptest one needs to tweak

  • DESCRIPTION (well the use_httptest() function does)
  • test files
  • the helper that was set up by use_httptest()
  • potentially the redactor and request function in inst/httptest.

Why can't the the redactor and request function be defined in the helper.R instead? Alternatively, could use_httptest() create them even if they're empty?

Could there be an FAQ index?

At the moment the FAQ lives in the homepage so to get an overview of all questions one needs to scroll down.

Why not make the FAQ a re-usable Rmd fragment and have it both on the homepage and in a dedicated vignette? This way the FAQ vignette would have a TOC on the right.

use_httptest() not exported in version 3.1.0 (CRAN's version)

hi,

It seems your guidelines on how to use this package are broken for CRAN's version 3.1.0 because the function use_httptest() is not exported.

Works fine though with version 3.1.1. (installed from github):

# Previously installed from CRAN with install.packages("httptest")
library(httptest)
# Loading required package: testthat
packageVersion("httptest")
# [1] โ€˜3.1.0โ€™
"use_httptest" %in% ls("package:httptest")
# [1] FALSE
# Installing now the dev version from Github
detach("package:httptest", unload=TRUE)
devtools::install_github("nealrichardson/httptest")

# Downloading GitHub repo nealrichardson/httptest@master
# (...)
# * DONE (httptest)
library(httptest)
packageVersion("httptest")
# [1] โ€˜3.1.1โ€™
"use_httptest" %in% ls("package:httptest")
# [1] TRUE

Update GHA workflows

  • to use the r-lib/actions v2;
  • to have GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}.

I'll make a PR.

Add documentation on how to test for error behavior

Writing down what I think I understood

In all cases a good package structure seems to help, e.g. in your blog post example having crGET() makes things easier.
Also, if one wants to test the exact same function calls in two situations (server up, server down), one might need to play with .mockPaths().

capture_requests doesn't capture responses

Following your example I want to capture requests responses and use them later for tests. So, I tried

capture_requests({
    GET("http://httpbin.org/get")
    GET("http://httpbin.org/response-headers",
        query=list(`Content-Type`="application/json"))
})

and I got in RStudio terminal

> capture_requests({
+     GET("http://httpbin.org/get")
+     GET("http://httpbin.org/response-headers",
+         query=list(`Content-Type`="application/json"))
+ })
Response [http://httpbin.org/response-headers?Content-Type=application%2Fjson]
  Date: 2018-08-21 12:22
  Status: 200
  Content-Type: application/json
  Size: 104 B
{
  "Content-Length": "104", 
  "Content-Type": [
    "application/json", 
    "application/json"
  ]
}

but no files have been saved.
Changing path argument didn't help, setting options(httptest.verbose=TRUE) also did nothing. I got the same result with start_capturing()...stop_capturing() approach. What is wrong, why this function doesn't work as supposed to?

Feature: Compressed Cache

Problem
For my package, I'm parsing poorly structured HTML, leading to the need to test a large number of pages, in turn leading to a very large cache (48 MB and ever increasing).

Human readability of the cache is not required in my case, so there would be a huge savings in disk space and package size to compress the cache.

Solution Outline
Reading the cache would have an additional check if a compressed version exists should the initial file not be present.

Writing would create a compressed version if an environment variable is set (httpcache.compress = T?)

Alternatives
My plan B is to move to use httptest only for dev testing and all CRAN testing on manually downloaded copies.

I can do the implementation work if useful.

How to squelch "No encoding supplied: defaulting to UTF-8." for with_mock_dir()?

In a test with with_mock_dir() I get the message "No encoding supplied: defaulting to UTF-8." that I see is squelched for capture_requests() but not in this context.

Repo https://github.com/maelle/msghttptest
Test https://github.com/maelle/msghttptest/blob/main/tests/testthat/test-msg.R
Snap https://github.com/maelle/msghttptest/blob/main/tests/testthat/_snaps/msg.md
Mock file https://github.com/maelle/msghttptest/blob/main/tests/testthat/pr0/api.github.com/user.json

with_mock_api() does not fully clean up (untrace)

i have a test where i use httptest like this

test_that("check list parameter", {
httptest::with_mock_api({
expect_error(testClient$getMatrixData(c(10)), regexp = "listOfMatrixNames needs to be a vector of strings")
expect_error(testClient$getMatrixData(10), regexp = "listOfMatrixNames needs to be a vector of strings")
expect_error(testClient$getMatrixData("10", 10), regexp = "repository needs to be a string")
expect_error(testClient$getMatrixData("bla"), regexp = "POST.")
expect_error(testClient$getMatrixData(c("bla", "blub")), regexp = "POST.
")
})
})

and that works fine. i have also tests where i do real http request but if they run together in the same R session the real connection tests start throwing exception like this.

Error in readBin(con, "raw", nbytes) : invalid connection

That happens when uploading files using httr::upload_file(). i have to restart the session to make the integration tests pass again.

I include library("httptest") in setup.R (which is used by both, integration tests and mock tests)

R version: 4.1.0
Httr: 1.4.2
Httptest: 4.1.0

my crazy wild guess:
httr::POST is missing here?

), where = httr::PUT, print = getOption("httptest.debug", FALSE)))

Using httptest in vignettes: How to make sure that recorded requests are actually used when building the package?

Thanks a lot for your great work on this package.

I'm using httptest in the vignette for a package that interacts with an API. The API credentials are stored in my .renviron file. I am using the process as described in the "Vignettes" vignette. After building the vignette locally, I also have the expected folders and file names on my computer that should then be used by httptest in the vignette.

However, it seems that httptest ignores the recorded requests. This is most obvious when building a source package and checking it on another machine, which leads to the following warning.

* checking re-building of vignette outputs ... WARNING
Error(s) in re-building vignettes:
  ...
--- re-building 'introduction_to_pocketapi.Rmd' using rmarkdown
Quitting from lines 85-90 (introduction_to_pocketapi.Rmd) 
Error: processing vignette 'introduction_to_pocketapi.Rmd' failed with diagnostics:
POCKET_CONSUMER_KEY does not exist as environment variable. Add it to your R environment or manually specify the consumer_key argument.
--- failed re-building 'introduction_to_pocketapi.Rmd'

SUMMARY: processing the following file failed:
  'introduction_to_pocketapi.Rmd'
Error: Vignette re-building failed.
Execution halted

I suspect that this is related to a similar problem that I experienced with unit testing when httptest was expecting a specific hashed file name for the stored request, but the file name when capturing the requests was different. Here, it was possible to identify the expected file name based on the error message and manually change it; however, I could not figure out which file name was expected for the requests in the vignette.
Could you please help me understand how I could nudge httptest to use the actually stored requests for the vignette?

The full code incl. the recorded requests can be found here. It's built based on your vignette, but I've also checked the "rcrunch" and "pivotaltrackr" vignettes to check if there is an issue with the vignette code (and couldn't find any).

Thanks in advance for your help!

Tests using with_mock_api() fail with devtools::test() but not with devtools::check()

hi

I do not know why but all my tests using with_mock_api() fail when I run devtools::test().

If I run devtools::check() instead all tests run fine.

Could this be an issue with httptest or with devtools?

devtools::check()

> devtools::check()
Updating gwasrapidd documentation
Writing NAMESPACE
Loading gwasrapidd
Writing NAMESPACE
โ”€โ”€ Building โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ gwasrapidd โ”€โ”€
Setting env vars:
โ— CFLAGS    : -Wall -pedantic -fdiagnostics-color=always
โ— CXXFLAGS  : -Wall -pedantic -fdiagnostics-color=always
โ— CXX11FLAGS: -Wall -pedantic -fdiagnostics-color=always
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
โœ”  checking for file โ€˜/home/rmagno/sci/code/R/pkg/gwasrapidd/DESCRIPTIONโ€™ ...
โ”€  preparing โ€˜gwasrapiddโ€™:
โœ”  checking DESCRIPTION meta-information ...
โ”€  installing the package to build vignettes
โœ”  creating vignettes (1m 3.4s)
โ”€  checking for LF line-endings in source and make files and shell scripts
โ”€  checking for empty or unneeded directories
โ”€  looking to see if a โ€˜data/datalistโ€™ file should be added
โ”€  building โ€˜gwasrapidd_0.99.1.tar.gzโ€™
   
โ”€โ”€ Checking โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ gwasrapidd โ”€โ”€
Setting env vars:
โ— _R_CHECK_CRAN_INCOMING_USE_ASPELL_: TRUE
โ— _R_CHECK_CRAN_INCOMING_REMOTE_    : FALSE
โ— _R_CHECK_CRAN_INCOMING_           : FALSE
โ— _R_CHECK_FORCE_SUGGESTS_          : FALSE
โ”€โ”€ R CMD check โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
โ”€  using log directory โ€˜/tmp/Rtmpxl8X77/gwasrapidd.Rcheckโ€™
โ”€  using R version 3.6.0 (2019-04-26)
โ”€  using platform: x86_64-pc-linux-gnu (64-bit)
โ”€  using session charset: UTF-8
โ”€  using options โ€˜--no-manual --as-cranโ€™ (3s)
โœ”  checking for file โ€˜gwasrapidd/DESCRIPTIONโ€™
โ”€  checking extension type ... Package
โ”€  this is package โ€˜gwasrapiddโ€™ version โ€˜0.99.1โ€™
โ”€  package encoding: UTF-8
โœ”  checking package namespace information ...
โœ”  checking package dependencies (1.6s)
โœ”  checking if this is a source package ...
โœ”  checking if there is a namespace
โœ”  checking for executable files (8.4s)
โœ”  checking for hidden files and directories ...
โœ”  checking for portable file names ...
โœ”  checking for sufficient/correct file permissions ...
โœ”  checking whether package โ€˜gwasrapiddโ€™ can be installed (3s)
โœ”  checking installed package size ...
โœ”  checking package directory ...
โœ”  checking for future file timestamps (402ms)
โœ”  checking โ€˜buildโ€™ directory
โœ”  checking DESCRIPTION meta-information ...
โœ”  checking top-level files
โœ”  checking for left-over files ... 
โœ”  checking index information ...
โœ”  checking package subdirectories ...
โœ”  checking R files for non-ASCII characters ...
โœ”  checking R files for syntax errors ...
โœ”  checking whether the package can be loaded (365ms)
โœ”  checking whether the package can be loaded with stated dependencies ...
โœ”  checking whether the package can be unloaded cleanly ...
โœ”  checking whether the namespace can be loaded with stated dependencies ...
โœ”  checking whether the namespace can be unloaded cleanly ...
โœ”  checking loading without being on the library search path (353ms)
โœ”  checking dependencies in R code (546ms)
โœ”  checking S3 generic/method consistency (758ms)
โœ”  checking replacement functions ...
โœ”  checking foreign function calls (432ms)
โœ”  checking R code for possible problems (4s)
โœ”  checking Rd files (362ms)
โœ”  checking Rd metadata ...
โœ”  checking Rd line widths ...
โœ”  checking Rd cross-references ...
โœ”  checking for missing documentation entries (339ms)
โœ”  checking for code/documentation mismatches (1.1s)
โœ”  checking Rd \usage sections (1.1s)
โœ”  checking Rd contents ...
โœ”  checking for unstated dependencies in examples ...
โœ”  checking contents of โ€˜dataโ€™ directory ...
โœ”  checking data for non-ASCII characters (373ms)
โœ”  checking data for ASCII and uncompressed saves ...
โœ”  checking R/sysdata.rda ...
โœ”  checking installed files from โ€˜inst/docโ€™ ...
โœ”  checking files in โ€˜vignettesโ€™ ...
โœ”  checking examples (15.1s)
   Examples with CPU or elapsed time > 5s
                 user system elapsed
   get_variants 1.644  0.052   6.887
โœ”  checking for unstated dependencies in โ€˜testsโ€™ ...
โ”€  checking tests ...
โœ”  Running โ€˜spelling.Rโ€™
โœ”  Running โ€˜testthat.Rโ€™ [17s/20s] (20.3s)
โœ”  checking for unstated dependencies in vignettes (20.5s)
โœ”  checking package vignettes in โ€˜inst/docโ€™ ...
โœ”  checking re-building of vignette outputs (55.1s)
   
   
โ”€โ”€ R CMD check results โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ gwasrapidd 0.99.1 โ”€โ”€โ”€โ”€
Duration: 2m 2.3s

0 errors โœ” | 0 warnings โœ” | 0 notes โœ”

devtools::test(filter = 'get_metadata')

> devtools::test(filter = 'get_metadata')
Loading gwasrapidd
Testing gwasrapidd
โœ” |  OK F W S | Context
โœ– |  24 1     | test-metadata [0.5 s]
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
test-get_metadata.R:175: error: get_metadata
GET https://www.ebi.ac.uk/gwas/rest/api/metadata (www.ebi.ac.uk/gwas/rest/api/metadata.json)
1: expect_identical(get_metadata(), lst0) at /home/rmagno/sci/code/R/pkg/gwasrapidd/tests/testthat/test-get_metadata.R:175
2: quasi_label(enquo(object), label, arg = "object")
3: eval_bare(get_expr(quo), get_env(quo))
4: get_metadata()
5: gc_request("/metadata", verbose = verbose, warnings = warnings) at /home/rmagno/sci/code/R/pkg/gwasrapidd/R/get_metadata.R:86
6: httr::GET(url, user_agent_id) at /home/rmagno/sci/code/R/pkg/gwasrapidd/R/request.R:39
7: request_perform(req, hu$handle$handle)
8: request_fetch(req$output, req$url, handle)
9: (function (req, handle, refresh) 
   {
       req$url <- sub("^:///", "", req$url)
       f <- buildMockURL(get_current_requester()(req))
       mockfile <- find_mock_file(f)
       if (!is.null(mockfile)) {
           return(load_response(mockfile, req))
       }
       req$mockfile <- f
       return(stop_request(req))
   })(req)
10: stop_request(req)
11: stop(out, call. = FALSE)
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

โ•โ• Results โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
Duration: 0.5 s

OK:       24
Failed:   1
Warnings: 0
Skipped:  0

Add trick to doc of build_mock_url?

When trying to guess the correct path for a mock file one could read the docs of build_mock_url() or run a test with with_mock_api as the error message will contain the filepath. Should the latter, although it's documented elsewhere, be mentioned in the docs of build_mock_url()?

Support httr::RETRY() in capture_requests()

Hi Neal!

I'm updating some tests and noticed that none of the mock files are being written, so giving failures when using with_mock_API()

My example:

library(googleLanguageR)
# auto auth

library(httptest)

test_text <- "Translate this"

capture_requests(
  gl_translate(test_text), path = ".", verbose = TRUE
)

with_mock_api(gl_translate(test_text))

sessionInfo()

dir.exists("translation.googleapis.com")

which gives:

> library(googleLanguageR)
> # auto auth
> 
> library(httptest)
> 
> test_text <- "Translate this"
> 
> capture_requests(
+   gl_translate(test_text), path = ".", verbose = TRUE
+ )
2018-06-15 10:28:30 -- Translating text: 14 characters - 
# A tibble: 1 x 3
  translatedText detectedSourceLanguage text          
* <chr>          <chr>                  <chr>         
1 Translate this en                     Translate this
> 
> with_mock_api(gl_translate(test_text))
2018-06-15 10:28:30 -- Translating text: 14 characters - 
Error: POST https://translation.googleapis.com/language/translate/v2/ target=en&format=text&source=&q=Translate%20this (translation.googleapis.com/language/translate/v2-b2807c-POST.json)
Request failed [ERROR]. Retrying in 1 seconds...
Error: POST https://translation.googleapis.com/language/translate/v2/ target=en&format=text&source=&q=Translate%20this (translation.googleapis.com/language/translate/v2-b2807c-POST.json)
Request failed [ERROR]. Retrying in 1 seconds...
Error : POST https://translation.googleapis.com/language/translate/v2/ target=en&format=text&source=&q=Translate%20this (translation.googleapis.com/language/translate/v2-b2807c-POST.json)
 Show Traceback
 
 Rerun with Debug
 Error: Request failed before finding status code: POST https://translation.googleapis.com/language/translate/v2/ target=en&format=text&source=&q=Translate%20this (translation.googleapis.com/language/translate/v2-b2807c-POST.json) > 
> sessionInfo()
R version 3.5.0 (2018-04-23)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.5

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] httptest_3.0.1             testthat_2.0.0             googleLanguageR_0.1.1.9000

loaded via a namespace (and not attached):
 [1] utf8_1.1.4        crayon_1.3.4      digest_0.6.15     assertthat_0.2.0  R6_2.2.2         
 [6] jsonlite_1.5      magrittr_1.5      pillar_1.2.3      httr_1.3.1        cli_1.0.0        
[11] rlang_0.2.1       curl_3.2          rstudioapi_0.7    googleAuthR_0.6.2 tools_3.5.0      
[16] purrr_0.2.5       yaml_2.1.19       compiler_3.5.0    base64enc_0.1-3   memoise_1.1.0    
[21] openssl_1.0.1     tibble_1.4.2     
> 
> dir.exists("translation.googleapis.com")
[1] FALSE

Release httptest 4.0.0

cc @jonkeane @maelle

There are a few new features, plus better support for testthat 3rd edition, so a release would be good.

Checklist:

How to get rid of "tracing function "form_file" in package "curl""

Great package - I had some fears that I would be fiddling a day to get the configuration right, things just worked out of the box!

A minor issue: My test output is a bit polluted by this output

tracing function "form_file" in package "curl"

which I do not understand where it comes from. How do I suppress it?

Note: I am using the "third edition":
https://testthat.r-lib.org/articles/third-edition.html

so I cannot rule out that this is the result of the new "messages are not suppressed" feature.

== Testing test-AuthenticatedAccount.R ===============================================================
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 0 ]Loading required package: curl
Tracing function "form_file" as seen from package "httr"
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 4 ] Done!
Untracing function "form_file" in package "curl"

Testing GET Rest API with http://{domain}/

Hi,

I am not sure how to use with_mock_API against the full URL that contains http(s)://{domain}.
Where the json files should be located in this case?

For example if I want to do something like this in my package:

    response <- GET(url  = "https://mydomain.zendesk.com",
                    path = "api/v2/incremental/tickets.json?update_date=3423423",
                    add_headers("Content-Type" = "application/json"),
                    authenticate(user = Sys.getenv("ZENDESK_USER"),
                                 password = Sys.getenv("ZENDESK_PASSWORD"))

Can I mock the response?

Testing response headers

Some APIs rely on values in the response headers. Is there a graceful way to test headers?

Thanks
Bulat

Creating a package of extra-large cache files

Hi Neal,

In #11 you suggested possibly creating a data package of test files that is downloaded during testing. Do you have any suggestions as to best-practices for doing this?

I'm trying to mentally model what that might be and would love thoughts on the following process:

  • uploading test files to a separately tracked/version-controlled git repository that doesn't go to CRAN (since CRAN data packages also have a 5MB limit)
  • doing a "skip_on_cran" or "skip_if_unavailable" check for the github hosted data
  • in a helper/setup file, downloading the data into a tempdir and then setting .mockPaths to the tempdir
  • running all the tests
  • on teardown, cleaning up by deleting the tempdir

Is there anything I'm missing from doing it with this approach? (Maybe @maelle has some thoughts as well?)

expect_GET() and others don't detect httr calls

Problem

I noticed that the whole expect_verb() family doesn't detect any calls made through httr; including these used as examples to docs.

# An example of a call that throws no error even though it should
httptest::expect_no_request(httr::GET("http://example.com"))

However, httr is detected in at least some functions:

# This code throws error as expected
httptest::without_internet(httr::GET("http://httpbin.org/get"))

Session info

R version 4.1.2 (2021-11-01)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19044)

Matrix products: default

locale:
[1] LC_COLLATE=English_United Kingdom.1252 LC_CTYPE=English_United Kingdom.1252
[3] LC_MONETARY=English_United Kingdom.1252 LC_NUMERIC=C
[5] LC_TIME=English_United Kingdom.1252

attached base packages:
[1] stats graphics grDevices utils datasets methods base

other attached packages:
[1] httr_1.4.2 httptest_4.1.0 woodendesc_0.0.0.9000 usethis_2.1.5
[5] testthat_3.1.2

loaded via a namespace (and not attached):
[1] Rcpp_1.0.8 pillar_1.7.0 compiler_4.1.2 base64enc_0.1-3 tools_4.1.2 pkgload_1.2.4
[7] digest_0.6.29 jsonlite_1.8.0 lifecycle_1.0.1 tibble_3.1.6 pkgconfig_2.0.3 rlang_1.0.1
[13] cli_3.2.0 rstudioapi_0.13 yaml_2.3.5 curl_4.3.2 crul_1.2.0 withr_2.5.0
[19] desc_1.4.1 fauxpas_0.5.0 fs_1.5.2 vctrs_0.3.8 triebeard_0.3.0 rprojroot_2.0.2
[25] glue_1.6.2 httpcode_0.3.0 R6_2.5.1 fansi_1.0.2 purrr_0.3.4 vcr_1.0.2
[31] webmockr_0.8.0 magrittr_2.0.2 whisker_0.4 urltools_1.7.3 ellipsis_0.3.2 utf8_1.2.2
[37] crayon_1.5.1 brio_1.1.3

use with_mock_api without hashed filename

I need to do POST requests which include fields that change over time like timestamp, r version, package versions etc. The current logic of hashing body prevents me from using with_mock_api because it never finds the correct mock file. Is there already a way of bypassing this? Looking at the source code there seems to be no option for it.

with_mock_api in combination with httr::upload_file seems to leak on win10

when i run my tests, i constantly see warnings like this:

Warning in .Internal(gc(verbose, reset, full)) :
closing unused connection 4 (./testdata/lfr/upload_test.md)

where upload_test.md is the file i use with httr::upload_file

by running tests i mean running devtools::test() in the same console multiple times

Bad redacting when URL parameters are URL themselves

Example:

The URL:

https://www.ebi.ac.uk/gwas/rest/api/studies/search/findByEfoUri?uri=http://www.ebi.ac.uk/efo/EFO_0004761&page=0&size=20

Gets written as:

<pkg_path>/tests/testthat/www.ebi.ac.uk/efo/EFO_0004761&page=0&size=20.json

If I use instead:

https://www.ebi.ac.uk/gwas/rest/api/studies/search/findByEfoUri?uri=http:%2f%2fwww.ebi.ac.uk%2fefo%2fEFO_0004761

Then I get the expected mock file:

<pkg_path>/tests/testthat/www.ebi.ac.uk/gwas/rest/api/studies/search/findByEfoUri-5bb3d9.json

Interaction with memoise

Just a note for info (should httptest get discussions?)

I had a case with tests passing on Ubuntu and failing on macOS and Windows. I first thought the mock file was not found despite its being there but it turns out it was not there.

  • Test 1 (test_that()) was creating a mock dir, everything ok.
  • Test 2 (test_that() in the same test file) was creating a mock dir but not all mock files because some of the requests had been memoised.

I'm not even sure what the OS problem was but in conclusion one should pay attention when capturing requests that are memoised. ๐Ÿ˜… Maybe too niche for any doc though?

Use absolute paths for .mockPaths() for tests using a temporary directory?

If a test sets a temporary directory like so https://github.com/maelle/setwdsetwd/blob/main/tests/testthat/test-request.R then no mock directory is set in the actual package.

If I comment out the line with withr::local_dir(tempdir) then a mock dir is created.

My real use case consists in testing a package that is used for package development, therefore most tests use throwaway packages, and most functions work on the current directory so I really need to change the working directory.

I think this problem might affect httptest2 too.

This issue reminds me of r-lib/testthat#1359

Mock file names with special characters are not matched correctly

I am performing a GET request against Azure Table Service REST API. You can see the request format here: https://docs.microsoft.com/en-us/rest/api/storageservices/query-entities#request

I am able to generate mock files, but I think these files are not being found when using use_mock_api(). The error message would be clearer if it stated something to the effect of "Mock file not found", but I just get an error with my request and the name of the mock file which is not very helpful for debugging.

Error: GET https://mystorageaccount.table.core.windows.net/MyTable(PartitionKey='1',RowKey='01daa220815781f3885bead08130bace5635d83387bfcc674d48b7dda77f31ac959601038914ccf25e7549715a2a0482e8aa06e923f858686fa1b0e0f6ed49d1') (mystorageaccount.table.core.windows.net/MyTable(PartitionKey='1',RowKey='01daa220815781f3885bead08130bace5635d83387bfcc674d48b7dda77f31ac959601038914ccf25e7549715a2a0482e8aa06e923f858686fa1b0e0f6ed49d1').json)

I believe I have traced the error to the following line:

mockbasename <- paste0("^", basename(mp), ".[[:alnum:]]*$")

I think you should treat the mock file name as literal to account for special characters that are not escaped. This seemed to work for me:

mockbasename <- paste0("^\\Q", basename(mp), "\\E.[[:alnum:]]*$")

How to implement for higher level API functions

Hello, this looks perfect for my caching issues as you noted in your review of googleLanguageR (and others based on googleAuthR)

I can record the API requests ok (the files appear in /mock/), but when trying to use the cache it looks like it expects the function responses to be httr responses.

I tried this:

library(httptest)
library(googleLanguageR)

local_auth <- Sys.getenv("GL_AUTH") != ""
if(!local_auth){
  cat("\nNo authentication file detected - skipping integration tests\n")
} else {
  cat("\nPerforming API calls for integration tests\n")
}

.mockPaths("mock")

context("Creating record of API tests if online")

test_that("Record requests", {
  skip_if_disconnected()
  skip_if_not(local_auth)

  ## test reqs
  test_text <- "The cat sat on the mat"
  capture_requests(
    path = "mock", {
      nlp <- gl_nlp(test_text)
    })

})


with_mock_API({
  context("Unit tests - NLP")

  test_that("NLP returns expected fields", {

    test_text <- "The cat sat on the mat"
    nlp <- gl_nlp(test_text)

    expect_equal(nlp$sentences$text$content, test_text)

  })
})
#Error : POST https://language.googleapis.com/v1/documents:annotateText/ {"document":
#{"type":"PLAIN_TEXT","language":"en","content":"The cat sat on the
# mat"},"encodingType":"UTF8","features":
#{"extractSyntax":true,"extractEntities":true,"extractDocumentSentiment":true}} #(language.googleapis.com/v1/documents:annotateText-45bb10-POST.json)
#Error : is.response(x) is not TRUE

If this working as intended, should I then perhaps look at integrating with_mock_API() into googleAuthR at the time of the request?

Failures when running the tests interactively

For info as it doesn't seem to impede development :-)

I see the messages are in French, will try to fix

devtools::test( "path to httptest")
#> Loading httptest
#> Le chargement a nรฉcessitรฉ le package : testthat
#> Testing httptest
#> โœ” |  OK F W S | Context
#> โ  |   0       | capture-requests                                                โ ‹ |   1       | capture-requests                                                โ ผ |   5       | capture-requests                                                โ ‹ |  11       | capture-requests                                                โ ธ |  14       | capture-requests                                                โ ผ |  15       | capture-requests                                                โ  |  20       | capture-requests                                                โ ผ |  24     1 | capture-requests                                                โ ง |  27     1 | capture-requests                                                โ  |  29     1 | capture-requests                                                โœ” |  30     1 | capture-requests [3.9 s]
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> Skip (test-capture-requests.R:149:9): Recorded JSON is prettified
#> Reason: TODO: prettify when simplify=FALSE
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> โ  |   0       | content-type                                                    โœ” |   4       | content-type
#> โ  |   0       | expect-header                                                   โ ผ |   5       | expect-header                                                   โ ™ |  11     1 | expect-header                                                   โ ด |  15     1 | expect-header                                                   โ ฆ |  16     1 | expect-header                                                   โ ง |  17     1 | expect-header                                                   โœ” |  17     1 | expect-header [0.8 s]
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> Skip (test-expect-header.R:40:13): expect_header with mock API
#> Reason: third_edition  is TRUE
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> โ  |   0       | fake-http                                                       โ ง |   8       | fake-http                                                       โ น |  23       | fake-http                                                       โ ง |  38       | fake-http                                                       โœ” |  42       | fake-http [0.3 s]
#> โ  |   0       | find-redactor                                                   โ ง |   8       | find-redactor                                                   โ น |  13       | find-redactor
#> Using redact.R from "testpkg"
#> โ ธ |  14       | find-redactor                                                   โ ด |  16       | find-redactor                                                   โ ‡ |  18 1     | find-redactor                                                   โœ– |  18 1     | find-redactor [1.5 s]
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> ERROR (test-find-redactor.R:99:13): Loading a package with pkgload (devtools)
#> Error: l'espace de noms 'pkgload' est importรฉ par 'devtools' et ne peut, donc, pas รชtre dรฉchargรฉ
#> Backtrace:
#>  1. base::unloadNamespace("pkgload") test-find-redactor.R:99:12
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> โ  |   0       | json-equal                                                      โœ” |   7       | json-equal
#> โ  |   0       | mock-api                                                        โ ผ |   5       | mock-api                                                        โ ด |  16       | mock-api                                                        โ ง |  28       | mock-api                                                        โ ‹ |  31       | mock-api                                                        โœ” |  46       | mock-api [0.6 s]
#> โ  |   0       | mock-paths                                                      โ ‡ |   9       | mock-paths                                                      โœ” |  13       | mock-paths [0.1 s]
#> โ  |   0       | offline                                                         โ ผ |   4     1 | offline                                                         โœ” |   5     1 | offline [0.1 s]
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> Skip (test-offline.R:11:9): skip_if_disconnected when disconnected
#> Reason: This should skip
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> โ  |   0       | public                                                          โœ– |   1 1     | public
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> FAILURE (test-public.R:7:9): If a function is not exported, the public test context errors
#> `.internalFunction()` did not throw the expected error.
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> โ  |   0       | redact                                                          โ ‹ |   1       | redact                                                          โ ธ |   4       | redact                                                          โ ฆ |   7       | redact                                                          โ น |  13       | redact                                                          โ ‹ |  21       | redact                                                          โ ธ |  24       | redact                                                          โ ฆ |  27       | redact                                                          โ  |  35 3 1 1 | redact                                                          โœ– |  38 3 1 1 | redact [1.2 s]
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> FAILURE (test-redact.R:173:9): But the mock file gets written to the modified path with altered content
#> `object` did not throw the expected error.
#> 
#> Warning (test-redact.R:173:9): But the mock file gets written to the modified path with altered content
#> 4 components of `...` were not used.
#> 
#> We detected these problematic arguments:
#> * `fixed`
#> * `ignore.case`
#> * `perl`
#> * `useBytes`
#> 
#> Did you misspecify an argument?
#> Backtrace:
#>  1. httptest::expect_GET(GET("http://example.com/get"), "http://example.com/get") test-redact.R:173:8
#>  2. httptest::expect_mock_request(object, "GET ", url, " ", ...) /home/maelle/Documents/ropensci/httptest/R/expect-request.R:46:4
#>  3. request_happened()(...) /home/maelle/Documents/ropensci/httptest/R/expect-request.R:89:4
#>  4. testthat:::expect_condition_matching(...)
#>  6. ellipsis:::action_dots(...)
#> 
#> FAILURE (test-redact.R:175:9): But the mock file gets written to the modified path with altered content
#> `alt <- GET("http://example.com/fakeurl")` threw an unexpected error.
#> Message: GET http://example.com/fakeurl (example.com/fakeurl.json)
#> Class:   simpleError/error/condition
#> Backtrace:
#>   1. testthat::expect_error(...) test-redact.R:175:8
#>   7. httr::GET("http://example.com/fakeurl")
#>   8. httr:::request_perform(req, hu$handle$handle)
#>   9. httr:::request_fetch(req$output, req$url, handle)
#>  11. httptest::stop_request(req) /home/maelle/Documents/ropensci/httptest/R/mock-api.R:62:4
#> 
#> ERROR (test-redact.R:176:9): But the mock file gets written to the modified path with altered content
#> Error: objet 'alt' introuvable
#> Backtrace:
#>  1. testthat::expect_identical(content(alt), list(changed = TRUE)) test-redact.R:176:8
#>  4. httr::content(alt)
#>  6. httr:::is.response(x)
#> 
#> Skip (test-redact.R:203:9): gsub_response handles URL encoding
#> Reason: TODO: handle URL escaping
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> โ  |   0       | trace                                                           โ ธ |   4       | trace                                                           โœ” |   8       | trace [0.2 s]
#> โ  |   0       | use-httptest                                                    โ ฆ |  17       | use-httptest                                                    โœ” |  35       | use-httptest [0.2 s]
#> โ  |   0       | vignette                                                        โ ‹ |   1       | vignette                                                        โ น |  13       | vignette                                                        โœ” |  16       | vignette [1.0 s]
#> โ  |   0       | without-internet                                                โ ‹ |   1       | without-internet                                                โ ธ |   4       | without-internet                                                โ ‹ |  11       | without-internet                                                โœ” |  15     1 | without-internet [0.6 s]
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> Skip (test-without-internet.R:50:13): max.print option
#> Reason: third_edition  is TRUE
#> โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> 
#> โ•โ• Results โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
#> Duration: 10.5 s
#> 
#> โ”€โ”€ Skipped tests  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
#> โ— TODO: handle URL escaping (1)
#> โ— TODO: prettify when simplify=FALSE (1)
#> โ— This should skip (1)
#> โ— third_edition  is TRUE (2)
#> 
#> [ FAIL 5 | WARN 1 | SKIP 5 | PASS 295 ]

Created on 2020-10-21 by the reprex package (v0.3.0.9001)

Integration tests with captured requests

Hi

First: I'm really enjoying this package!

Second: I have question about how I can use the captured requests as part of integration tests.
My situation is that I use {httptest} to run fast unit tests with mocked HTTP calls. But (sometimes) I also want to actually make the call and compare the result with the captured request. This has saved at least one regression in the API.

I can see something like this working:

response <- <function to make the call>
old_response_file <- file.path("capture folder", paste0(httptest::build_mock_url(response$request), ".json"))
# Compare httr::content(response, as = "text") with content of "old_response_file"

This would some extra lines of code in each integration test, but it seems to me that save_response does most of this already. Have I overlooked a feature in {httptest} to do this? If not, would you be interested in such a function?

Package redactors should load when testing interactively with devtools::test()

Hello!

I've tried this:

You can also provide this function in inst/httptest/request.R, and any time your package is loaded (as when you run tests or build vignettes), this function will be called automatically.

With this code:

set_requester(function (request) {
  gsub_request(request, "https\\://(.+).googleapis.com/", "api/")
})

...but the mocks are still written to the long form folders, without substitution. Am I missing something?

Its for this repo https://github.com/ropensci/googleLanguageR/blob/master/inst/httptest/request.R and the mocks are here https://github.com/ropensci/googleLanguageR/tree/master/tests

httptest fails with dev httr

library(httr)
library(httptest)
#> Loading required package: testthat

with_fake_http(GET("http://example.com"))
#> GET http://example.com
#> Error in rawToChar(raw): argument 'x' must be a raw vector

Created on 2018-12-06 by the reprex package (v0.2.1)

I think this is because you're assuming a particularly formulation of the internals of httr that is no longer true. Can you please confirm/reproduce ASAP? (This is blocking the httr release)

Version of with_mock_API that will not assume 200 status

It will help to extend or add new mock API function that can support codes like 201/202 or error codes.

It is quite normal for data API to return 201 (Created) code, so developer is responsible to request for data again after a period of time.

it will also help to build tests for cases when API returns error, e.g. 403 (Forbidden)

If you have any suggestion on where and how you would like it to be addressed, I will try to add this functionality.

Support of POST requests

Hi,

I am looking to add support of POST requests to your package and at this point not sure why did you restrict it to GET in the first place.

How do you think the path to a fake file will be different?

Thanks
Bulat

Long filepaths (over 100 bytes) fail R CMD check

Hello, I am hoping that in a future release you can reduce the length of the filepaths/filenames that are generated for storing the response data. Currently, you are including the URL in the filepath, which is leading to very long filepaths. When a file path reaches 100 bytes, it causes warnings in the R CMD check of a package I am writing:

Tarballs are only required to store paths of up to 100 bytes and cannot store those of more than 256 bytes, with restrictions including to 100 bytes for the final component.
See section 'Package structure' in the 'Writing R extensions' manual.

I guess you could shorten the filepaths by using hash signatures. Or you could create random short filenames but keep a mapping in a central file. Just throwing some ideas.

Thanks for the useful package.

Unexpected '<' in POST response

Hi Neal, I have a failing test occur when the response includes an auth_token = <environment> field. The generated file is shown below - it is a POST batch from googleanalyticsR which is generated with this code ( link to file on GitHub)

The POST response returns other responses within it that may be GET, DELETE etc - its a way to send many API calls at once. Perhaps it is unexpected for your mock functions though.

library(httptest)
.mockPaths("..")

library(googleAnalyticsR)

accountId <- 54019251
webPropId <- "UA-54019251-4"
ga_id <- 106249469

context("Batch API Mocking")

test_that("Record requests if online", {
  skip_if_disconnected()
  skip_if_no_env_auth(local_auth)
  
  ## test reqs
  capture_requests(
    {
      google_analytics(c(ga_id2, ga_id),
                       start = "2015-07-30", end = "2015-10-01",
                       dimensions=c('medium'),
                       metrics = c('sessions'),
                       sort = "ga:sessions",
                       multi_account_batching = TRUE)
  })
})

with_mock_API({
  
  test_that("v3 multi account batching without flag", {

    skip_on_cran()
    multi <- google_analytics(c(ga_id, ga_id2),
                              start = "2015-07-31", end = "2015-10-01",
                              dimensions=c('medium'),
                              metrics = c('sessions'),
                              sort = "ga:sessions")

    expect_length(multi, 2)

    expect_s3_class(multi[[1]], "data.frame")
    expect_s3_class(multi[[2]], "data.frame")

  })

})

The offending response is below:

structure(list(url = "https://www.googleapis.com/batch/analytics/v3", 
    status_code = 200L, headers = structure(list(vary = "Origin", 
        vary = "X-Origin", `content-type` = "multipart/mixed; boundary=batch_83THWe0jLVQ_AAufwBbHho4", 
        `content-encoding` = "gzip", date = "Thu, 19 Oct 2017 08:43:52 GMT", 
        expires = "Thu, 19 Oct 2017 08:43:52 GMT", `cache-control` = "private, max-age=0", 
        `x-content-type-options` = "nosniff", `x-frame-options` = "SAMEORIGIN", 
        `x-xss-protection` = "1; mode=block", server = "GSE", 
        `alt-svc` = "quic=\":443\"; ma=2592000; v=\"39,38,37,35\"", 
        `transfer-encoding` = "chunked"), .Names = c("vary", 
    "vary", "content-type", "content-encoding", "date", "expires", 
    "cache-control", "x-content-type-options", "x-frame-options", 
    "x-xss-protection", "server", "alt-svc", "transfer-encoding"
    ), class = c("insensitive", "list")), all_headers = list(
        structure(list(status = 200L, version = "HTTP/1.1", headers = structure(list(
            vary = "Origin", vary = "X-Origin", `content-type` = "multipart/mixed; boundary=batch_83THWe0jLVQ_AAufwBbHho4", 
            `content-encoding` = "gzip", date = "Thu, 19 Oct 2017 08:43:52 GMT", 
            expires = "Thu, 19 Oct 2017 08:43:52 GMT", `cache-control` = "private, max-age=0", 
            `x-content-type-options` = "nosniff", `x-frame-options` = "SAMEORIGIN", 
            `x-xss-protection` = "1; mode=block", server = "GSE", 
            `alt-svc` = "quic=\":443\"; ma=2592000; v=\"39,38,37,35\"", 
            `transfer-encoding` = "chunked"), .Names = c("vary", 
        "vary", "content-type", "content-encoding", "date", "expires", 
        "cache-control", "x-content-type-options", "x-frame-options", 
        "x-xss-protection", "server", "alt-svc", "transfer-encoding"
        ), class = c("insensitive", "list"))), .Names = c("status", 
        "version", "headers"))), cookies = structure(list(domain = logical(0), 
        flag = logical(0), path = logical(0), secure = logical(0), 
        expiration = structure(numeric(0), class = c("POSIXct", 
        "POSIXt")), name = logical(0), value = logical(0)), .Names = c("domain", 
    "flag", "path", "secure", "expiration", "name", "value"), row.names = integer(0), class = "data.frame"), 
    content = as.raw(c(0x2d, 0x2d, 0x62, 0x61, 0x74, 0x63, 0x68, 
    0x5f, 0x38, 0x33, 0x54, 0x48, 0x57, 0x65, 0x30, 0x6a, 0x4c, 
....
    0x51, 0x5f, 0x41, 0x41, 0x75, 0x66, 0x77, 0x42, 0x62, 0x48, 
    0x68, 0x6f, 0x34, 0x2d, 0x2d, 0x0d, 0x0a)), date = structure(1508402632, class = c("POSIXct", 
    "POSIXt"), tzone = "GMT"), times = structure(c(0, 4e-05, 
    4.2e-05, 0.000102, 0.333098, 0.333141), .Names = c("redirect", 
    "namelookup", "connect", "pretransfer", "starttransfer", 
    "total")), request = structure(list(method = "POST", url = "https://www.googleapis.com/batch/analytics/v3", 
        headers = structure(c("application/json, text/xml, application/xml, */*", 
        "gzip", "multipart/mixed; boundary=gar_batch", "REDACTED"
        ), .Names = c("Accept", "Accept-Encoding", "Content-Type", 
        "Authorization")), fields = NULL, options = structure(list(
            http_version = 0, post = TRUE, postfieldsize = 620L, 
            postfields = as.raw(c(0x2d, 0x2d, 0x67, 0x61, 0x72, 
            0x5f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x0d, 0x0a, 0x43, 
......
            0x68, 0x2d, 0x2d)), useragent = "googleAuthR/0.5.1.9005 (gzip)"), .Names = c("http_version", 
        "post", "postfieldsize", "postfields", "useragent")), 
        auth_token = <environment>, output = structure(list(), class = c("write_memory", 
        "write_function"))), .Names = c("method", "url", "headers", 
    "fields", "options", "auth_token", "output"), class = "request")), .Names = c("url", 
"status_code", "headers", "all_headers", "cookies", "content", 
"date", "times", "request"), class = "response")

When trying to debug I tried to load the file contents via copy-pasting the created file's content and got the same error.

mocked <- COPY-PASTE
#...
#"post", "postfieldsize", "postfields", "useragent")), 
#+                                                                                                                                                                                                                                                                      #auth_token = <environment>, output = structure(list(), class = c("write_memory", 
#Error: unexpected '<' in "                                                                                                                                                                                                #"
#>                                                                                                                                                                                                                                                                                                                                       #"write_function"))), .Names = c("method", "url", "headers", 
#Error: unexpected ')' in "                                                                                                                                                                                                #"
#>       

Also just for future reference, is the expected behaviour to be able to recreate the response from the copy-pasted file contents?

NULL response getting passed around

Hello! Thank you for this excellent package. I use it all the time.

I encountered a strange error today. Doing this:

start_capturing()
httr::GET("dafasdfasdflkm;aslkdfmasdf.com")

Returned:

 Error in if (method != "GET") { : argument is of length zero 

Based on the docs, I would have expected httptest to write out a .R file mocking the failed request. I spent about half an hour trying to walk my way back through the code interactively tonight, but I couldn't track it down.

Hope you are able to reproduce it!

Feature: Bypass mock

It would be useful to be able to run the same code without the mock response instead making the actual call - either for recording captures or due to concerns about API changes, but also running with the mock for fast testing

Currently, this can be accomplished by writing the tests separately and then sourcing withing different contexts - with and without the mock, or keeping duplicate code for online and offline versions of the same tests

I'd propose a global option - along the lines of "httptest.bypass.mock" which is checked at the start of the context and prevents overridding GET, etc.

I can write this feature, but wanted to get feedback before pursuing it.

Works in RStudio (and Travis) but not at command line

Hi!

I've been writing tests for an API wrapper package and {httptest} has been a complete game changer. Thanks for your work on this.

I have run into a snag though. My tests work 100% fine in RStudio and also under Travis. The serialised results from the API calls are stored in a folder called mainnet.infura.io under tests/testthat. This is the contents of that folder:

$ ls -l mainnet.infura.io/
-rw-rw-r--   60 Jan 25 13:13 9BvO7Cvbe3p5FpinlbXv-14a098-POST.json
-rw-rw-r--  768 Jan 25 13:13 9BvO7Cvbe3p5FpinlbXv-23dd28-POST.json
-rw-rw-r--  768 Jan 25 13:13 9BvO7Cvbe3p5FpinlbXv-5c5555-POST.json

But when I build a source package and then run R CMD check the tests fail. Here is the error message:

โ”€โ”€ 1. Error: valid protocol version (@test-eth.R#11)  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
POST https://mainnet.infura.io/ {"jsonrpc":"2.0","method":"eth_protocolVersion","id":1,"params":[]} (mainnet.infura.io-14a098-POST.json)
Backtrace:
  1. testthat::expect_type(eth_protocolVersion(), "integer")
  4. ether::eth_protocolVersion()
  8. ether:::get_post_response("eth_protocolVersion")
 12. httr::POST(...)
 13. httr:::request_perform(req, hu$handle$handle)
 14. httr:::request_fetch(req$output, req$url, handle)
 16. httptest:::stop_request(req)

โ”€โ”€ 2. Error: can retrieve transactions (@test-eth.R#17)  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
POST https://mainnet.infura.io/ {"jsonrpc":"2.0","method":"eth_getTransactionByBlockHashAndIndex","id":1,"params":["0xd3ca9f0659473d93a00dc2076c2c9e8d7805243c59c8cb0d3501b262f98a15f1","0x0"]} (mainnet.infura.io-23dd28-POST.json)
Backtrace:
  1. ether::eth_getTransactionByBlockHashAndIndex(...)
  2. ether:::get_post_response(...)
  6. httr::POST(...)
  7. httr:::request_perform(req, hu$handle$handle)
  8. httr:::request_fetch(req$output, req$url, handle)
 10. httptest:::stop_request(req)

My understanding is that {httptest} is now looking for the API results in mainnet.infura.io-14a098-POST.json and mainnet.infura.io-23dd28-POST.json.

I'm not sure why the names and location of these files differ under these different scenarios. I've hit my head against this for a bit and I'd really appreciate some help.

Thanks,
Andrew.

expect_verb should allow regexes in the url argument

Happy to take lead on the PR for this one, would like to start discussing some design first though.

I have a function that writes a file via a PUT request, however the design of the API requires a file name as part of the URL. As the file name is unimportant and I don't want to overwrite past files, I use a UUID for file name, ergo the urls can be something like https://fake.com/<path>/<random_uuid>.

I'd love to test this function using something like

httptest::with_fake_http({
        httptest::expect_PUT(
            object = my_function(file=file),
            url = "https://fake\\.com/path/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
        )
    })

What would make most sense design-wise for implementing this? I was thinking a boolean argument to expect_verb, something like:

expect_PUT(object, url = "", ..., .url_regex=FALSE)

does that work for you?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.