# How GoogleTest RecordProperty Logs Test Metadata and XML Attributes

> Learn how GoogleTest RecordProperty logs test metadata as XML attributes. Capture key-value pairs during tests for enhanced reporting and analysis. Optimize your testing workflow today.

- Repository: [Google/googletest](https://github.com/google/googletest)
- Tags: how-to-guide
- Published: 2026-08-29

---

**GoogleTest's `RecordProperty` API captures key-value metadata during test execution and serializes it as XML attributes on `<testcase>`, `<testsuite>`, or `<testsuites>` elements depending on the call context.**

The `RecordProperty` function in the GoogleTest framework (google/googletest) enables developers to attach custom metadata to test results without modifying assertion logic. This metadata enriches XML reports with contextual information such as hardware versions, configuration flags, or build identifiers. Understanding how `RecordProperty` maps internal `TestProperty` objects to XML output helps teams generate actionable test logs for CI/CD pipelines.

## Understanding the RecordProperty API

`RecordProperty` is declared in [`googletest/include/gtest/gtest.h`](https://github.com/google/googletest/blob/main/googletest/include/gtest/gtest.h) and provides a public interface for attaching string-based metadata to test results. The function accepts a **key** and **value** pair, where the value can be passed directly as a string or automatically converted via a templated overload.

According to the google/googletest source code, the primary entry point is `Test::RecordProperty`, defined around lines 303-311 in [`gtest.h`](https://github.com/google/googletest/blob/main/gtest.h). This method forwards the property to the global `UnitTest` singleton, which determines the current execution context and routes the data to the appropriate `TestResult` object.

## Context-Aware XML Attribute Mapping

The destination of recorded properties depends entirely on where the call originates. The framework distinguishes between three distinct scopes:

- **Inside a test body**: Properties attach to the individual `<testcase>` element
- **Inside a test suite fixture** (`SetUpTestSuite` or `TearDownTestSuite`): Properties attach to the parent `<testsuite>` element
- **Global code** (outside any test, including `Environment` callbacks or `main`): Properties attach to the root `<testsuites>` element

This scoping allows developers to log configuration data at the appropriate granularity—per-test hardware specifications, per-suite version markers, or global run identifiers.

## Internal Implementation Flow

The journey from API call to XML attribute follows a four-stage pipeline implemented across the GoogleTest codebase.

### Step 1: User API Invocation

Tests invoke `RecordProperty(key, value)` directly. The templated overload converts numeric values to strings automatically. This call enters the static method defined in [`googletest/include/gtest/gtest.h`](https://github.com/google/googletest/blob/main/googletest/include/gtest/gtest.h).

### Step 2: Context Routing via UnitTest

The static `Test::RecordProperty` forwards to `UnitTest::RecordProperty`, implemented in [`googletest/src/gtest-internal-inl.h`](https://github.com/google/googletest/blob/main/googletest/src/gtest-internal-inl.h) (lines 734-740). This method identifies the current execution context—whether the caller is inside a test, a suite fixture, or global code—and delegates to the corresponding `TestResult::RecordProperty` method.

### Step 3: TestProperty Storage and Validation

The `TestResult` class stores properties in an internal vector of `TestProperty` objects. As implemented in [`googletest/include/gtest/gtest.h`](https://github.com/google/googletest/blob/main/googletest/include/gtest/gtest.h) (lines 777-785), the storage logic **replaces existing entries** that share the same key, ensuring only the final value persists. This stage also performs validation, rejecting keys that conflict with reserved XML attribute names.

### Step 4: XML Serialization

When the XML listener generates the test report (located in `googletest/src/gtest.cc`), it iterates over the `test_properties_` vector within each `TestResult`. Each `TestProperty` object writes itself as an XML attribute (`key="value"`) on the element corresponding to its recorded scope.

## Practical Code Examples

### Recording Properties Inside a Test

Use `RecordProperty` within individual test bodies to attach metadata specific to that execution:

```cpp
TEST(FooTest, DoesSomething) {
  EXPECT_EQ(Compute(), 42);
  RecordProperty("hardware", "v2.1");
  RecordProperty("seed", 12345);  // Templated overload converts to string
}

```

This generates a `<testcase>` element with `hardware="v2.1"` and `seed="12345"` attributes.

### Recording Properties in Suite Fixtures

Attach metadata to the entire test suite using static fixture methods:

```cpp
class BarTest : public testing::Test {
 protected:
  static void SetUpTestSuite() {
    RecordProperty("suite_version", "1.0");
  }
  static void TearDownTestSuite() {
    RecordProperty("suite_cleanup", "ok");
  }
};

TEST_F(BarTest, First) { EXPECT_TRUE(true); }
TEST_F(BarTest, Second) { EXPECT_FALSE(false); }

```

These calls populate attributes on the parent `<testsuite>` element.

### Recording Global Properties

Log run-level metadata before executing tests:

```cpp
int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  RecordProperty("run_id", "2026-08-29_01");
  RecordProperty("commit_hash", "a1b2c3d");
  return RUN_ALL_TESTS();
}

```

When executed with `--gtest_output=xml:report.xml`, the output appears as:

```xml
<testsuites run_id="2026-08-29_01" commit_hash="a1b2c3d">
  <testsuite name="BarTest" suite_version="1.0" suite_cleanup="ok">
    <testcase name="DoesSomething" hardware="v2.1" seed="12345"/>
  </testsuite>
</testsuites>

```

## Key Source Files and Architecture

| File | Purpose |
|------|---------|
| [`googletest/include/gtest/gtest.h`](https://github.com/google/googletest/blob/main/googletest/include/gtest/gtest.h) | Declares `Test::RecordProperty` and implements `TestResult::RecordProperty` with validation logic |
| [`googletest/src/gtest-internal-inl.h`](https://github.com/google/googletest/blob/main/googletest/src/gtest-internal-inl.h) | Forwards calls to `UnitTest` and routes properties to the correct `TestResult` based on context |
| `googletest/src/gtest.cc` | Contains the XML listener that serializes `TestProperty` objects as attributes |

## Summary

- **`RecordProperty`** is the public API for attaching key-value metadata to test runs in google/googletest.
- **Context determines destination**: test bodies write to `<testcase>`, suite fixtures write to `<testsuite>`, and global code writes to `<testsuites>`.
- **Last-write-wins**: Duplicate keys within the same scope overwrite previous values, ensuring only the final property persists.
- **Validation occurs at storage**: Reserved XML attribute names are rejected during the `TestResult::RecordProperty` call.
- The implementation spans [`gtest.h`](https://github.com/google/googletest/blob/main/gtest.h) for the API, [`gtest-internal-inl.h`](https://github.com/google/googletest/blob/main/gtest-internal-inl.h) for routing, and `gtest.cc` for XML generation.

## Frequently Asked Questions

### What XML element receives properties recorded inside a test body?

Properties recorded inside a test body—either in the test function itself or in `SetUp`/`TearDown` methods—become attributes of the `<testcase>` element representing that specific test. This allows individual tests to carry metadata like random seeds or hardware configurations.

### Can RecordProperty be called outside of test functions?

Yes. When called from global code, `main`, or `Environment` callbacks before or after `RUN_ALL_TESTS()`, the properties attach to the root `<testsuites>` element. This is useful for logging build identifiers or CI run IDs that apply to the entire test execution.

### How does GoogleTest handle duplicate property keys?

GoogleTest implements a last-write-wins policy. If `RecordProperty` is called multiple times with the same key within the same scope, the `TestResult::RecordProperty` method replaces the existing `TestProperty` entry with the new value. Only the final value appears in the XML output.

### Where is the RecordProperty validation logic implemented?

Input validation, including checks for reserved XML attribute names that could conflict with the schema, occurs in `TestResult::RecordProperty` within [`googletest/include/gtest/gtest.h`](https://github.com/google/googletest/blob/main/googletest/include/gtest/gtest.h) (around lines 777-785). Invalid keys are rejected at this stage before reaching the XML serializer.