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.
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("../")
.
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