Parsing the body of a stubbed request from webmockr

I’m trying to use webmockr to mock some HTTP responses for testing a package of mine that wraps an API (using httr), but I’m having difficulty creating stubbed requests whose content behaves the same way as the actual API responses. Here is a reprex:

library("webmockr")
library("httr")
library("magrittr")

## Some fake data
dat_json <- '{"odata.metadata":["https://someurlhere.com"],"odata.count":["110"],"value":[{"SomeData":{"whatever":{"species":["Mouse"]}},"Comments":{}}]}'

httr_mock(on = TRUE) # turn on mocking

## Stub request
stub <- stub_request('get', uri = 'https://httpbin.org/get') %>%
  to_return(
    body = dat_json,
    headers = list('Content-Type' = 'application/json; charset=utf-8')
  )

(res <- GET("https://httpbin.org/get"))
#> Response [https://httpbin.org/get]
#>   Date: NULL
#>   Status: 200
#>   Content-Type: application/json; charset=utf-8
#>   Size: 140 B

## Get parsed content. IRL, this is one line within the function I am trying to
## test. On real data it works fine but with the stubbed data it throws an
## error:
content(res, "parsed")
#> Error: No automatic parser available for application/octet-stream.

httr_mock(on = FALSE) # turn off mocking

When I actually query the API, calling content() on the response gives data that looks like this:

## Expected result:
(dat <- list(
  odata.metadata = "https://someurlhere.com",
  odata.count = "110",
  value = list(
    list(
      SomeData = list(whatever = list(species = "Mouse")),
      Comments = NULL
    )
  )
))
#> $odata.metadata
#> [1] "https://someurlhere.com"
#> 
#> $odata.count
#> [1] "110"
#> 
#> $value
#> $value[[1]]
#> $value[[1]]$SomeData
#> $value[[1]]$SomeData$whatever
#> $value[[1]]$SomeData$whatever$species
#> [1] "Mouse"
#> 
#> 
#> 
#> $value[[1]]$Comments
#> NULL

Created on 2019-02-01 by the reprex package (v0.2.1)

Am I doing something wrong here?

1 Like

thx for your question @karawoo !

Weirdness. Looks like this is the culprit:

# mocked request
str(res$headers)
#> List of 1
#>  $ content-type: chr "application/json; charset=utf-8"

# real request
str(real_req$headers)
#> List of 1
#>  $ content-type: chr "application/json"
#>  - attr(*, "class")= chr [1:2] "insensitive" "list" 

And then in the mocked response the content type header doesn’t get pulled out but the real response one does.

res$headers[["Content-Type"]]
#> NULL

real_req$headers[["Content-Type"]]
#> [1] "application/json"

Looks like in httr they attach a class to the list of headers that makes it insensitive to case

So I guess I need to do likewise in webmockr to make this work

1 Like

Opened an issue https://github.com/ropensci/webmockr/issues/59

concurrent fixing with https://github.com/ropensci/webmockr/issues/58

can you install remotes::install_github("ropensci/webmockr@fix-httr-date") and try again.

Now im getting:

library("webmockr")
library("httr")
dat_json <- '{"odata.metadata":["https://someurlhere.com"],"odata.count":["110"],"value":[{"SomeData":{"whatever":{"species":["Mouse"]}},"Comments":{}}]}'
httr_mock()
stub <- stub_request('get', uri = 'https://httpbin.org/get') %>%
  to_return(
    body = dat_json,
    headers = list('Content-Type' = 'application/json; charset=utf-8')
  )
res <- GET("https://httpbin.org/get")
content(res)
1 Like

That’s working, thanks @sckott!

1 Like

Cool. will submit this milestone https://github.com/ropensci/webmockr/milestone/10 soon to cran