惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
P
Proofpoint News Feed
The Hacker News
The Hacker News
Know Your Adversary
Know Your Adversary
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tenable Blog
AWS News Blog
AWS News Blog
S
Securelist
T
Threatpost
C
Cybersecurity and Infrastructure Security Agency CISA
IT之家
IT之家
腾讯CDC
WordPress大学
WordPress大学
Spread Privacy
Spread Privacy
C
Check Point Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Engineering at Meta
Engineering at Meta
Latest news
Latest news
A
About on SuperTechFans
The Register - Security
The Register - Security
L
LINUX DO - 热门话题
T
The Exploit Database - CXSecurity.com
C
Cisco Blogs
T
Tailwind CSS Blog
Simon Willison's Weblog
Simon Willison's Weblog
阮一峰的网络日志
阮一峰的网络日志
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
T
Tor Project blog
L
Lohrmann on Cybersecurity
G
GRAHAM CLULEY
B
Blog RSS Feed
Scott Helme
Scott Helme
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
NISL@THU
NISL@THU
P
Privacy International News Feed
Security Latest
Security Latest
Recorded Future
Recorded Future
L
LangChain Blog
Cyberwarzone
Cyberwarzone
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 聂微东
Google DeepMind News
Google DeepMind News
Last Week in AI
Last Week in AI
Apple Machine Learning Research
Apple Machine Learning Research
F
Fortinet All Blogs
O
OpenAI News
T
Threat Research - Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale

kmcd.dev

Exploring Protocol Buffers Interactively Introducing ProtoDocs Ghost in the Shell: The Manga Behind the Anime The Hidden Cost of google.protobuf.Value Why Networking Built Its Own Data Modeling Language Zero-Friction Demos with WASM Let's Learn About BGP ConnectRPC: Where is it now? Building APIs with Contracts The Case for Greppable Code Unknown Fields in Protobuf IRC Log: Reactionary Faking protobuf data in Go Y'all are Sleeping on Mise-en-Place IRC Log: Standup 2 HTTP/2 From Scratch: Part 4 IRC Log: rm -rf /var/opt/gitlab/postgresql/data HTTP/2 From Scratch: Part 3 Building a Live BGP Map HTTP/2 From Scratch: Part 2 IRC Log: The Cloud Scale Incident Visualizing the Internet (2026) Shell Log: Namaste HTTP/2 From Scratch: Part 1 IRC Log: Standup HTTP/1.1 From Scratch WHOIS is dead, long live RDAP Months Considered Harmful Encryption vs. Compression On Creating My Own Cover Art Traceroute Tool from Scratch in Go My Favorite Interview Question From JSON to Protobuf Breaking gRPC Morse Code Can You Hack a Phone with Your Voice? Visualizing the Internet (2025) HTTP QUERY and Go I made a daily word game Protovalidate: Can Input Validation Be This Easy? Behold! The Barcode Scanner FauxRPC and Protovalidate The Call of the Monolithic Codebase FauxRPC + Test Containers Self-Documenting Connect Services gRPC Over HTTP/3: Followup JSON to Protobuf Conversion gRPC: The Ugly Parts Working with Protobuf in 2024 Introducing FauxRPC HTTP/1.0 From Scratch Y'all are sleeping on HTTP/3 HTTP/0.9 From Scratch What version of HTTP are you using? Texans in Denmark gRPC Over HTTP/3 gRPC: The Good Parts Leaving Texas for Greener Pastures gRPC: The Bad Parts Unit Testing ConnectRPC Servers Daily Prompts Adding chart.js to Hugo Why I'm Rebranding Benchmarking gRPC (golang) Blog Update gRPC From Scratch: Part 3 - Protobuf Encoding Tracking the Wins Visualizing the Internet (2024) Dropping Unknown Fields in ConnectRPC RESTless: Web APIs After REST Introducing unknownconnect-go Making gRPC more approachable with ConnectRPC Inspecting Protobuf Messages Introducing protoc-gen-connect-openapi gRPC From Scratch: Part 2 - Server gRPC From Scratch: Part 1 - Client Why you should use gNMI over SNMP in 2026 The Rollercoaster of Productivity in Side Projects Lessons from a Decades-Long Project How I learned to code Economists with (virtual) Guns Visualizing the Internet (2023) softlayer-python: language bindings/CLI for a cloud company SwFTP: SFTP/FTP Server For Openstack Swift Video: Morning Copenhagen Commute Goodbye Evepraisal Visualizing the spectrum of the sun (Part 2) Visualizing the Internet (2022) Evepraisal: A price estimation tool for Eve Online Visualizing the spectrum of the sun
Mixing CEL and Protobuf for Fun
2024-12-17 · via kmcd.dev

