Mock Google Cloud Platform services in your tests - the moto for GCP.
Project description
🐦 drongo
Mock Google Cloud Platform services in your tests - the moto for GCP.
drongo lets you test code that talks to Google Cloud without touching the
network, running an emulator, or paying for real resources. It stands up an
in-memory, in-process fake of GCP service APIs and transparently intercepts the
requests your google-cloud client libraries make.
If you've used moto for AWS, drongo will
feel immediately familiar - that's on purpose.
Why "drongo"?
boto(the AWS SDK) is named after the boto, the Amazon river dolphin, andmotomocksboto. The drongo is a bird famed for vocal mimicry: the fork-tailed drongo copies other animals' alarm calls to fool them into dropping their food. That is exactly what a mock does, sodrongodoes it for Google Cloud. 🐦
Features
- 🎯 One decorator -
@mock_gcppatches every supported service, just like@mock_aws. - 🔌 Flexible - use it as a decorator, a context manager, a class decorator, or a
unittest.TestCasemixin. - 🧠 In-memory & fast - no Docker, no emulators, no sockets; tests run in milliseconds.
- 🌐 Standalone server -
drongo serverspeaks real HTTP so SDKs in any language can point at it. - 🧪 pytest-native - a
drongofixture is auto-registered on install. - 🧩 Extensible - adding a service is
models.py+responses.py+urls.py, the same shape as moto. - ✅ Typed - ships
py.typed, checked with mypy.
Installation
pip install drongo
drongo does not depend on the google-cloud client libraries - you bring your
own (google-cloud-storage, google-cloud-secret-manager, …). It mocks
whatever you already use.
Quickstart
from drongo import mock_gcp
@mock_gcp
def test_upload_download():
from google.cloud import storage
client = storage.Client(project="my-project")
bucket = client.create_bucket("my-bucket")
bucket.blob("hello.txt").upload_from_string(
"hello drongo", content_type="text/plain"
)
assert bucket.blob("hello.txt").download_as_text() == "hello drongo"
assert [b.name for b in client.list_blobs("my-bucket")] == ["hello.txt"]
No credentials, no network, no emulator. storage.Client() works with or
without arguments - drongo supplies anonymous credentials and a default project
while a mock scope is active.
Every way to invoke it (all like moto)
from drongo import mock_gcp
# 1. Bare decorator
@mock_gcp
def test_a(): ...
# 2. Called decorator
@mock_gcp()
def test_b(): ...
# 3. Context manager
def test_c():
with mock_gcp():
...
# 4. Class decorator (plain class or unittest.TestCase)
@mock_gcp
class TestSuite:
def test_d(self): ...
pytest fixture
def test_with_fixture(drongo):
from google.cloud import storage
storage.Client(project="p").create_bucket("b")
# Inspect raw backend state, moto-style.
assert "b" in drongo.backend("storage").buckets
Secret Manager
Construct the client with the REST transport so drongo can intercept it:
from drongo import mock_gcp
@mock_gcp
def test_secret():
from google.cloud import secretmanager
client = secretmanager.SecretManagerServiceClient(transport="rest")
secret = client.create_secret(
request={
"parent": "projects/my-project",
"secret_id": "api-key",
"secret": {"replication": {"automatic": {}}},
}
)
client.add_secret_version(
request={"parent": secret.name, "payload": {"data": b"s3cr3t"}}
)
accessed = client.access_secret_version(
request={"name": f"{secret.name}/versions/latest"}
)
assert accessed.payload.data == b"s3cr3t"
Standalone server mode
Run drongo as a real HTTP server - the same trick as moto_server - so
non-Python SDKs, or the google libraries in emulator mode, can use it:
drongo server --port 9090
export STORAGE_EMULATOR_HOST=http://localhost:9090
from google.cloud import storage
from google.auth.credentials import AnonymousCredentials
client = storage.Client(project="p", credentials=AnonymousCredentials())
client.create_bucket("b") # served by the drongo server over HTTP
Supported services
| Service | Transport | Coverage |
|---|---|---|
| Cloud Storage | JSON API (default) | buckets, objects, multipart/simple/resumable uploads, downloads (incl. Range), list w/ prefix & delimiter, copy/rewrite, metadata |
| Secret Manager | REST (transport="rest") |
secrets, versions, access, enable/disable/destroy, list |
More services are on the roadmap. Adding one is intentionally
mechanical - see docs/contributing-a-service.md.
How it works
drongo mirrors moto's architecture, adapted from AWS/botocore to GCP's
REST+JSON APIs:
mock_gcpstarts a reentrant controller that (a) activates theresponsesHTTP interception layer and (b) patchesgoogle.auth.defaultto return anonymous credentials.- Each service ships a
models.py(in-memory state), aresponses.py(aBaseResponsesubclass with one method per API call), and aurls.py(url_bases+url_paths) - the same trio moto uses. - Backends are sharded through a
BackendDictkeyed by project (moto keys by account + region). Globally-namespaced resources like buckets share one backend, exactly as moto special-cases S3. - The standalone server replays those same route tables over a real socket.
See docs/architecture.md for the full tour.
Roadmap
- Pub/Sub
- Firestore
- BigQuery
- Cloud Tasks
- Resource Manager (projects)
- gRPC transport interception (currently REST/JSON)
Want one sooner? Open an issue or contribute it - see below.
Contributing
Contributions are very welcome! Adding a service is a great first PR. Start with
CONTRIBUTING.md and
docs/contributing-a-service.md.
git clone https://github.com/proxyroot/drongo
cd drongo
make install # editable install + dev tools
make check # ruff + mypy + pytest
License
Apache License 2.0 - the same license as moto.
Acknowledgements
drongo is heavily inspired by moto and owes
its design to that project. It is not affiliated with or endorsed by Google
or the moto maintainers.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file drongo-0.1.0.tar.gz.
File metadata
- Download URL: drongo-0.1.0.tar.gz
- Upload date:
- Size: 32.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c49d80341f7ca31f66c955f147d852c4dba246b1632fd1385a47302806b27c1
|
|
| MD5 |
504abe653f9418790feee83b3857c1d1
|
|
| BLAKE2b-256 |
cdb197bfe5f6ac3556fe88ccb010d32c0987e82711bae351fdf2fec2c6c2d250
|
Provenance
The following attestation bundles were made for drongo-0.1.0.tar.gz:
Publisher:
release.yml on proxyroot/drongo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
drongo-0.1.0.tar.gz -
Subject digest:
1c49d80341f7ca31f66c955f147d852c4dba246b1632fd1385a47302806b27c1 - Sigstore transparency entry: 2278965832
- Sigstore integration time:
-
Permalink:
proxyroot/drongo@87d2388949bc8085b561fc701096d3dacba3cf83 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/proxyroot
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@87d2388949bc8085b561fc701096d3dacba3cf83 -
Trigger Event:
release
-
Statement type:
File details
Details for the file drongo-0.1.0-py3-none-any.whl.
File metadata
- Download URL: drongo-0.1.0-py3-none-any.whl
- Upload date:
- Size: 36.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a8b19128ad74af8fa4563e75343c7750be6a77694d869beda1a5dad2031237d
|
|
| MD5 |
67d9aaec06f7c3520165f03338a58e7e
|
|
| BLAKE2b-256 |
ec37f244204bee652f462774f085e0cc6686b49a6e9882952c0a0cc3b405241c
|
Provenance
The following attestation bundles were made for drongo-0.1.0-py3-none-any.whl:
Publisher:
release.yml on proxyroot/drongo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
drongo-0.1.0-py3-none-any.whl -
Subject digest:
3a8b19128ad74af8fa4563e75343c7750be6a77694d869beda1a5dad2031237d - Sigstore transparency entry: 2278965842
- Sigstore integration time:
-
Permalink:
proxyroot/drongo@87d2388949bc8085b561fc701096d3dacba3cf83 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/proxyroot
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@87d2388949bc8085b561fc701096d3dacba3cf83 -
Trigger Event:
release
-
Statement type: