Home > Net >  R - mocking API requests with `gh` package
R - mocking API requests with `gh` package

Time:07-09

I am trying to mock the output of a gh API request:

httptest2::with_mock_dir("gh", {
  test_that("api works", {
    gh::gh("GET /repos/r-lib/gh")
  })
})

I am trying to set up testing for custom functions that routinely make API calls to GitHub and I am using gh to make these requests. I am following this tutorial as guidance: https://books.ropensci.org/http-testing/

However, no directory is created when this function is run. Is there anyway to capture the output of gh::gh and store it as a mock API return so that I can run my tests without needing GitHub authentication or even an internet connection?

CodePudding user response:

httptest2 is specifically designed to test httr2 requests:

This package helps with writing tests for packages that use httr2

Unfortunately, gh uses httr:

Imports:
    cli (>= 3.0.1),
    gitcreds,
    httr (>= 1.2),
    ini,
    jsonlite

This means that you can't directly use httptest2 with gh.

However, using gh source code, you can extract the parameters of the GET request sent to httr by gh:

gh_get <- function(endpoint, ..., per_page = NULL, .token = NULL, .destfile = NULL,
               .overwrite = FALSE, .api_url = NULL, .method = "GET",
               .limit = NULL, .accept = "application/vnd.github.v3 json",
               .send_headers = NULL, .progress = TRUE, .params = list()) {
  params <- c(list(...), .params)
  params <- gh:::drop_named_nulls(params)
  
  if (is.null(per_page)) {
    if (!is.null(.limit)) {
      per_page <- max(min(.limit, 100), 1)
    }
  }
  
  if (!is.null(per_page)) {
    params <- c(params, list(per_page = per_page))
  }
  
  req <- gh:::gh_build_request(
    endpoint = endpoint, params = params,
    token = .token, destfile = .destfile,
    overwrite = .overwrite, accept = .accept,
    send_headers = .send_headers,
    api_url = .api_url, method = .method
  )
  req
}

req <- gh_get("GET /repos/r-lib/gh")
req

#$method
#[1] "GET"

#$url
#[1] "https://api.github.com/repos/r-lib/gh"

#$headers
#                      User-Agent                           Accept 
#   "https://github.com/r-lib/gh" "application/vnd.github.v3 json" 

#$query
#NULL

#$body
#NULL

#$dest
#<request>
#Output: write_memory

This allows with the example you provided to use httr2 to send the same request :

library(httr2)
resp_httr2 <- request(base_url=req$url) %>%
              req_perform() %>%
              resp_body_json()


If you are mainly interested in json content, the results are the same, only the attributes differ :

resp_gh <- gh::gh("GET /repos/r-lib/gh")

all.equal(resp_gh,resp_httr2,check.attributes=FALSE)
#[1] TRUE

If you want to use httptest2, switching to httr2 would work:

httptest2::with_mock_dir("gh", {
    test_that("api works", {
    resp <- request(base_url=req$url) %>%
      req_perform() %>%
      resp_body_json()
      expect_equal(resp$full_name,"r-lib/gh")})
  })
#Test passed            
  • Related