Protobufs offer a structured approach to data definition, but testing gRPC services built with them can be a hurdle. By leveraging FauxRPC and CEL, you can accelerate development cycles, enhance test coverage, and ensure the reliability of your microservices.

Scaling Protobuf Testing

gRPC has emerged as a powerful framework for building efficient and scalable microservices, enabling seamless communication across diverse technologies. Its language-agnostic nature, powered by Protobuf, allows services written in Go, Java, Python, or any other supported language to interact effortlessly.

However, while gRPC excels at interoperability, generating test data that conforms to Protobuf specifications can still be a hurdle and most tools focus on testing specific languages. Imagine this: you’re building a gRPC service in Go, but your clients might be written in Python, Java, or even C++. Ensuring your tests cover a wide range of scenarios with valid Protobuf messages for all these languages can become quite cumbersome. Now consider that each language has its own tooling for generating and using stub data. While this probably works for most language, I feel like this fragmentation of the ecosystem goes against the mission of protobuf and gRPC. Instead, I think a better approach is a language agnostic approach.

This is where FauxRPC steps in, offering a streamlined approach to gRPC testing. Instead of crafting individual Protobuf responses for each test case, FauxRPC empowers you to define gRPC services that automatically generate realistic fake data for every response. This means you can effortlessly create a wide array of scenarios, simulate various data conditions, and thoroughly exercise your gRPC clients without the overhead of manually constructing complex Protobuf messages. However, this does fall short when you want to test specific test scenarios. That’s where FauxRPC stubs come in.

The Pet Store Example

For the example, we are using an example protobuf file from vanguard-go for this demonstration.

Creating a basic stub

First let’s create a stub file at get-pets.yaml to stub the response to the GetPetByID method.

---
stubs:
- id: get-pets-by-id-id-1
  target: io.swagger.petstore.v2.PetService/GetPetByID
  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

This is a simple static stub. And will be returned any time GetPetByID is called.

Now start the FauxRPC server:

$ buf build ssh://[email protected]/connectrpc/vanguard-go.git -o petstore.binpb --path internal/examples/pets/internal/proto/io/swagger/petstore/v2
$ fauxrpc run --schema=petstore.binpb --only-stubs --stubs=get-pets.yaml

Notice that I actually ran two commands: the first one uses buf build to build a buf image using protobuf schema defined in the vanguard-go repo. The other actually starts the server. Let’s break down the options used real quick:

  • --schema=petstore.binpb: tells FauxRPC about the schema. We use the buf image from the previous command for this.
  • --only-stubs: tells FauxRPC to only use pre-defined stubs and to avoid generating random data when there are no relevant stubs defined.
  • --stubs=get-pets.yaml: tells FauxRPC to load stubs from the get-pets.yaml file from above. This option can be specified multiple times and if you give it a directory it will recursively look for stub files to use.

Now, when I hit the GetPetByID method, I now get Whiskers back.

$ buf curl --http2-prior-knowledge -d '{"pet_id": "1"}' http://127.0.0.1:6660/io.swagger.petstore.v2.PetService/GetPetByID
{
  "id": "1",
  "category": {
    "id": "1",
    "name": "cat"
  },
  "name": "Whiskers",
  "photoUrls": [
    "https://cataas.com/cat"
  ],
  "tags": [
    {
      "id": "1",
      "name": "cute"
    },
    {
      "id": "2",
      "name": "kid-friendly"
    }
  ],
  "status": "available"
}

If I were to add more stub entries, the response will be random amongst the applicable set of stubs. This is fine for some situations, but this also falls flat when you want to set up more complex test scenarios.

Let’s Improve This

Enter CEL (Common Expression Language), a powerful and versatile expression language. With the latest release, FauxRPC now supports using CEL to define Protobuf messages. This unlocks a whole new level of conciseness, flexibility, and readability for your gRPC tests. Imagine creating dynamic messages, generating test data on the fly, and expressing complex scenarios with ease – all within your familiar Go environment.

So what does this mean? Well, there’s three new attributes for FauxRPC stubs: active_if priority and cel_content. Let’s improve upon the stubs that we defined above:

---
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
    }

Well, the first entry now has two new lines: active_if: req.pet_id == 1 and priority: 100. The active_if property lets us define an expression to decide if this stub should be used or not. In this case, if the request has pet_id == 1 then we will use this stub. The second option sets this stub to the highest priority so it will be considered first.

