Skip to main content

pytest-gcpsecretmanager

A pytest plugin that provides an in-memory fake for Google Cloud Secret Manager. No Docker, no emulator, no GCP credentials required.

Both SecretManagerServiceClient (sync) and SecretManagerServiceAsyncClient (async) are transparently patched for the duration of each test.

Installation

pip install pytest-gcpsecretmanager

The plugin requires Python 3.12+ and pytest 7.0+. It does not require google-cloud-secret-manager to be installed — if the SDK is absent, fake modules are injected so that from google.cloud.secretmanager import SecretManagerServiceClient still works in your code under test.

Quick start

import pytest
from google.cloud.secretmanager import SecretManagerServiceClient

@pytest.mark.secret("my-api-key", "s3cret")
def test_reads_secret(secret_manager):
    client = SecretManagerServiceClient()
    response = client.access_secret_version(
        name="projects/test-project/secrets/my-api-key/versions/latest"
    )
    assert response.payload.data == b"s3cret"

The secret_manager fixture activates patching and returns the underlying SecretStore. The @pytest.mark.secret marker pre-populates secrets before the test runs.

Fixtures

secret_manager

Function-scoped. Activates patching and returns a SecretStore instance. Any SecretManagerServiceClient or SecretManagerServiceAsyncClient instantiated during the test will use this in-memory store.

def test_programmatic_setup(secret_manager):
    secret_manager.set_secret("db-password", "hunter2")
    secret_manager.set_secret("binary-key", b"\x00\x01\x02")

    client = SecretManagerServiceClient()
    resp = client.access_secret_version(
        name="projects/test-project/secrets/db-password/versions/latest"
    )
    assert resp.payload.data == b"hunter2"

The store also supports multiple versions:

def test_versioned_secrets(secret_manager):
    secret_manager.set_secret_sequence("rotating-key", ["v1", "v2", "v3"])

    client = SecretManagerServiceClient()
    resp = client.access_secret_version(
        name="projects/test-project/secrets/rotating-key/versions/2"
    )
    assert resp.payload.data == b"v2"

secret_manager_client

Returns a FakeSecretManagerServiceClient instance backed by the current test's store.

def test_with_client(secret_manager_client):
    secret_manager_client.create_secret(
        parent="projects/test-project", secret_id="new-secret"
    )
    secret_manager_client.add_secret_version(
        parent="projects/test-project/secrets/new-secret",
        payload={"data": b"payload"},
    )
    resp = secret_manager_client.access_secret_version(
        name="projects/test-project/secrets/new-secret/versions/1"
    )
    assert resp.payload.data == b"payload"

secret_manager_async_client

Returns a FakeSecretManagerServiceAsyncClient — same API, but all methods are async.

async def test_async_client(secret_manager_async_client):
    await secret_manager_async_client.create_secret(
        parent="projects/test-project", secret_id="async-secret"
    )
    await secret_manager_async_client.add_secret_version(
        parent="projects/test-project/secrets/async-secret",
        payload={"data": b"hello"},
    )
    resp = await secret_manager_async_client.access_secret_version(
        name="projects/test-project/secrets/async-secret/versions/1"
    )
    assert resp.payload.data == b"hello"

secret_failure_injector

Returns the active _FailureInjector for programmatic failure injection (see Failure injection below).

Markers

@pytest.mark.secret(secret_id, value, *, project=None)

Pre-populate a secret before the test runs. The default project is "test-project".

# Single version
@pytest.mark.secret("api-key", "my-key")
def test_single(secret_manager): ...

# Multiple versions (pass a list)
@pytest.mark.secret("rotating", ["v1", "v2", "v3"])
def test_versions(secret_manager): ...

# Custom project
@pytest.mark.secret("api-key", "my-key", project="my-project")
def test_custom_project(secret_manager): ...

# Stack multiple markers
@pytest.mark.secret("key-a", "value-a")
@pytest.mark.secret("key-b", "value-b")
def test_multiple(secret_manager): ...

@pytest.mark.secret_failure(method, exception, *, transient=False, count=1)

Inject a failure into a specific client method.

from pytest_gcpsecretmanager import NotFound

