<?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: Milan Martiniak</title>
    <description>The latest articles on DEV Community by Milan Martiniak (@milan_martiniak_1216b0004).</description>
    <link>https://dev.to/milan_martiniak_1216b0004</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%2F2680612%2Ff173a568-ead8-4dd4-ad9d-329966b30682.jpg</url>
      <title>DEV Community: Milan Martiniak</title>
      <link>https://dev.to/milan_martiniak_1216b0004</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/milan_martiniak_1216b0004"/>
    <language>en</language>
    <item>
      <title>Let your AI agent write integration tests for your API</title>
      <dc:creator>Milan Martiniak</dc:creator>
      <pubDate>Thu, 29 Oct 2026 13:03:21 +0000</pubDate>
      <link>https://dev.to/milan_martiniak_1216b0004/let-your-ai-agent-write-integration-tests-for-your-api-1ha6</link>
      <guid>https://dev.to/milan_martiniak_1216b0004/let-your-ai-agent-write-integration-tests-for-your-api-1ha6</guid>
      <description>&lt;p&gt;AI agents write backend code fast. A new endpoint, a validation rule, a background&lt;br&gt;
job - a few minutes and it's done. The part that usually stays behind is the proof&lt;br&gt;
that the API really behaves the way you think it does.&lt;/p&gt;

&lt;p&gt;Unit tests help, and agents are happy to write them. However, they test classes,&lt;br&gt;
not the HTTP contract: status codes, headers, JSON shapes, auth, and what happens&lt;br&gt;
when a background job finishes two seconds later. That's what integration tests are&lt;br&gt;
for, and that's exactly the part I want the agent to do for me.&lt;/p&gt;

&lt;p&gt;In this post I'll show how I let an agent write, run and fix API integration tests&lt;br&gt;
with &lt;a href="https://www.teapie.fun" rel="noopener noreferrer"&gt;TeaPie&lt;/a&gt; - and why plain &lt;code&gt;.http&lt;/code&gt; files turned out to be&lt;br&gt;
a great format for it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 If you haven't used TeaPie yet, the&lt;br&gt;
&lt;a href="https://blog.burgyn.online/2026/03/12/teapie-getting-started" rel="noopener noreferrer"&gt;getting started post&lt;/a&gt; takes five minutes.&lt;br&gt;
You don't need it to follow this one, though.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Why TeaPie fits AI agents
&lt;/h2&gt;

&lt;p&gt;An agent works in a loop: write something, run it, read the output, fix it, repeat.&lt;br&gt;
The faster and clearer that loop is, the better the result. TeaPie fits it well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tests are plain text.&lt;/strong&gt; A test case is a &lt;code&gt;.http&lt;/code&gt; file with optional C# scripts
next to it. No test project, no compilation of a whole solution, no fixtures.
The agent writes a small file and runs it right away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple checks need no code.&lt;/strong&gt; &lt;code&gt;## TEST-EXPECT-STATUS: [201]&lt;/code&gt; is one line the agent
can't get wrong. C# scripts are there only for real assertions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One CLI command with an exit code.&lt;/strong&gt; &lt;code&gt;teapie test&lt;/code&gt; returns &lt;code&gt;0&lt;/code&gt; when everything
passes, so the agent (and your CI) knows immediately whether it's done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failures are easy to investigate.&lt;/strong&gt; &lt;code&gt;--requests-log-file&lt;/code&gt; writes every request and
response to a JSON file, so the agent can see exactly what the server returned
instead of guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything ends up in your PR.&lt;/strong&gt; The tests live next to the backend code, so you
review them like any other change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The missing piece is that the agent has to &lt;em&gt;know&lt;/em&gt; TeaPie: the file conventions,&lt;br&gt;
the directives, the &lt;code&gt;tp&lt;/code&gt; API in scripts. That's what the TeaPie skill is for.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install the TeaPie plugin
&lt;/h2&gt;

&lt;p&gt;You need the TeaPie CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; TeaPie.Tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Claude Code, install the TeaPie plugin. It contains a skill that teaches the agent&lt;br&gt;
everything about TeaPie - how to structure test cases, which directives exist,&lt;br&gt;
how to write &lt;code&gt;.csx&lt;/code&gt; scripts, and how to debug failing tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin install teapie --marketplace Kros-sk/TeaPie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a different agent, or want the skill committed into your repository so the&lt;br&gt;
whole team gets it? Download it into the project instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;teapie install-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This puts the skill into &lt;code&gt;.claude/skills/teapie/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask for tests
&lt;/h2&gt;

