Google Cloud Spanner C++ Client  1.32.1
A C++ Client Library for Google Cloud Spanner
Mocking the Cloud Spanner C++ Client with Google Mock

In this document we describe how to write unit tests that mock google::cloud::spanner::Client using Google Mock. This document assumes the reader is familiar with the Google Test and Google Mock frameworks and with the Cloud Spanner C++ Client.

Mocking a successful <tt>ExecuteQuery</tt>

First include the headers for the Cloud Spanner Client, the mocking class, and the Google Mock framework.

#include "absl/memory/memory.h"
#include <google/protobuf/text_format.h>
#include <gmock/gmock.h>

The example uses a number of aliases to save typing and improve readability:

using ::testing::Return;
namespace spanner = ::google::cloud::spanner;
Contains all the Cloud Spanner C++ client types and functions.
Definition: backup.cc:21

Create a mocking object for google::cloud::spanner::Connection:

auto conn = std::make_shared<google::cloud::spanner_mocks::MockConnection>();

We will setup this mock in a second, but first let's look at how it is used to create a google::cloud::spanner::Client object:

spanner::Client client(conn);
Performs database client operations on Spanner.
Definition: client.h:122

Once the client is created you can make calls on the client as usual:

auto rows = client.ExecuteQuery(
spanner::SqlStatement("SELECT Id, Greeting FROM Greetings"));
Represents a potentially parameterized SQL statement.
Definition: sql_statement.h:51

And then verify the results meet your expectations:

int count = 0;
using RowType = std::tuple<std::int64_t, std::string>;
for (auto const& row : spanner::StreamOf<RowType>(rows)) {
ASSERT_TRUE(row);
auto expected_id = ++count;
EXPECT_EQ(expected_id, std::get<0>(*row));
EXPECT_EQ("Hello World", std::get<1>(*row));
}

All of this depends on creating a google::cloud::spanner::RowStream that simulates the stream of results you want. To do so, you need to mock a source that streams google::cloud::spanner::Rows:

auto source =
absl::make_unique<google::cloud::spanner_mocks::MockResultSetSource>();

The source must define the names and types of the columns returned by the query:

auto constexpr kText = R"pb(
row_type: {
fields: {
name: "Id",
type: { code: INT64 }
}
fields: {
name: "Greeting",
type: { code: STRING }
}
})pb";
google::spanner::v1::ResultSetMetadata metadata;
ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(kText, &metadata));
EXPECT_CALL(*source, Metadata()).WillRepeatedly(Return(metadata));

And then setup the mock to return the results. Note that the results are returned one value at a time, even if a row contains multiple values.

EXPECT_CALL(*source, NextRow())
.WillOnce(Return(
{"Greeting", spanner::Value("Hello World")}})))
.WillOnce(Return(
{"Greeting", spanner::Value("Hello World")}})))
The Value class represents a type-safe, nullable Spanner value.
Definition: value.h:168
Row MakeTestRow(std::vector< std::pair< std::string, Value >> pairs)
Creates a Row with the specified column names and values.
Definition: row.cc:36

Note that the last value in the stream is indicated by an absl::optional without a value:

.WillOnce(Return(spanner::Row()));
A Row is a sequence of columns each with a name and an associated Value.
Definition: row.h:85

Once the source has been created and its behavior mocked, you mock the behavior for ExecuteQuery:

EXPECT_CALL(*conn, ExecuteQuery)
.WillOnce([&source](spanner::Connection::SqlParams const&)
return spanner::RowStream(std::move(source));
});
Represents the stream of Rows returned from spanner::Client::Read() or spanner::Client::ExecuteQuery(...
Definition: results.h:73

Full Listing

Finally we present the full code for this example:

#include "absl/memory/memory.h"
#include <google/protobuf/text_format.h>
#include <gmock/gmock.h>
namespace {
using ::testing::Return;
namespace spanner = ::google::cloud::spanner;
TEST(MockSpannerClient, SuccessfulExecuteQuery) {
// Create a mock object to stream the results of a ExecuteQuery.
auto source =
absl::make_unique<google::cloud::spanner_mocks::MockResultSetSource>();
// Setup the return type of the ExecuteQuery results:
auto constexpr kText = R"pb(
row_type: {
fields: {
name: "Id",
type: { code: INT64 }
}
fields: {
name: "Greeting",
type: { code: STRING }
}
})pb";
google::spanner::v1::ResultSetMetadata metadata;
ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(kText, &metadata));
EXPECT_CALL(*source, Metadata()).WillRepeatedly(Return(metadata));
// Setup the mock source to return some values:
EXPECT_CALL(*source, NextRow())
.WillOnce(Return(
{"Greeting", spanner::Value("Hello World")}})))
.WillOnce(Return(
{"Greeting", spanner::Value("Hello World")}})))
.WillOnce(Return(spanner::Row()));
// Create a mock for `spanner::Connection`:
auto conn = std::make_shared<google::cloud::spanner_mocks::MockConnection>();
// Setup the connection mock to return the results previously setup:
EXPECT_CALL(*conn, ExecuteQuery)
.WillOnce([&source](spanner::Connection::SqlParams const&)
return spanner::RowStream(std::move(source));
});
// Create a client with the mocked connection:
spanner::Client client(conn);
// Make the request and verify the expected results:
auto rows = client.ExecuteQuery(
spanner::SqlStatement("SELECT Id, Greeting FROM Greetings"));
int count = 0;
using RowType = std::tuple<std::int64_t, std::string>;
for (auto const& row : spanner::StreamOf<RowType>(rows)) {
ASSERT_TRUE(row);
auto expected_id = ++count;
EXPECT_EQ(expected_id, std::get<0>(*row));
EXPECT_EQ("Hello World", std::get<1>(*row));
}
}
} // namespace