# Permanent failure — every call raises
@pytest.mark.secret_failure("access_secret_version", NotFound("gone"))
def test_not_found(secret_manager):
    client = SecretManagerServiceClient()
    with pytest.raises(NotFound):
        client.access_secret_version(name="projects/p/secrets/s/versions/1")

# Transient failure — fails `count` times, then succeeds
@pytest.mark.secret_failure(
    "access_secret_version", NotFound("retry me"), transient=True, count=2
)
@pytest.mark.secret("key", "val")
def test_transient(secret_manager):
    client = SecretManagerServiceClient()
    for _ in range(2):
        with pytest.raises(NotFound):
            client.access_secret_version(
                name="projects/test-project/secrets/key/versions/latest"
            )
    # Third call succeeds
    resp = client.access_secret_version(
        name="projects/test-project/secrets/key/versions/latest"
    )
    assert resp.payload.data == b"val"

Failure injection

In addition to the marker, you can inject failures programmatically via the secret_failure_injector fixture:

from pytest_gcpsecretmanager import PermissionDenied

def test_programmatic_failure(secret_manager, secret_failure_injector):
    secret_failure_injector.add_permanent_failure(
        "create_secret", PermissionDenied("nope")
    )
    client = SecretManagerServiceClient()
    with pytest.raises(PermissionDenied):
        client.create_secret(parent="projects/test-project", secret_id="x")

Available exception types: NotFound, AlreadyExists, PermissionDenied, FailedPrecondition, InvalidArgument, ResourceExhausted, DeadlineExceeded.

Supported API methods

Both sync and async clients support:

Method Description
create_secret Create a new secret
get_secret Get secret metadata
delete_secret Delete a secret and all its versions
list_secrets List secrets in a project
add_secret_version Add a new version to a secret
get_secret_version Get version metadata
access_secret_version Access the secret payload
destroy_secret_version Permanently destroy a version
disable_secret_version Disable a version
enable_secret_version Re-enable a disabled version
list_secret_versions List all versions of a secret
secret_path Static helper: build a secret resource name
secret_version_path Static helper: build a version resource name

How patching works

When the secret_manager fixture is active:

  1. If google-cloud-secret-manager is installed, the real client classes are patched via unittest.mock.patch across all known import paths (google.cloud.secretmanager, google.cloud.secretmanager_v1, etc.).
  2. If the SDK is not installed, fake modules are injected into sys.modules so that from google.cloud.secretmanager import SecretManagerServiceClient resolves to the fake client.

This means your application code doesn't need any special imports or conditional logic — the fake is transparent.

Response types

Responses use lightweight dataclasses that mirror the GCP protobuf shapes:

  • AccessSecretVersionResponse — has .name and .payload (a SecretPayload with .data bytes and .data_crc32c)
  • Secret — has .name, .replication, .create_time, .labels
  • SecretVersion — has .name, .create_time, .state

License

MIT

Metadata

Release files for pytest-gcpsecretmanager 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-gcpsecretmanager 0.2.0
File Size Uploaded Provenance
pytest_gcpsecretmanager-0.2.0.tar.gz 12.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-gcpsecretmanager 0.2.0
File Interpreter ABI Platform
pytest_gcpsecretmanager-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 28.7 kB

Release files / pytest_gcpsecretmanager-0.2.0.tar.gz

Download URL pytest_gcpsecretmanager-0.2.0.tar.gz
Size 12.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e4564db83c4f95f8a9781405999fb74766f27e1aa22ae5d0bfcf64999d25ae79
BLAKE2b-256 checksum
How to use checksums
afa9e21e1249b6dce304772e60768124fbd2dd5f4f05ce1da88732ec62f091ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 18, 2026.

Transparency log

Release files / pytest_gcpsecretmanager-0.2.0-py3-none-any.whl

Download URL pytest_gcpsecretmanager-0.2.0-py3-none-any.whl
Size 16.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
662cbb5147bc770a067f4cf01375e9ec83b371d406a324fdeaa4a2254117ce44
BLAKE2b-256 checksum
How to use checksums
b3ce01b20138329ac81cd4312578d5fc887369c44a9560d803f19cfea3e4d260
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 18, 2026.

Transparency log

Security for pytest-gcpsecretmanager 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pytest_gcpsecretmanager-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 18, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page