Mocking HTTP Calls in Unit Tests

How can I unit test code that makes HTTP requests without actually making network calls?

Unit testing is an important part of building reliable software in any language, including C++. But testing code that makes HTTP requests can be tricky - we don't want our tests to actually send network traffic or rely on a particular server being available.

The solution is to use mocking or faking to replace the real HTTP calls with simulated versions that always return a pre-configured response.

There are a number of C++ mocking libraries available that support this, such as GoogleMock or FakeIt. But even without a mocking library, we can apply the basic principle.

Here's an example of how we might structure our code to allow mocking of cpr::Get calls in unit tests:

#include <cpr/cpr.h>
#include <functional>
#include <iostream>

using HttpGetFunc = std::function<cpr::Response(
  cpr::Url, cpr::Parameters)>;

int getNumberOfThings(HttpGetFunc getter) {
  cpr::Url url{"http://www.example.com/things"};
  cpr::Parameters params{{"type", "widgets"}};

  cpr::Response r = getter(url, params);

  if (r.status_code == 200) {
    return std::stoi(r.text);
  } else {
    return -1;
  }
}

cpr::Response cprGet(cpr::Url url,
  cpr::Parameters params) {
  return cpr::Get(url, params);
}

int main() {
  int num = getNumberOfThings(cprGet);
  std::cout << "There are " << num
    << " things.\n";
}

Here the actual business logic is in the getNumberOfThings function. But instead of calling cpr::Get directly, it accepts a getter parameter, which is a function matching the signature of cpr::Get.

In the "production" code path, we call getNumberOfThings(cpr::Get), passing the real cpr::Get function.

But in unit tests, we can pass a mock or fake function that returns a predefined response without making any network calls:

#include <gtest/gtest.h>

cpr::Response mockGet(cpr::Url url, cpr::Parameters params) {
  cpr::Response fakeResponse;
  fakeResponse.status_code = 200;
  fakeResponse.text = "42";
  return fakeResponse;
}

TEST(ThingsTest, GetNumberOfThings) {
  int num = getNumberOfThings(mockGet);
  ASSERT_EQ(num, 42);
}

Our mockGet function always returns a 200 OK response with the text "42", regardless of the URL or parameters passed to it. This allows us to test that getNumberOfThings parses the response correctly without needing any real network access.

Of course, this is a simplified example. In a real application, we'd probably want to check that getNumberOfThings constructs the expected URL and parameters. And we'd want to test error cases by having the mock function return different status codes.

But the principle is the same - by abstracting the HTTP call behind a function interface, we can easily substitute mocks or fakes in tests to avoid relying on network access and make our tests faster and more reliable.

Using HTTP in Modern C++

A detailed and practical tutorial for working with HTTP in modern C++ using the cpr library.

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Testing HTTP Code Locally
How can I test my HTTP code locally without making real network requests?
Handling Binary Data in HTTP
How do I send and receive binary data like images over HTTP with C++?
Handling Paginated HTTP API Responses
Many HTTP APIs return paginated responses when there is a lot of data. How do I handle this in C++?
Setting Timeouts on HTTP Requests
How can I set a timeout on my HTTP requests to avoid waiting too long for a response?
Security Considerations for HTTP Requests
What security issues do I need to be aware of when making HTTP requests in C++?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant