---
title: "Writing tests sucks. Use LLMs so it sucks less."
excerpt: "tb test validates your Tinybird pipelines before deployment. Catch errors locally instead of discovering them in production."
authors: "Javi Santana"
categories: "Product updates"
createdOn: "2025-03-19 00:00:00"
publishedOn: "2025-03-20 00:00:00"
updatedOn: "2025-03-20 00:00:00"
status: "published"
---

<p>There are a few different places to find problems in software products:&nbsp;</p><ul><li><strong>While developing</strong>: Using unit, integration, and end-to-end tests</li><li><strong>In CI</strong>: Using the same tests above, but automatically</li><li><strong>In code reviews</strong></li><li><strong>While operating:</strong> Monitoring, alerting, observability, etc.</li></ul><p>Everybody does this in software development. Nobody would deploy to production without testing. It’s a fundamental software development practice.</p><p>Sadly, best practices haven't been widely adopted in the data and analytics world. I'll try to expand on why below, but let me summarize:&nbsp;</p><ol><li>It’s boring, and&nbsp;</li><li>You usually need production data to test.</li></ol><p>(In case you're interested, I asked Grok why, and it made some <a href="https://x.com/i/grok/share/yAJ96vES4DrJ6gHlLi969QdON"><u>interesting points</u></a>. Anyway…)</p><h2 id="the-testing-gap-in-data-engineering">The testing gap in data engineering</h2><p>Data presents some unique challenges when it comes to testing:</p><ul><li><strong>Data variability</strong>: Real-world data is messy, inconsistent, and constantly changing. This is especially true when you work with billions of records. If you have that much data, there's a high probability of unexpected data.</li><li><strong>Complex</strong> <strong>transformations</strong>: It's hard to thoroughly test SQL queries and complex data pipelines. And SQL never had a proper testing framework.</li><li><strong>Environment dependencies</strong>: Testing often requires production-like data volumes and structures. SQL is probably the language with the simplest runtime setup, but people still don't test it.</li><li><strong>Lack of tooling</strong>: Again, the data ecosystem hasn't had the same robust testing frameworks as software development.</li></ul><p>Because of these things, many data teams rely on manual validation, spot checks, or simply hope that their transformations work as intended. This leads to brittle pipelines, unexpected results, and a lack of confidence when making changes.</p><p>So, we tried to solve this by:</p><ol><li>Making it easy to generate test data, and</li><li>Providing a basic but powerful test framework integrated with the product&nbsp;</li><li>Allowing LLMs to handle all the mundane bits of generating tests and test data</li></ol>
<!--kg-card-begin: html-->
<div class="tip-box"><div class="tip-box-container"><div class="tip-box-title">Tinybird Forward is here.</div><div class="tip-box-content">Last week, we launched <a href="https://www.tinybird.co/blog-posts/announcing-tinybird-forward">Forward</a>, a new Tinybird UX that makes shipping software with big data requirements faster and more intuitive.<br><br>Start using it by installing the new CLI: <code>curl https://tinybird.co | sh</code></div></div></div>
<!--kg-card-end: html-->
<h2 id="generating-realistic-test-data-with-tb-mock">Generating realistic test data with <code>tb mock</code></h2><p>The <code>tb mock</code> command uses an LLM to analyze the SQL queries and data schemas in your data project and automatically generate realistic test data that covers your logic paths:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJkAAAAAAAAAABBKUqGk9nLKveAAWfZGOBay0z6Z5GLG6YOlmfep-iUzktCOBFQ2tIf4zWZKB0xPnxGE3k8sTX5ywNn2l3_03j_zF5h5jI9vPUPukzQOZkCp0j2X7l9X6FOHAn___rakAA/embed"></iframe>
<!--kg-card-end: html-->
<p>This command:</p><ul><li>Analyzes the table schema of the supplied data source to understand the columns and types</li><li>Analyze the SQL structure of the pipes that select from this data source (including the template logic)</li><li>Generates synthetic data that exercises different code paths (for example different values for query params or if/else logic in the templating language)</li><li>Creates a mock data file (fixture) that you can use for testing</li></ul><p>That solves most of the boring part of generating fixtures, which is handling all the type matching and the logic.</p><p>But it doesn't solve everything. As I mentioned, production data is messy, and it turns out that generating production-like data is pretty hard. You have to account for all kinds of details like seasonality (your website has less traffic on weekends and during the holidays), state (a shopper on your e-commerce store shouldn't produce a <code>checkout</code> event if they haven't first produced an <code>add_to_cart</code> event), and reasonable values (temperatures can't drop below 0K).</p><p>If you want to test an edge case, it’s unlikely the LLM running under the hood can spot it right away, so we added a way to tell the LLMs what you want:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAALLAAAAAAAAAABBKUqGk9nLKvEmFldDXn-I9-8_ZpVt-8uya_FU7RhW1PVARRCGi6PVmOV6h6BPSVwKb6IajCKyZwWi1bvfMpLyMqg0UH7nvMso6ePlpgLFXmtqBCbTFe8DbaH_kIyl4pi1_-Ho3AbnCFI9gNnt2Md3LUOu_zSR2TIbkCmkKtVvvYs4olSxRXi1bMH1AnCCa9TtQkq1lxTMUIqldgdEWcUqaMDxAJJ9bdkTdS7n_vtLegA/embed"></iframe>
<!--kg-card-end: html-->
<p>You can get as complex as you want with the prompt. Tinybird saves the prompt in the <code>fixtures/</code> folder so you can reuse it and modify it if you want.</p><p>The fixtures generated are just .ndjson files, easy to modify by hand (or with an LLM) if needed.&nbsp;</p><p>Last but not least, we use a nice trick to generate the data: instead of generating just the ndjson, we generate the SQL that runs on the <a href="https://www.tinybird.co/blog-posts/local-first"><u>Tinybird Local database</u></a> to generate the data. This way, you can change the SQL and the data will be regenerated based on your query. And you can generate as much data as you want (without killing your cloud bill on LLM tokens).</p><h2 id="testing-sql-logic-with-tb-test">Testing SQL logic with <code>tb test</code></h2><p>Once you have mock data, the <code>tb test</code> command provides a framework for validating your data transformations. But first, you need to generate the tests.</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJrAAAAAAAAAABBKUqGk9nLKvqsCWfZGOBayajObOYk5MZUDxP9H4OFxPidchO71jVhFqbhVQhQ5mmpCNIYTOeruhqEeXKb-HktersiexAztlQsNSsEosHlu0ZyiGonDxeDNeWpCEmUvrawP_5IJwA/embed"></iframe>
<!--kg-card-end: html-->
<p>This analyzes the SQL of the supplied endpoint pipe and generates a set of tests that cover all the possible paths based on the parameters (well, maybe not <em>all </em>of them, but at least the ones the underlying LLMs can detect).</p><p>It then generates some YAML files in the <code>tests/</code> directory that define these tests.</p><p>Then, you can run the tests:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJVAAAAAAAAAABBKUqGk9nLKvLLfETdK6gFCNqrPaNWLog12SVdDdFGVrQVc7NxKruL3G6u3pgf8GeLKPH1Tx905awQ-P0t0I2AeVez3mii6ENTFUuX6HtQdTL_2MxQAA/embed"></iframe>
<!--kg-card-end: html-->
<p>One of the pain points when working with data tests is that when the logic or data changes, you need to update the expected result. That's covered, too:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJpAAAAAAAAAABBKUqGk9nLKveQiWRgLJY4ehqdhL_bdVUawWybkMgzkjSOXIQCY9KyeN_brXOtiNjinErJ4RXZF1g2A5UBmJ3VuS5Plw9mIyvywzvvvDnZc5ARRwfVeqPOyEjlKi4sL___ragAAA/embed"></iframe>
<!--kg-card-end: html-->
<p>This reruns the tests and updates the expected results to match the current implementation. Of course, you need to be careful with this, but it's super handy when you actually do expect something different than what you're currently testing for.</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/WA2KeQONUu4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Generate mock data and tests using LLMs"></iframe></figure><h2 id="did-we-solve-the-testing-problem">Did we solve the testing problem?</h2><p>To be honest, I don’t know. We've provided a set of tools to make it less boring to run tests on your data, but you still need a testing culture, and that's something data engineering doesn't have yet. Our goal when we designed this was to give you at least some basic tests without investing a lot of time. So, you can go to the project and run...</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAALeAAAAAAAAAABBKUqGk9nLKwdaa_Gge4sJ1mwfssjHFEh37O7tf5XiI_VMGeIYLnx1xGylqXEMB0ShI5v0MQYhsGoENiNRq93NVCqJI8en6Bm268iMVLbDOrhz5_Kxec8n4l3d22zZRLVQd9QPfH6xr1gONKWFiEfE19hVfpl7kuNBx7-9SVnpgunc5BUZSw3cwl4YcViKuxGpXt9JQFInEUAqWfuLwz7vfCwvfZyDBhFgCO5Qn1hJ9xtxnngbE1H_t7egAA/embed"></iframe>
<!--kg-card-end: html-->
<p>… and some tests will run in your CI (because we also generate CI workflow config files for GitHub/GitLab that automatically call <code>tb test run</code>).</p><p>So really, there's no excuse not to run tests, because we've made it super easy. We'll see if people actually do it.</p><h2 id="get-started-with-tb-test">Get started with <code>tb test</code></h2><p>If you already have an existing data project, just create tests for your endpoints:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKGAAAAAAAAAABBKUqGk9nLKwEEIWfZGOBazOm1Yq_Af4iEI1Cwt6L3X0nh5vVZbZ_HZbB5yp20NRYlrqRnsoWrR_kZkSUECjAHUWzn6am93NfSfoLpy6Cho6r6hRXgul2p089z26W53tQor_kB0zABbXaU2ojqh1um6c5P5PBHUiCy5f-KuoAA/embed"></iframe>
<!--kg-card-end: html-->
<p>Then just push the generated project to your git provider (don't forget to add secrets for host and token to authorize your CI to run against your project).</p><p>You can find more info in the docs on <a href="https://www.tinybird.co/docs/forward/test-and-deploy/test-your-project"><u>creating mock data and tests</u></a>, and learn more about <a href="https://www.tinybird.co/docs/forward/test-and-deploy/deployments/cicd"><u>testing and deploying in CI/CD</u></a>.</p><p>If you don't have a data project to test, install the CLI:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJqAAAAAAAAAABBKUqGk9nLKveFXIuu24V3EPFSfAfk-FVV1U7jYZu6Yfw-2nPaTdPajDV83PPieYgP5czNEwW0sllZEG_dSBMAjolcLUSofcYvILvy9iBf_juJMOFsN3urcJm89h9k6-2rj17__8VjgAA/embed"></iframe>
<!--kg-card-end: html-->
<p>And just follow the prompts to create an API in a few minutes.</p>