&lt;p&gt;I'll use &lt;a href="https://github.com/Burgyn/MMLib.DummyApi" rel="noopener noreferrer"&gt;MMLib.DummyApi&lt;/a&gt; as the backend&lt;br&gt;
again, so you can try it without writing an API first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 ghcr.io/burgyn/mmlib-dummyapi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its &lt;code&gt;orders&lt;/code&gt; endpoints are a nice test subject. They require an &lt;code&gt;X-Api-Key&lt;/code&gt;&lt;br&gt;
header, validate the body, and a background job moves each new order to&lt;br&gt;
&lt;code&gt;completed&lt;/code&gt; a few seconds after it's created.&lt;/p&gt;

&lt;p&gt;Now the prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cover the orders endpoints of the API running on http://localhost:8080 with TeaPie
integration tests. The API key is test-api-key-123. Include the happy path,
validation errors, a missing API key, and check that a new order eventually
reaches the 'completed' status. Run the tests and make sure they pass.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran exactly this prompt in Claude Code with the TeaPie plugin installed, in an&lt;br&gt;
empty folder. Here's what happened.&lt;/p&gt;

&lt;p&gt;First, the agent loaded the TeaPie skill. Then it found the OpenAPI document at&lt;br&gt;
&lt;code&gt;/openapi/v1.json&lt;/code&gt;. The spec doesn't say which header carries the API key, so it&lt;br&gt;
sent a few requests with &lt;code&gt;curl&lt;/code&gt; to see how the API really behaves. It read the&lt;br&gt;
skill's references on directives and retries, ran &lt;code&gt;teapie init&lt;/code&gt;, put the base URL&lt;br&gt;
and the API key into &lt;code&gt;.teapie/env.json&lt;/code&gt;, and wrote the tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests/teapie/
├── 001-Orders-Lifecycle/
│   ├── 001-Create-Order-req.http
│   ├── 001-Create-Order-test.csx
│   ├── 002-Get-Order-By-Id-req.http
│   ├── ...
│   └── 008-Get-Deleted-Order-req.http
├── 002-Orders-Validation/
│   ├── 001-Create-Order-Invalid-req.http
│   ├── 002-Update-Order-Invalid-req.http
│   └── 003-Non-Existent-Order-req.http
└── 003-Orders-Authentication/
    ├── 001-Missing-Api-Key-req.http
    └── 002-Invalid-Api-Key-req.http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;26 files, 60 checks, about four minutes from the prompt to a green run.&lt;br&gt;
&lt;em&gt;(It cost about $1.50 in API usage.)&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What the agent writes
&lt;/h2&gt;

&lt;p&gt;The tests look like the ones you'd write by hand. That's the point - you can read&lt;br&gt;
and review them in a minute. All snippets below are exactly what the agent wrote.&lt;/p&gt;

&lt;p&gt;The create request uses directives for the simple checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# @name CreateOrderRequest
## TEST-EXPECT-STATUS: [201]
## TEST-HAS-BODY
## TEST-HAS-HEADER: Location
POST {{ApiBaseUrl}}/orders
Content-Type: application/json
X-Api-Key: {{ApiKey}}

