Stubs
FauxRPC stubs allow you to define precise responses for your gRPC APIs, enabling comprehensive testing and development without relying on a real backend. Define static responses, introduce dynamic behavior with CEL expressions, and manage stub priority for granular control over your mock services.
Stub Definition
Stubs are defined using the Stub
message in the stubs.v1
package:
message Stub {
StubRef ref = 1 [(buf.validate.field).required = true];
oneof content {
bytes proto = 2;
string json = 3;
Error error = 4;
}
// CEL rule to decide if this stub should be used for a given request.
string active_if = 5;
// Similar to the json attribute but is a CEL expression that returns the result.
string cel_content = 6;
int32 priority = 7 [(buf.validate.field).int32 = {
gte: 0
lte: 100
}];
}
- ref: A
StubRef
message that identifies the target method or type for this stub. - content: The response content, which can be:
- proto: Raw protobuf bytes.
- json: A JSON representation of the protobuf message.
- error: An
Error
message to simulate an error response.
- active_if: A CEL expression that determines if this stub should be used based on the request.
- cel_content: A CEL expression that dynamically generates the response content.
- priority: An integer from 0 to 100 (higher is more preferred) to manage stub selection when multiple stubs match a request.
Adding Stubs
While that protobuf message is how stubs are defined, you can add stubs using the fauxrpc stub add
CLI command:
fauxrpc stub add <target> [flags]
where <target>
is the fully qualified protobuf method or type.
Flags:
--id
: A unique identifier for the stub.--json
: JSON response content.--error-message
: Error message for error responses.--error-code
: gRPC error code for error responses.--active-if
: CEL expression for conditional activation.--priority
: Stub priority.--cel
: CEL expression for dynamic content generation.
Examples:
- Return a static JSON response:
fauxrpc stub add connectrpc.eliza.v1.ElizaService/Say --json '{"sentence": "Test from the CLI"}'
- Return an error:
fauxrpc stub add buf.registry.owner.v1.UserService/CreateUsers --error-message="hello!" --error-code=5
Defining Stubs in YAML
You can also define stubs in YAML (or JSON) files. This allows for more complex scenarios and better organization.
Example (dynamic content with CEL):
---
stubs:
- id: add-pet
target: io.swagger.petstore.v2.PetService/AddPet
cel_content: req
This stub will return the request itself as the response.
Example (static response):
---
stubs:
- id: find-pets-by-status
target: io.swagger.petstore.v2.PetService/FindPetsByStatus
content:
pets:
- id: 1
category:
id: 1
name: cat
name: Whiskers
photo_urls:
- https://cataas.com/cat
tags:
- id: 1
name: cute
- id: 2
name: kid-friendly
status: available
This stub will always return a predefined list of pets.
Example (combining active_if
, priority
, and cel_content
):
---
stubs:
- id: get-pets-by-id-id-1
target: io.swagger.petstore.v2.PetService/GetPetByID
active_if: req.pet_id == 1
priority: 100
content:
id: 1
category:
id: 1
name: cat
name: Whiskers
photo_urls:
- https://cataas.com/cat
tags:
- id: 1
name: cute
- id: 2
name: kid-friendly
status: available
- id: get-pets-by-id-default
target: io.swagger.petstore.v2.PetService/GetPetByID
cel_content: |
{
'id': req.pet_id,
'category': {'id': gen, 'name': 'gen'},
'name': gen,
'photo_urls': [gen, gen],
'tags': [{'id': gen, 'name': gen}],
'status': gen
}
In this example:
- The first stub provides a specific response for when
pet_id
is 1. - The second stub uses
cel_content
and thegen
value to generate a dynamic response for any otherpet_id
. - This demonstrates how to handle specific cases with high-priority stubs while providing a general fallback with dynamic content.
Using CEL for Dynamic Responses
CEL (Common Expression Language) allows you to define dynamic stub behavior based on the request. You can use CEL in two ways:
- active_if: Determine if a stub should be used based on a condition.
- cel_content: Generate the response content dynamically.
Example (using active_if
):
stubs:
- id: add-pet-active-if-example
target: io.swagger.petstore.v2.PetService/AddPet
json: '{"id": 1}'
active_if: 'req.name == "Fluffy"'
This stub will only be active if the name
field in the request is “Fluffy”.
Example (using cel_content
):
stubs:
- id: add-pet-cel-content-example
target: io.swagger.petstore.v2.PetService/AddPet
cel_content: '{"id": req.id, "name": req.name + " the Great"}'
This stub will generate a response where the id
is taken from the request and the name
is the request’s name
with " the Great" appended.
Stub Priority
When multiple stubs match a request, FauxRPC uses the priority
field to determine which stub to use. Higher priority stubs (100 being the highest) are preferred over lower priority stubs.
This allows you to define default responses with lower priority and more specific responses with higher priority.
Conclusion
FauxRPC stubs provide a powerful mechanism for controlling API responses, enabling you to simulate various scenarios, test edge cases, and develop against your APIs without a real backend. By combining static responses, dynamic CEL expressions, and priority management, you can achieve fine-grained control over your mock services and streamline your development workflow.
The examples mentioned here are available in the sudorandom/fauxrpc repo.