<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SixSet</title>
    <description>The latest articles on DEV Community by SixSet (@sixset).</description>
    <link>https://dev.to/sixset</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4042257%2Fa684b9c7-01f1-4083-a43f-d31fd34ff933.png</url>
      <title>DEV Community: SixSet</title>
      <link>https://dev.to/sixset</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sixset"/>
    <language>en</language>
    <item>
      <title>A Better Way to Test Kafka and RabbitMQ Workflows</title>
      <dc:creator>SixSet</dc:creator>
      <pubDate>Wed, 22 Jul 2026 15:29:03 +0000</pubDate>
      <link>https://dev.to/sixset/a-better-way-to-test-kafka-and-rabbitmq-workflows-3p1j</link>
      <guid>https://dev.to/sixset/a-better-way-to-test-kafka-and-rabbitmq-workflows-3p1j</guid>
      <description>&lt;p&gt;This isn't a Kafka tutorial. It's about how I stopped writing disposable producer scripts every time I needed to test an event-driven workflow.&lt;/p&gt;

&lt;p&gt;I keep running into the same situation. I'm testing a feature in a distributed system where the actions are loosely connected through events a user makes a payment or a purchase, and that has to start propagating: to accounting, to inventory, to whatever downstream service is waiting for it.&lt;/p&gt;

&lt;p&gt;The problem is that on test environments, I rarely have the full chain available. Either the other services aren't deployed yet, or the functionality that would normally trigger the event just doesn't exist yet. So I need to fake that one event myself.&lt;/p&gt;

&lt;p&gt;Most of the time this ended up being one of three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kafka-console-producer&lt;/code&gt; with a hand-typed payload&lt;/li&gt;
&lt;li&gt;a small producer script in Python or Go, written once and never cleaned up&lt;/li&gt;
&lt;li&gt;a throwaway Spring Boot app whose only job was to fire one event&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these work. But after a few months, your repo (or your &lt;code&gt;~/scripts&lt;/code&gt; folder) turns into a graveyard of one-off producers, each slightly different, each half-remembered when you need it again six weeks later.&lt;/p&gt;

&lt;p&gt;I got tired of that pattern enough to build Devset, a tool for testing event-driven systems without writing throwaway producers and consumers. This is a walkthrough of the three specific problems that pushed me to build it.&lt;/p&gt;

&lt;p&gt;The examples below use Kafka, but the same workflow applies to RabbitMQ exchanges and routing keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending a single event
&lt;/h2&gt;

&lt;p&gt;Sometimes I just need to fire one message a specific payload, on a specific topic, with a specific key and see what happens downstream. &lt;code&gt;kafka-console-producer&lt;/code&gt; technically solves this problem, but in practice it still means constructing JSON by hand, remembering CLI flags, and repeating the whole process every time you want to replay the same event.&lt;/p&gt;

&lt;p&gt;Instead of writing a producer every time, I paste the payload, pick the topic and key, and publish it. It gets saved, so the next time I need the same message I'm not reconstructing it from memory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmqv3ao2osb2a20yt13v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmqv3ao2osb2a20yt13v.gif" alt="Pasting a payload, picking a topic/key, and publishing it — the message shows up on the topic immediately" width="720" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Replaying complete workflows
&lt;/h2&gt;

&lt;p&gt;This is the part that actually mattered more than sending single messages.&lt;/p&gt;

&lt;p&gt;Sometimes one event isn't enough I need to simulate a whole sequence. A user adds something to a cart, enters an amount, pays, and a separate service should confirm the order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderCreated
     │
     ▼
InventoryReserved
     │
     ▼
PaymentAuthorized
     │
     ▼
InvoiceGenerated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's a simplified example, but real workflows usually branch here. Inventory may fail, payment may time out, an order may need to be retried, or a compensating event may need to be emitted. Those are the scenarios that quickly turn a handful of producer scripts into something nobody wants to maintain.&lt;/p&gt;

&lt;p&gt;Normally, testing that chain means either standing up four real services, or writing four scripts and running them in the right order with the right delays between them and hoping nothing about the payload shapes changed since the last time you touched them.&lt;/p&gt;

&lt;p&gt;In Devset, this is one saved workflow. I lay the steps out on a canvas, each step can carry a condition, a delay, or reference state from an earlier step (so &lt;code&gt;PaymentAuthorized&lt;/code&gt; can carry the order ID that &lt;code&gt;OrderCreated&lt;/code&gt; generated), and the whole thing runs as a single unit that I can re-trigger any time I need it — instead of four services or four scripts, it's one saved workflow.&lt;/p&gt;

&lt;p&gt;The same workflows also let me observe responses coming back, so I'm not only publishing events but exercising complete request/reaction chains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzkay40aw6v9dc1hfj9hh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzkay40aw6v9dc1hfj9hh.gif" alt="Building the four-step chain above on the Flow Builder canvas and running it end to end" width="720" height="441"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Working with Protobuf
&lt;/h2&gt;

&lt;p&gt;A chunk of my day-to-day work is on services that speak Protobuf, and firing Protobuf messages from a script is a different level of annoying than JSON. You need the schema on hand, you're dealing with compiled classes or manual encoding, and the moment the schema changes across environments you're debugging a wire-format mismatch instead of the thing you actually meant to test.&lt;/p&gt;

&lt;p&gt;I keep the schemas I use versioned in one place now, and messages can be built against JSON Schema or Protobuf the same way — the encoding and validation happen automatically instead of being something I have to get right by hand every time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F79s0wb4qz295eqgt46sd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F79s0wb4qz295eqgt46sd.gif" alt="Picking a Protobuf schema and sending a binary-encoded message the same way as the JSON one" width="720" height="441"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Where this is going
&lt;/h2&gt;

&lt;p&gt;Devset CE is source-available and self-hosted, and runs locally against your own brokers with a single Docker command. If you'd like to try it, the repository and documentation are linked below.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/devset-io" rel="noopener noreferrer"&gt;
        devset-io
      &lt;/a&gt; / &lt;a href="https://github.com/devset-io/devset-ce" rel="noopener noreferrer"&gt;
        devset-ce
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Local-first workflow engine for testing Kafka and RabbitMQ event-driven systems. Define scenarios, send real messages, inspect results — all on your machine.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/devset-io/devset-ce/docs/images/devset-banner-v4@2x.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevset-io%2Fdevset-ce%2FHEAD%2Fdocs%2Fimages%2Fdevset-banner-v4%402x.png" width="100%" alt="Devset"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;em&gt;Local message lab for Kafka and RabbitMQ — no cloud, no scripts.&lt;/em&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/devset-io/devset-ce/LICENSE" rel="noopener noreferrer"&gt;&lt;img alt="License" src="https://camo.githubusercontent.com/c1bc7fe82ef8aece9940a0bb0bf58241ee3d1734ca0e98e7b3fb75d9afddaaa4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d46534c2d2d312e312d2d4170616368652d2d322e302d3465623538613f7374796c653d666c61742d737175617265"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/devset-io/devset-ce/releases" rel="noopener noreferrer"&gt;&lt;img alt="Release" src="https://camo.githubusercontent.com/3e736c3b80f820ea57e1d02bb2a72f6d697b41ee22760207dc6f60371173abed/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f6465767365742d696f2f6465767365742d63653f636f6c6f723d346562353861267374796c653d666c61742d737175617265"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/devset-io/devset-ce/stargazers" rel="noopener noreferrer"&gt;&lt;img alt="Stars" src="https://camo.githubusercontent.com/16711e157d5d905d262ce94a9206451c97cf9bf4557a4f10768b6fa93fe7ac4c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6465767365742d696f2f6465767365742d63653f636f6c6f723d346562353861267374796c653d666c61742d737175617265"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/devset-io/devset-ce/actions/workflows/ci.yml?query=branch%3Amain" rel="noopener noreferrer"&gt;&lt;img alt="CI" src="https://camo.githubusercontent.com/743bad5df06a54d1621dfc1ced6270ee9af8b3fd7640411687c2a3d8dfdec3bb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6465767365742d696f2f6465767365742d63652f63692e796d6c3f6272616e63683d6d61696e266c6162656c3d6369267374796c653d666c61742d737175617265"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/devset-io/devset-ce/actions/workflows/e2e.yml?query=branch%3Amain" rel="noopener noreferrer"&gt;&lt;img alt="E2E" src="https://camo.githubusercontent.com/d7d2e2329e6eb3562463b3fc88297503873a248b1c2102bf13f9d494b16c76a6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6465767365742d696f2f6465767365742d63652f6532652e796d6c3f6272616e63683d6d61696e266c6162656c3d653265267374796c653d666c61742d737175617265"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/devset-io/devset-ce/actions/workflows/release.yml" rel="noopener noreferrer"&gt;&lt;img alt="Release" src="https://camo.githubusercontent.com/25e4e3120004c9f38b17c7b0cc52e07137e09935ab5b437ec1f1df4b8ccfdd1d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6465767365742d696f2f6465767365742d63652f72656c656173652e796d6c3f6c6162656c3d72656c65617365267374796c653d666c61742d737175617265"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0cac086a4ea45b44f3c5c6629d0b4fd9c339a962a4f3ef513603d6d748348da7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6176612d32352d3465623538613f7374796c653d666c61742d737175617265"&gt;&lt;img alt="Java" src="https://camo.githubusercontent.com/0cac086a4ea45b44f3c5c6629d0b4fd9c339a962a4f3ef513603d6d748348da7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6176612d32352d3465623538613f7374796c653d666c61742d737175617265"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f74db4f5e319ba38286e1caa19a277cbdcc3528fc5e7c244660a297c4856e186/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72656163742d31392d3465623538613f7374796c653d666c61742d737175617265"&gt;&lt;img alt="React" src="https://camo.githubusercontent.com/f74db4f5e319ba38286e1caa19a277cbdcc3528fc5e7c244660a297c4856e186/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72656163742d31392d3465623538613f7374796c653d666c61742d737175617265"&gt;&lt;/a&gt;
  &lt;a href="https://sonarcloud.io/summary/new_code?id=devset-io_devset-ce" rel="nofollow noopener noreferrer"&gt;&lt;img alt="SonarQube" src="https://camo.githubusercontent.com/3d5ae66feabbb1b9da240ad6b7f6e63ba0e75095b7d7c1d8b9d4faeb96597dd9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7363616e6e65642532306f6e2d536f6e6172517562652d3465396263643f7374796c653d666c61742d737175617265266c6f676f3d736f6e617271756265266c6f676f436f6c6f723d7768697465"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Devset&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Testing event-driven systems is painful. You end up writing throwaway scripts to publish a few messages, integration tests are brittle and slow, multi-step scenarios need glue code nobody wants to maintain, and reproducing production payloads locally is a guessing game. Devset is a self-hosted scenario engine that compiles a declarative DSL into runs against real Kafka or RabbitMQ brokers, with a visual builder on top.&lt;/p&gt;