Next, we added a new stub that uses cel_contents. This new field defines a CEL expression to generate a response for us. In this case we use req.pet_id from the request. This demonstrates You can reference the request message in order to craft more believable response messages. For every other field we generate random responses using the special gen value. Using gen will use the field descriptor to decide what kinds of data should be generated.

Here’s what it looks like to call this metheod with a pet_id set to something other than 2:

$ buf curl --http2-prior-knowledge -d '{"pet_id": "2"}' http://127.0.0.1:6660/io.swagger.petstore.v2.PetService/GetPetByID
{
  "id": "2",
  "category": {
    "id": "6775158014153383345",
    "name": gen
  },
  "name": "Mohammad",
  "photoUrls": [
    "https://picsum.photos/400",
    "https://picsum.photos/400"
  ],
  "tags": [
    {
      "id": "3433292194552134769",
      "name": "Chad"
    }
  ],
  "status": "deleted"
}

As you can see, this makes stubs much more dynamic. The output can be based on the input so at the very least the id field can match. Let’s delve deeper into how CEL can be used to create more sophisticated and dynamic test scenarios.

Conditional Logic

Imagine a scenario where you want to return different responses based on specific conditions in the request. You could use CEL to define these conditions:

- id: get-pets-by-id-conditional
  target: io.swagger.petstore.v2.PetService/GetPetByID
  cel_content: |
    {
        'id': req.pet_id,
        'category': {'id': 1, 'name': 'cat'},
        'name': 'Whiskers',
        'photoUrls': ['https://cataas.com/cat'],
        'tags': [{'id': 1, 'name': 'cute'}, {'id': 2, 'name': 'kid-friendly'}],
        'status': 'available'
    if req.pet_id == 1 else {
        'id': req.pet_id,
        'category': {'id': 2, 'name': 'dog'},
        'name': 'Buddy',
        'photoUrls': ['https://dog.ceo/api/breeds/image/random'],
        'tags': [{'id': 3, 'name': 'friendly'}, {'id': 4, 'name': 'playful'}],
        'status': 'pending'
    }

In this example, if the pet_id is 1, the first response is returned. Otherwise, the second response is returned.

Dynamic Data Generation

CEL can be used to generate dynamic data based on various factors, such as integers, usernames, sentences, random numbers, or values from the request:

- id: get-pets-by-id-dynamic
  target: io.swagger.petstore.v2.PetService/GetPetByID
  cel_content: |
    {
        'id': req.pet_id,
        'category': [{'id': 1, 'name': 'cat'}, {'id': 2, 'name': 'dog'}][fake_int() % 2],
        'name': ['Mr', 'Ms'][fake_int() % 2] + ' ' + fake_first_name(),
        'photoUrls': ['https://picsum.photos/200'],
        'tags': [{'id': gen, 'name': gen}],
        'status': ['available', 'pending', 'sold'][fake_int() % 3]
    }
  • id: This represents the unique identifier of a pet. It takes its value directly from req.pet_id, which is pulled the pet ID from the incoming request.
  • category: This defines the pet’s category. fake_int() % 2 is used to randomly select one of the categories since the result of the modulo operation will be either 0 or 1.
  • name: This generates a random pet name. It combines ‘Mr’ or ‘Ms’ (chosen randomly using fake_int() % 2) with a fake first name generated by the fake_first_name() function.
  • photoUrls: This field is meant to hold an array of URLs pointing to pet photos. In this case, it’s a single-element array with a placeholder URL from https://picsum.photos/200 (which generates a random image).
  • tags: This defines an array of tags associated with the pet. gen is used to generate a random (but type appropriate) value for a single tag’s ID and name fields.
  • status: This indicates the pet’s current status. It randomly selects one of three possible values (‘available’, ‘pending’, or ‘sold’) using fake_int() % 3.

All of the functions starting with fake_ are provided by a new FauxRPC package, celfakeit. This library exposes many functions from gofakeit as CEL functions.

Recap

Since I’ve been making a lot of changes and improvements, it seems like a good idea to recap how FauxRPC works and how it’s different from other similar tools.

Conclusion

In conclusion, the combination of Protobuf and CEL offers a powerful and flexible approach to testing gRPC services. By leveraging CEL’s expressive power, developers can define dynamic and realistic test data, significantly enhancing the efficiency and effectiveness of their testing efforts.

FauxRPC, with the integration of CEL, simplifies the process of generating complex Protobuf messages, allowing developers to focus on writing robust and reliable gRPC services. By embracing this innovative approach, teams can accelerate their development cycles and deliver high-quality gRPC applications with confidence.

… and don’t forget to star the repo on GitHub. It helps more than you know!