Where are my mocks recorded?

By default, with_mock_api() will look for and capture_requests() will write mocks to your package’s tests/testthat directory, or else the current working directory if that path does not exist. If you want to look in or write to other places, call .mockPaths() to add directories to the search path.

If you’re running capture_requests() within a test suite in an installed package, or if you’re running interactively from a different directory, the working directory may not be the same as your code repository. If you aren’t sure where the files are going, set options(httptest2.verbose = TRUE), and it will message the absolute path of the files as it writes them.

To change where files are being written or read from, use .mockPaths() (like base::.libPaths()) to specify a different directory.

How do I fix “non-portable file paths”?

If you see this error in R CMD build or R CMD check, it means that there are file paths are longer than 100 characters, which can sometimes happen when you record requests. httptest preserves the URL structure of mocks in file paths to improve the readability and maintainability of your tests, as well as to make visible the properties of your API. Indeed, the file-system tree view of the mock files gives a visual representation of your API. This value comes with a tradeoff: sometimes URLs can be long, and R doesn’t like that.

Depending on how long your URLs are, there are a few ways to save on characters without compromising readability of your code and tests.

A good way to cut long file paths is by using a redactor: a function that alters the content of your requests and responses before mapping them to mock files. For example, if all of your API endpoints sit beneath https://language.googleapis.com/v1/, you could:

set_redactor(function (x) {
  gsub_response(x, "https\\://language.googleapis.com/v1/", "api/")
})

This will replace that string in all parts of the mock file that is saved, including in the file path that is written–that is, paths will start with “api/” rather than “language.googleapis.com/v1/”, saving you (in this case) 23 characters. The function will also be called when loading mocks in with_mock_api() so that the shortened file paths are found.

You can also provide this function in inst/httptest2/redact.R, and any time your package is loaded (as when you run tests or build vignettes, or when anyone else uses your package in their package), this function will be called automatically. See vignette("redacting") for more.

You may also be able to economize on other parts of the file paths. If you’ve recorded requests and your file paths contain long ids like “1495480537a3c1bf58486b7e544ce83d”, depending on how you access the API in your code, you may be able to simply replace that id with something shorter, like “1”. The mocks are just files, disconnected from a real server and API, so you can rename them and munge them as needed.

Finally, if you have your tests inside a tests/testthat/ directory, and your fixture files inside that, you can save 9 characters by moving the fixtures up to tests/ and setting .mockPaths("../").

Why are mocks stored as text files?

Plain-text files have several nice features, particularly relative to storing serialized (binary) R objects. You can more easily confirm that your mocks look correct, and you can more easily maintain them without having to re-record them. When you do edit them, text files are more easily handled by version-control systems like Git. Plain-text files can also have comments, so you can make notes as to why a certain fixture exists, what a particular value means, and so on, which will help the users of your package—and your future self!

By having mocks in human-readable text files, you can also more easily extend your code. APIs are living things that evolve over time, and your code that communicates with an API needs to be able to change with them. If the API adds an additional attribute to an object, no big deal: just touch up the mocks. In addition, you can future-proof your code against that kind of API change by tweaking a fixture file. In this example from the crunch package, an extra, nonsense attribute was added to the JSON just to ensure that the code doesn’t break if there are new, unknown features added to the API response. That way, if the API grows new features, people who are using your package don’t get errors if they haven’t upgraded to the latest release that recognizes the new feature.

If you’re responsible for the API as well as the R client code that communicates with it, the plain-text mocks can be a valuable source of documentation. Indeed, the file-system tree view of the mock files gives a visual representation of your API. For example, in the crunch package, the mocks show an API of catalogs that contain entities that may contain other subdocuments:

app.crunch.io/
├── api
│   ├── accounts
│   │   ├── account1
│   │   │   └── users.json
│   │   └── account1.json
│   ├── datasets
│   │   ├── 1
│   │   │   ├── export.json
│   │   │   ├── filters
│   │   │   │   └── filter1.json
│   │   │   ├── filters.json
│   │   │   ├── permissions.json
│   │   │   ├── summary-73a614.json
│   │   │   ├── variables
│   │   │   │   ├── birthyr
│   │   │   │   │   ├── summary-73a614.json
│   │   │   │   │   └── values-3d4982.json
│   │   │   │   ├── birthyr.json
│   │   │   │   ├── gender
│   │   │   │   │   ├── summary.json
│   │   │   │   │   └── values-51980f.json
│   │   │   │   ├── gender.json
│   │   │   │   ├── starttime
│   │   │   │   │   └── values-3d4982.json
│   │   │   │   ├── starttime.json
│   │   │   │   ├── textVar
│   │   │   │   │   └── values-641ef3.json
│   │   │   │   ├── textVar.json
│   │   │   │   └── weights.json
│   │   │   ├── variables-d118fa.json
│   │   │   └── variables.json
│   │   ├── 1.json
│   │   └── search-c89aba.json
│   └── users.json
└── api.json