{
    "customerId": "{{$guid}}",
    "customerName": "Jane Doe",
    "customerEmail": "jane.doe@example.com",
    "totalAmount": 149.99,
    "shippingAddress": "Main Street 12",
    "shippingCity": "Bratislava",
    "shippingCountry": "Slovakia"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The post-response script compares the response with the request and stores the&lt;br&gt;
ID for the following test cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Created order should match the request and have an ID."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;requestBody&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;responseBody&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;JsonContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;dynamic&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBodyAsExpandoAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;True&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryParse&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// 👇 shared with the next test cases in the collection&lt;/span&gt;
    &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OrderId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CreatedOrder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The background job is where agents usually reach for &lt;code&gt;Thread.Sleep&lt;/code&gt;. With the&lt;br&gt;
skill, it used a TeaPie retry strategy instead. It registered the strategy once&lt;br&gt;
in &lt;code&gt;.teapie/init.csx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterRetryStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Wait for order processing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RetryStrategyOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;MaxRetryAttempts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Delay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;BackoffType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DelayBackoffType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Constant&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it retried the request until the test passed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;## RETRY-STRATEGY: Wait for order processing
## RETRY-UNTIL-TEST-PASS: Order should eventually reach the completed status.
## TEST-EXPECT-STATUS: [200]
GET {{ApiBaseUrl}}/orders/{{OrderId}}
X-Api-Key: {{ApiKey}}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order should eventually reach the completed status."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;responseBody&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JsonDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;RootElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;True&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Order has no status yet."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The negative cases are just directives. Several requests share one file,&lt;br&gt;
separated by &lt;code&gt;###&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# @name GetAllOrdersWithoutApiKey
## TEST-EXPECT-STATUS: [401]
GET {{ApiBaseUrl}}/orders

###

# @name CreateOrderWithoutApiKey
## TEST-EXPECT-STATUS: [401]
POST {{ApiBaseUrl}}/orders
Content-Type: application/json

{ ... }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The loop: run, read, fix
&lt;/h2&gt;

&lt;p&gt;Then the agent ran the whole collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;teapie &lt;span class="nb"&gt;test &lt;/span&gt;tests/teapie
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first run wasn't green: 58 checks passed, 2 failed. The interesting one was&lt;br&gt;
the test that reads the new order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'TeaPie.Json.CaseInsensitiveExpandoObject' does not contain a definition for 'status'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent didn't guess. It re-ran just the lifecycle folder, watched the retries,&lt;br&gt;
and found out that the API returns a new order without any &lt;code&gt;status&lt;/code&gt; for the first&lt;br&gt;
three seconds or so. Then it goes &lt;code&gt;pending&lt;/code&gt;, &lt;code&gt;processing&lt;/code&gt;, and &lt;code&gt;completed&lt;/code&gt; after&lt;br&gt;
about ten seconds. The agent adjusted the tests to that behavior, ran the&lt;br&gt;
collection again, and got 60 passed checks and exit code &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And then the part I liked the most. In its summary, it pointed out that an order&lt;br&gt;
without a status might be a bug in the API, and that if a new order should be&lt;br&gt;
&lt;code&gt;pending&lt;/code&gt; right away, the check should become strict. That's exactly the&lt;br&gt;
conversation you want to have during code review.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💁 When the console output isn't enough, the skill tells the agent to re-run&lt;br&gt;
the failing test case with &lt;code&gt;--requests-log-file requests.json&lt;/code&gt; (every request&lt;br&gt;
and response as JSON) or with &lt;code&gt;-v&lt;/code&gt; for verbose logging. In my run, the console&lt;br&gt;
output was enough.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice what happened there, though. The agent made the tests pass by adapting&lt;br&gt;
them to the API, and only &lt;em&gt;mentioned&lt;/em&gt; the possible bug. Mine did it openly, but&lt;br&gt;
don't count on that every time. If you test an API you own, tell the agent in&lt;br&gt;
the prompt to report suspicious behavior instead of changing the expected values,&lt;br&gt;
and review the assertions in the PR.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tests from an OpenAPI specification
&lt;/h2&gt;

&lt;p&gt;If your API has an OpenAPI document, you can go one step further:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create TeaPie tests for all endpoints in our OpenAPI specification.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skill describes how to find the specification (a file in the repository or a&lt;br&gt;
runtime endpoint such as &lt;code&gt;/swagger/v1/swagger.json&lt;/code&gt;) and how to turn each operation&lt;br&gt;
into test cases with the right status codes and request bodies. It's a good way to&lt;br&gt;
get a baseline for an existing API, and then you add the scenarios that matter&lt;br&gt;
to your domain.&lt;/p&gt;
&lt;h2&gt;
  
  
  Run it in CI
&lt;/h2&gt;

&lt;p&gt;Nothing changes for CI. It's the same command, plus a JUnit XML report that&lt;br&gt;
GitHub Actions or Azure DevOps can display:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;teapie &lt;span class="nb"&gt;test &lt;/span&gt;Tests &lt;span class="nt"&gt;-e&lt;/span&gt; staging &lt;span class="nt"&gt;-r&lt;/span&gt; test-results.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent writes the tests, CI runs them on every PR, and you review both the code&lt;br&gt;
and the tests in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;For me, the combination works because both sides are simple. TeaPie tests are&lt;br&gt;
small text files with clear conventions, and the skill gives the agent exactly the&lt;br&gt;
knowledge it needs to write them and debug them. You describe &lt;em&gt;what&lt;/em&gt; to test, the&lt;br&gt;
agent handles the &lt;em&gt;how&lt;/em&gt;, and you stay in control through code review.&lt;/p&gt;

&lt;p&gt;Give it a try on one of your endpoints. And if you have ideas on what the skill&lt;br&gt;
should do better, &lt;a href="https://github.com/Kros-sk/TeaPie/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Kros-sk/TeaPie" rel="noopener noreferrer"&gt;TeaPie on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.teapie.fun/docs/introduction.html" rel="noopener noreferrer"&gt;TeaPie documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.teapie.fun/docs/ai-agent-skills.html" rel="noopener noreferrer"&gt;AI Agent Skills documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.teapie.fun/docs/requests-logging.html" rel="noopener noreferrer"&gt;Requests logging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.teapie.fun/docs/retrying.html" rel="noopener noreferrer"&gt;Retrying&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Burgyn/MMLib.DummyApi" rel="noopener noreferrer"&gt;MMLib.DummyApi on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.burgyn.online/2026/03/12/teapie-getting-started" rel="noopener noreferrer"&gt;TeaPie: Test your REST API in 5 minutes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.burgyn.online/2026/03/18/teapie-real-world-api-testing" rel="noopener noreferrer"&gt;TeaPie: Testing real-world API scenarios&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://blog.burgyn.online/2026/10/29/teapie-ai-agent-integration-tests" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>testing</category>
      <category>api</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