&lt;p&gt;Think Postman or Cypress, but for Kafka and RabbitMQ instead of HTTP.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No cloud.&lt;/strong&gt; Runs entirely on your machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No accounts.&lt;/strong&gt; Open the UI and start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No telemetry.&lt;/strong&gt; Your scenarios stay local.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source-available under FSL-1.1 — free for internal use; each release converts to Apache 2.0 on its second anniversary.&lt;/p&gt;




&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/devset-io/devset-ce/docs/images/Flow%20Builder.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdevset-io%2Fdevset-ce%2FHEAD%2Fdocs%2Fimages%2FFlow%2520Builder.png" alt="Flow Builder"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick start&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1. Run Devset&lt;/h3&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;docker run -p 8082:8082 -v devset-data:/data ghcr.io/devset-io/devset-ce:latest&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Open &lt;strong&gt;&lt;a href="http://localhost:8082" rel="nofollow noopener noreferrer"&gt;http://localhost:8082&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;2. Add a broker connection&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;Go to…&lt;/p&gt;&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/devset-io/devset-ce" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;&lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://docs.devset.io" rel="noopener noreferrer"&gt;https://docs.devset.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few things I'm currently working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deeper branching in the Flow Builder, so workflows can handle retries, timeouts, and compensating events instead of just linear chains&lt;/li&gt;
&lt;li&gt;live topic inspection&lt;/li&gt;
&lt;li&gt;offline workflow simulation (run the pipeline without touching a real broker)&lt;/li&gt;
&lt;li&gt;a JSON DSL so the same workflows can run from CI, not just the UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work with Kafka or RabbitMQ and have opinions, issues, or PRs, I'd genuinely like to hear them.&lt;/p&gt;




&lt;p&gt;How are you testing event chains like this today real services, mocks, something else? Curious what other people have landed on.&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>java</category>
      <category>eventdriven</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
