<?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: RODRIGO SIDNEY COLQUE QUISPE</title>
    <description>The latest articles on DEV Community by RODRIGO SIDNEY COLQUE QUISPE (@rodrigo_sidneycolquequi).</description>
    <link>https://dev.to/rodrigo_sidneycolquequi</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%2F3889989%2F01a2c073-b7e4-4d28-9337-b2294ffcec60.png</url>
      <title>DEV Community: RODRIGO SIDNEY COLQUE QUISPE</title>
      <link>https://dev.to/rodrigo_sidneycolquequi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rodrigo_sidneycolquequi"/>
    <language>en</language>
    <item>
      <title>Testing Management Tools: A Comparative Guide and Real-World Reporting</title>
      <dc:creator>RODRIGO SIDNEY COLQUE QUISPE</dc:creator>
      <pubDate>Sat, 27 Jun 2026 15:58:53 +0000</pubDate>
      <link>https://dev.to/rodrigo_sidneycolquequi/testing-management-tools-a-comparative-guide-and-real-world-reporting-g5b</link>
      <guid>https://dev.to/rodrigo_sidneycolquequi/testing-management-tools-a-comparative-guide-and-real-world-reporting-g5b</guid>
      <description>&lt;h1&gt;
  
  
  Testing Management Tools: A Comparative Guide and Real-World Reporting
&lt;/h1&gt;

&lt;p&gt;As software projects grow, keeping track of what has been tested, what failed, and what needs to be re-tested becomes a logistical nightmare. Spreadsheets just don't cut it anymore. This is where &lt;strong&gt;Testing Management and Reporting Tools&lt;/strong&gt; come into play.&lt;/p&gt;

&lt;p&gt;In this article, we will compare three of the most popular test management tools in the industry and provide a real-world code example showing how to generate a management dashboard locally for your automated tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. TestRail
&lt;/h2&gt;

&lt;p&gt;TestRail is a dedicated, standalone web-based test case management tool. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Pros:&lt;/strong&gt; Incredible UI/UX, highly customizable, excellent reporting metrics, and a robust REST API.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cons:&lt;/strong&gt; Can be expensive for large teams, not natively built inside Jira (though it has an integration plugin).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Best for:&lt;/strong&gt; Teams that want a dedicated, powerful platform solely for QA and testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Zephyr Scale (formerly TM4J)
&lt;/h2&gt;

&lt;p&gt;Zephyr Scale is built directly into Jira.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Pros:&lt;/strong&gt; Seamless Jira integration (issues, epics, and bugs are natively linked to tests), very cost-effective if you already use Jira.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cons:&lt;/strong&gt; The UI can feel cluttered since it lives inside Jira. Slower performance on massive test suites compared to standalone tools.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Best for:&lt;/strong&gt; Agile teams already heavily invested in the Atlassian (Jira) ecosystem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Pytest-HTML / Local Reporting Tools
&lt;/h2&gt;

&lt;p&gt;While TestRail and Zephyr are enterprise solutions, many developers use open-source local reporting plugins like &lt;strong&gt;pytest-html&lt;/strong&gt; or &lt;strong&gt;Allure&lt;/strong&gt; to generate self-contained HTML management dashboards from their test runs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Pros:&lt;/strong&gt; 100% Free, runs offline on your own machine, extremely fast, generates a beautiful visual HTML dashboard.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cons:&lt;/strong&gt; Doesn't support manual test case writing (purely for automated tests), and doesn't store historical data across years unless you save the files.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Best for:&lt;/strong&gt; Individual developers, students, or teams that need instant, visual reporting directly from their CI pipeline without paying for a SaaS tool.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Code Example: Generating a Test Dashboard Locally
&lt;/h2&gt;

&lt;p&gt;Let's look at how we can implement a local reporting/management tool using &lt;strong&gt;Python&lt;/strong&gt; and the &lt;code&gt;pytest-html&lt;/code&gt; plugin. This will allow us to run tests and instantly get a professional HTML dashboard showing exactly what passed, what failed, and how long it took.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;

&lt;p&gt;First, we install Pytest and the HTML reporting plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest pytest-html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we write some simple tests for a mock Login system in a file called &lt;code&gt;test_login.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_successful_login&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Test a valid login scenario.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Mock login logic
&lt;/span&gt;    &lt;span class="n"&gt;is_logged_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# We expect this test to PASS
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;is_logged_in&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_failed_login&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Test an invalid login scenario.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wrongpassword&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;is_logged_in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# We expect this test to FAIL (because it returns False)
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;is_logged_in&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Execution and Dashboard Generation
&lt;/h3&gt;

&lt;p&gt;Normally, you would run &lt;code&gt;pytest&lt;/code&gt; and get a text output in the terminal. To use our reporting tool, we pass a special flag to generate the dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest test_login.py &lt;span class="nt"&gt;--html&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;report.html &lt;span class="nt"&gt;--self-contained-html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When this runs, &lt;code&gt;pytest-html&lt;/code&gt; intercepts the results and creates a visually appealing file named &lt;code&gt;report.html&lt;/code&gt;. If you open this file in Google Chrome or any browser, you will see a full Test Management dashboard displaying:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Environment metrics (Python version, OS).&lt;/li&gt;
&lt;li&gt;A Summary table (1 Passed, 1 Failed).&lt;/li&gt;
&lt;li&gt;The exact logs and tracebacks for the failed tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Public Example Repository:&lt;/strong&gt;&lt;br&gt;
You can find the complete, runnable code example for this integration in this GitHub repository: &lt;br&gt;
🔗 &lt;code&gt;https://github.com/Rodrig0496/U3_Article.git&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;By automating your test reporting, your QA managers get real-time visibility into the health of the application without the developers or testers having to do any manual data entry.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>python</category>
      <category>pytest</category>
      <category>reporting</category>
    </item>
    <item>
      <title>Applying API Testing Frameworks in the Real World: A Practical Guide with Pytest</title>
      <dc:creator>RODRIGO SIDNEY COLQUE QUISPE</dc:creator>
      <pubDate>Sat, 27 Jun 2026 15:40:37 +0000</pubDate>
      <link>https://dev.to/rodrigo_sidneycolquequi/applying-api-testing-frameworks-in-the-real-world-a-practical-guide-with-pytest-3cjg</link>
      <guid>https://dev.to/rodrigo_sidneycolquequi/applying-api-testing-frameworks-in-the-real-world-a-practical-guide-with-pytest-3cjg</guid>
      <description>&lt;p&gt;In today's interconnected software landscape, Application Programming Interfaces (APIs) are the bridges that allow different systems to communicate. With the growing reliance on microservices and third-party integrations, ensuring that your APIs are robust, secure, and performant is no longer optional—it's critical.&lt;/p&gt;

&lt;p&gt;This is where API testing frameworks come in. In this article, we'll explore how to apply API testing in the real world using Python and Pytest, one of the most popular and powerful testing frameworks available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do We Need API Testing Frameworks?
&lt;/h2&gt;

&lt;p&gt;Manual testing using tools like Postman or Insomnia is great for exploration, but it doesn't scale. When you have hundreds of endpoints and continuous integration/continuous deployment (CI/CD) pipelines, you need automated frameworks to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ensure Reliability:&lt;/strong&gt; Catch breaking changes before they reach production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate Business Logic:&lt;/strong&gt; Verify that the API returns the correct data for both valid and invalid inputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check Performance and Security:&lt;/strong&gt; Ensure the API can handle load and is secure against common vulnerabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Tooling: Python, Pytest, and Requests
&lt;/h2&gt;

&lt;p&gt;For our real-world example, we'll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python:&lt;/strong&gt; A versatile language widely used for test automation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pytest:&lt;/strong&gt; A mature testing framework that makes writing small tests easy, yet scales to support complex functional testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requests:&lt;/strong&gt; The elegant and simple HTTP library for Python to make API calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Scenario: Testing a User Management API
&lt;/h2&gt;

&lt;p&gt;Imagine we are building a backend for a social media application. We have an API endpoint to retrieve user profiles. We need to ensure that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A valid request returns a &lt;code&gt;200 OK&lt;/code&gt; status and the correct user data structure.&lt;/li&gt;
&lt;li&gt;Requesting a non-existent user returns a &lt;code&gt;404 Not Found&lt;/code&gt; status.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's write tests for a free, public mock API (&lt;a href="https://jsonplaceholder.typicode.com" rel="noopener noreferrer"&gt;https://jsonplaceholder.typicode.com&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Setting up the environment
&lt;/h3&gt;

&lt;p&gt;First, install the necessary packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Writing our first tests
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;test_users_api.py&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://jsonplaceholder.typicode.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_valid_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Test that retrieving an existing user returns a 200 status code
    and the correct data structure.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Assert Status Code
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected 200, but got &lt;/span&gt;&lt;span class="si"&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;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Parse JSON response
&lt;/span&gt;    &lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&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;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Assert Data Structure and Content
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_nonexistent_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Test that retrieving a user that does not exist returns a 404 status code.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;  &lt;span class="c1"&gt;# Assuming this user doesn't exist
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Assert Status Code
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected 404, but got &lt;/span&gt;&lt;span class="si"&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;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Check that the response is empty as expected by this mock API
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Running the tests
&lt;/h3&gt;

&lt;p&gt;Execute the tests in your terminal using the pytest command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest test_users_api.py &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;============================= test session starts ==============================
...
test_users_api.py::test_get_valid_user PASSED                            [ 50%]
test_users_api.py::test_get_nonexistent_user PASSED                      [100%]

============================== 2 passed in 0.45s ===============================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Real-World Practices
&lt;/h2&gt;

&lt;p&gt;While the example above is simple, real-world API testing frameworks incorporate more advanced patterns:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Data-Driven Testing with &lt;code&gt;@pytest.mark.parametrize&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of writing separate functions for every edge case, you can parameterize your tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id, expected_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_users_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Authentication and Setup with Pytest Fixtures
&lt;/h3&gt;

&lt;p&gt;If your API requires authentication (e.g., Bearer tokens), you shouldn't log in inside every test. Use fixtures to handle setup and teardown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Code to authenticate and get token
&lt;/span&gt;    &lt;span class="c1"&gt;# login_response = requests.post(f"{BASE_URL}/login", json={"email": "...", "password": "..."})
&lt;/span&gt;    &lt;span class="c1"&gt;# return login_response.json()["token"]
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mock_token_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_secure_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/secure-data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ... assertions
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Schema Validation
&lt;/h3&gt;

&lt;p&gt;In the real world, APIs evolve. Instead of manually asserting every key in a JSON response, use schema validation libraries like &lt;code&gt;Cerberus&lt;/code&gt; or &lt;code&gt;jsonschema&lt;/code&gt; to ensure the response payload matches the expected contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a robust API testing framework is an investment that pays off exponentially as your application grows. By leveraging Python, &lt;code&gt;requests&lt;/code&gt;, and the powerful features of &lt;code&gt;pytest&lt;/code&gt; like parametrization and fixtures, you can create a test suite that is easy to maintain, highly readable, and perfectly suited for CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Start small, automate your most critical endpoints first, and gradually build out your coverage. Happy testing!&lt;/p&gt;

</description>
      <category>api</category>
      <category>testing</category>
      <category>python</category>
      <category>pytest</category>
    </item>
    <item>
      <title>Applying API Testing Frameworks in the Real World: A Practical Guide with Pytest</title>
      <dc:creator>RODRIGO SIDNEY COLQUE QUISPE</dc:creator>
      <pubDate>Sat, 27 Jun 2026 14:35:34 +0000</pubDate>
      <link>https://dev.to/rodrigo_sidneycolquequi/applying-api-testing-frameworks-in-the-real-world-a-practical-guide-with-pytest-2pj8</link>
      <guid>https://dev.to/rodrigo_sidneycolquequi/applying-api-testing-frameworks-in-the-real-world-a-practical-guide-with-pytest-2pj8</guid>
      <description>&lt;h1&gt;
  
  
  Applying API Testing Frameworks in the Real World: A Practical Guide with Pytest
&lt;/h1&gt;

&lt;p&gt;In today's interconnected software landscape, Application Programming Interfaces (APIs) are the bridges that allow different systems to communicate. With the growing reliance on microservices and third-party integrations, ensuring that your APIs are robust, secure, and performant is no longer optional—it's critical. &lt;/p&gt;

&lt;p&gt;This is where API testing frameworks come in. In this article, we'll explore how to apply API testing in the real world using &lt;strong&gt;Python&lt;/strong&gt; and &lt;strong&gt;Pytest&lt;/strong&gt;, one of the most popular and powerful testing frameworks available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do We Need API Testing Frameworks?
&lt;/h2&gt;

&lt;p&gt;Manual testing using tools like Postman or Insomnia is great for exploration, but it doesn't scale. When you have hundreds of endpoints and continuous integration/continuous deployment (CI/CD) pipelines, you need automated frameworks to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ensure Reliability:&lt;/strong&gt; Catch breaking changes before they reach production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate Business Logic:&lt;/strong&gt; Verify that the API returns the correct data for both valid and invalid inputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check Performance and Security:&lt;/strong&gt; Ensure the API can handle load and is secure against common vulnerabilities.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Tooling: Python, Pytest, and Requests
&lt;/h2&gt;

&lt;p&gt;For our real-world example, we'll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Python:&lt;/strong&gt; A versatile language widely used for test automation.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Pytest:&lt;/strong&gt; A mature testing framework that makes writing small tests easy, yet scales to support complex functional testing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Requests:&lt;/strong&gt; The elegant and simple HTTP library for Python to make API calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Scenario: Testing a User Management API
&lt;/h2&gt;

&lt;p&gt;Imagine we are building a backend for a social media application. We have an API endpoint to retrieve user profiles. We need to ensure that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A valid request returns a &lt;code&gt;200 OK&lt;/code&gt; status and the correct user data structure.&lt;/li&gt;
&lt;li&gt; Requesting a non-existent user returns a &lt;code&gt;404 Not Found&lt;/code&gt; status.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's write tests for a mock API (e.g., &lt;code&gt;https://reqres.in/api/users&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Setting up the environment
&lt;/h3&gt;

&lt;p&gt;First, install the necessary packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Writing our first tests
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;test_users_api.py&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://reqres.in/api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_valid_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Test that retrieving an existing user returns a 200 status code
    and the correct data structure.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Assert Status Code
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected 200, but got &lt;/span&gt;&lt;span class="si"&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;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Parse JSON response
&lt;/span&gt;    &lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&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;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Assert Data Structure and Content
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_nonexistent_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Test that retrieving a user that does not exist returns a 404 status code.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;  &lt;span class="c1"&gt;# Assuming this user doesn't exist
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Assert Status Code
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected 404, but got &lt;/span&gt;&lt;span class="si"&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;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Check that the response is empty as expected by this mock API
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Running the tests
&lt;/h3&gt;

&lt;p&gt;Execute the tests in your terminal using the &lt;code&gt;pytest&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest test_users_api.py &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;============================= test session starts ==============================
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;test_users_api.py::test_get_valid_user PASSED                            [ 50%]
test_users_api.py::test_get_nonexistent_user PASSED                      [100%]

============================== 2 passed in 0.45s ===============================
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Real-World Practices
&lt;/h2&gt;

&lt;p&gt;While the example above is simple, real-world API testing frameworks incorporate more advanced patterns:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Data-Driven Testing with &lt;code&gt;@pytest.mark.parametrize&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of writing separate functions for every edge case, you can parameterize your tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id, expected_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_users_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Authentication and Setup with Pytest Fixtures
&lt;/h3&gt;

&lt;p&gt;If your API requires authentication (e.g., Bearer tokens), you shouldn't log in inside every test. Use &lt;code&gt;fixtures&lt;/code&gt; to handle setup and teardown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Code to authenticate and get token
&lt;/span&gt;    &lt;span class="c1"&gt;# login_response = requests.post(f"{BASE_URL}/login", json={"email": "...", "password": "..."})
&lt;/span&gt;    &lt;span class="c1"&gt;# return login_response.json()["token"]
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mock_token_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_secure_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/secure-data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ... assertions
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Schema Validation
&lt;/h3&gt;

&lt;p&gt;In the real world, APIs evolve. Instead of manually asserting every key in a JSON response, use schema validation libraries like &lt;code&gt;Cerberus&lt;/code&gt; or &lt;code&gt;jsonschema&lt;/code&gt; to ensure the response payload matches the expected contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a robust API testing framework is an investment that pays off exponentially as your application grows. By leveraging Python, &lt;code&gt;requests&lt;/code&gt;, and the powerful features of &lt;code&gt;pytest&lt;/code&gt; like parametrization and fixtures, you can create a test suite that is easy to maintain, highly readable, and perfectly suited for CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Start small, automate your most critical endpoints first, and gradually build out your coverage. Happy testing!&lt;/p&gt;

</description>
      <category>api</category>
      <category>testing</category>
      <category>python</category>
      <category>pytest</category>
    </item>
    <item>
      <title>Applying API Testing Frameworks in the Real World: A Practical Guide with Pytest</title>
      <dc:creator>RODRIGO SIDNEY COLQUE QUISPE</dc:creator>
      <pubDate>Sat, 27 Jun 2026 14:35:34 +0000</pubDate>
      <link>https://dev.to/rodrigo_sidneycolquequi/applying-api-testing-frameworks-in-the-real-world-a-practical-guide-with-pytest-567d</link>
      <guid>https://dev.to/rodrigo_sidneycolquequi/applying-api-testing-frameworks-in-the-real-world-a-practical-guide-with-pytest-567d</guid>
      <description>&lt;h1&gt;
  
  
  Applying API Testing Frameworks in the Real World: A Practical Guide with Pytest
&lt;/h1&gt;

&lt;p&gt;In today's interconnected software landscape, Application Programming Interfaces (APIs) are the bridges that allow different systems to communicate. With the growing reliance on microservices and third-party integrations, ensuring that your APIs are robust, secure, and performant is no longer optional—it's critical. &lt;/p&gt;

&lt;p&gt;This is where API testing frameworks come in. In this article, we'll explore how to apply API testing in the real world using &lt;strong&gt;Python&lt;/strong&gt; and &lt;strong&gt;Pytest&lt;/strong&gt;, one of the most popular and powerful testing frameworks available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do We Need API Testing Frameworks?
&lt;/h2&gt;

&lt;p&gt;Manual testing using tools like Postman or Insomnia is great for exploration, but it doesn't scale. When you have hundreds of endpoints and continuous integration/continuous deployment (CI/CD) pipelines, you need automated frameworks to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ensure Reliability:&lt;/strong&gt; Catch breaking changes before they reach production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate Business Logic:&lt;/strong&gt; Verify that the API returns the correct data for both valid and invalid inputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check Performance and Security:&lt;/strong&gt; Ensure the API can handle load and is secure against common vulnerabilities.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Tooling: Python, Pytest, and Requests
&lt;/h2&gt;

&lt;p&gt;For our real-world example, we'll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Python:&lt;/strong&gt; A versatile language widely used for test automation.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Pytest:&lt;/strong&gt; A mature testing framework that makes writing small tests easy, yet scales to support complex functional testing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Requests:&lt;/strong&gt; The elegant and simple HTTP library for Python to make API calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Scenario: Testing a User Management API
&lt;/h2&gt;

&lt;p&gt;Imagine we are building a backend for a social media application. We have an API endpoint to retrieve user profiles. We need to ensure that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A valid request returns a &lt;code&gt;200 OK&lt;/code&gt; status and the correct user data structure.&lt;/li&gt;
&lt;li&gt; Requesting a non-existent user returns a &lt;code&gt;404 Not Found&lt;/code&gt; status.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's write tests for a mock API (e.g., &lt;code&gt;https://reqres.in/api/users&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Setting up the environment
&lt;/h3&gt;

&lt;p&gt;First, install the necessary packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Writing our first tests
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;test_users_api.py&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://reqres.in/api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_valid_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Test that retrieving an existing user returns a 200 status code
    and the correct data structure.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Assert Status Code
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected 200, but got &lt;/span&gt;&lt;span class="si"&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;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Parse JSON response
&lt;/span&gt;    &lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&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;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Assert Data Structure and Content
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_nonexistent_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Test that retrieving a user that does not exist returns a 404 status code.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;  &lt;span class="c1"&gt;# Assuming this user doesn't exist
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Assert Status Code
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected 404, but got &lt;/span&gt;&lt;span class="si"&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;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Check that the response is empty as expected by this mock API
&lt;/span&gt;    &lt;span class="k"&gt;assert&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;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Running the tests
&lt;/h3&gt;

&lt;p&gt;Execute the tests in your terminal using the &lt;code&gt;pytest&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest test_users_api.py &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;============================= test session starts ==============================
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;test_users_api.py::test_get_valid_user PASSED                            [ 50%]
test_users_api.py::test_get_nonexistent_user PASSED                      [100%]

============================== 2 passed in 0.45s ===============================
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Real-World Practices
&lt;/h2&gt;

&lt;p&gt;While the example above is simple, real-world API testing frameworks incorporate more advanced patterns:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Data-Driven Testing with &lt;code&gt;@pytest.mark.parametrize&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of writing separate functions for every edge case, you can parameterize your tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id, expected_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_users_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Authentication and Setup with Pytest Fixtures
&lt;/h3&gt;

&lt;p&gt;If your API requires authentication (e.g., Bearer tokens), you shouldn't log in inside every test. Use &lt;code&gt;fixtures&lt;/code&gt; to handle setup and teardown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Code to authenticate and get token
&lt;/span&gt;    &lt;span class="c1"&gt;# login_response = requests.post(f"{BASE_URL}/login", json={"email": "...", "password": "..."})
&lt;/span&gt;    &lt;span class="c1"&gt;# return login_response.json()["token"]
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mock_token_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_secure_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/secure-data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# ... assertions
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Schema Validation
&lt;/h3&gt;

&lt;p&gt;In the real world, APIs evolve. Instead of manually asserting every key in a JSON response, use schema validation libraries like &lt;code&gt;Cerberus&lt;/code&gt; or &lt;code&gt;jsonschema&lt;/code&gt; to ensure the response payload matches the expected contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a robust API testing framework is an investment that pays off exponentially as your application grows. By leveraging Python, &lt;code&gt;requests&lt;/code&gt;, and the powerful features of &lt;code&gt;pytest&lt;/code&gt; like parametrization and fixtures, you can create a test suite that is easy to maintain, highly readable, and perfectly suited for CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;Start small, automate your most critical endpoints first, and gradually build out your coverage. Happy testing!&lt;/p&gt;

</description>
      <category>api</category>
      <category>testing</category>
      <category>python</category>
      <category>pytest</category>
    </item>
    <item>
      <title>Breaking the SQL Barrier: How to Build a Natural Language Database Assistant</title>
      <dc:creator>RODRIGO SIDNEY COLQUE QUISPE</dc:creator>
      <pubDate>Sat, 27 Jun 2026 04:45:15 +0000</pubDate>
      <link>https://dev.to/rodrigo_sidneycolquequi/breaking-the-sql-barrier-how-to-build-a-natural-language-database-assistant-4fb7</link>
      <guid>https://dev.to/rodrigo_sidneycolquequi/breaking-the-sql-barrier-how-to-build-a-natural-language-database-assistant-4fb7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #DataEngineering #AI #Python #HuggingFace #Streamlit&lt;/p&gt;

&lt;p&gt;For decades, SQL has been the universal language for extracting insights from databases. But there's a catch: it creates a bottleneck. Business analysts, product managers, and marketers often have to wait for data teams to write queries for them. &lt;/p&gt;

&lt;p&gt;What if we could skip the code and just talk to our databases in plain English?&lt;/p&gt;

&lt;p&gt;Thanks to the rapid advancements in Artificial Intelligence and Large Language Models (LLMs), this is now entirely possible. Today, I'll walk you through how I built a &lt;strong&gt;Text-to-SQL assistant&lt;/strong&gt; using Python, and how you can do it too.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Text-to-SQL?
&lt;/h3&gt;

&lt;p&gt;At its core, Text-to-SQL is an AI capability that translates conversational questions into executable SQL code. Imagine typing, &lt;em&gt;"Show me all employees in the Sales department earning over 50k"&lt;/em&gt; and having the AI instantly generate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Sales'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s like having a senior data engineer at your fingertips 24/7.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;p&gt;To keep things simple and accessible, I chose a modern, lightweight stack for this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hugging Face:&lt;/strong&gt; To power the AI model (we're using &lt;code&gt;t5-base-finetuned-wikiSQL&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamlit:&lt;/strong&gt; To quickly build a clean, interactive user interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite &amp;amp; Pandas:&lt;/strong&gt; To handle our local mock data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How It Works Under the Hood
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. The Brains (Hugging Face API)
&lt;/h4&gt;

&lt;p&gt;Instead of training a model from scratch, we leverage Hugging Face's Inference API. By sending an HTTP request with our user's question, the API returns the translated SQL query. It's incredibly fast and requires very little code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api-inference.huggingface.co/models/mrm8488/t5-base-finetuned-wikiSQL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_sql_from_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inputs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;translate English to SQL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&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;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;generated_text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. The Data Layer
&lt;/h4&gt;

&lt;p&gt;For demonstration purposes, the app initializes an in-memory SQLite database loaded with some dummy employee records. This allows the app to actually &lt;em&gt;execute&lt;/em&gt; the AI-generated SQL and prove that it works, rather than just showing the query on the screen.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Putting it together with Streamlit
&lt;/h4&gt;

&lt;p&gt;Streamlit ties everything beautifully. We capture the user's input through a text box. When they hit "Generate", the app fetches the SQL from Hugging Face, executes it against our SQLite database using &lt;code&gt;pandas.read_sql_query&lt;/code&gt;, and renders the final dataset directly in the browser. &lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;Tools like this represent a massive shift in data democratization. When you remove the technical barrier of SQL, you empower everyone in an organization to be data-driven, speeding up decision-making across the board.&lt;/p&gt;

&lt;p&gt;Want to see the code in action or try running it yourself?&lt;br&gt;
I've made the entire project open-source. Check out my repository here:&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://github.com/FabricioRams/Research-Team-Work-N-01-SQL-AI-Database-Solutions.git" rel="noopener noreferrer"&gt;https://github.com/FabricioRams/Research-Team-Work-N-01-SQL-AI-Database-Solutions.git&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: Just install the requirements and run &lt;code&gt;streamlit run app.py&lt;/code&gt; to start chatting with your data!)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Under the Hood: How Bandit SAST Analyzes Your Python Code</title>
      <dc:creator>RODRIGO SIDNEY COLQUE QUISPE</dc:creator>
      <pubDate>Tue, 21 Apr 2026 16:10:54 +0000</pubDate>
      <link>https://dev.to/rodrigo_sidneycolquequi/under-the-hood-how-bandit-sast-analyzes-your-python-code-2nj8</link>
      <guid>https://dev.to/rodrigo_sidneycolquequi/under-the-hood-how-bandit-sast-analyzes-your-python-code-2nj8</guid>
      <description>&lt;p&gt;&lt;strong&gt;Abstract&lt;/strong&gt;&lt;br&gt;
While many developers use security scanners, few understand how they actually "read" code. This article explains the inner workings of Bandit, focusing on its use of the Abstract Syntax Tree (AST) to identify security patterns without ever executing a single line of code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Core Engine: AST (Abstract Syntax Tree)&lt;/strong&gt;&lt;br&gt;
Unlike a simple text search (which might give many false positives), Bandit doesn't just look for words like "password". It converts your Python code into an AST.&lt;/p&gt;

&lt;p&gt;What is AST? It is a tree representation of the abstract syntactic structure of your source code.&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.amazonaws.com%2Fuploads%2Farticles%2Fwpj1r887lhou8uu14hpw.png" 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.amazonaws.com%2Fuploads%2Farticles%2Fwpj1r887lhou8uu14hpw.png" alt=" " width="800" height="903"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why it matters: By building a tree, Bandit understands the context. It knows if a string is just a comment or if it's actually being assigned to a sensitive variable or passed to a dangerous function like eval().&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. How the "Scanning" Happens&lt;/strong&gt;&lt;br&gt;
Bandit works through a set of plugins. Each plugin is designed to look for a specific type of vulnerability:&lt;br&gt;
Blacklist Plugins: These look for the use of insecure modules (like pickle or telnetlib).&lt;br&gt;
Function Call Plugins: These trigger when they see dangerous calls (like subprocess.shell=True).&lt;br&gt;
Hardcoded Secret Plugins: These use heuristics to identify strings that look like passwords or API keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Severity and Confidence levels&lt;/strong&gt;&lt;br&gt;
One of Bandit's best features is its scoring system:&lt;br&gt;
Severity: How bad is the bug? (Low, Medium, High).&lt;br&gt;
Confidence: How sure is Bandit that this is actually a bug and not a mistake?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use it in the CI/CD?&lt;/strong&gt;&lt;br&gt;
The main advantage is speed. Because it doesn't need to compile or run the code, it can scan thousands of lines in seconds. Integrating it into GitHub Actions (as shown in my previous post) ensures that no "illegal" AST patterns make it into the main branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
Bandit isn't just a linter; it's a security-focused parser. By understanding the structure of Python, it provides a robust first line of defense for any backend developer.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>python</category>
      <category>security</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Implementing SAST in Your Infrastructure: Detecting Vulnerabilities with Checkov and GitHub Actions</title>
      <dc:creator>RODRIGO SIDNEY COLQUE QUISPE</dc:creator>
      <pubDate>Tue, 21 Apr 2026 03:50:44 +0000</pubDate>
      <link>https://dev.to/rodrigo_sidneycolquequi/implementing-sast-in-your-infrastructure-detecting-vulnerabilities-with-checkov-and-github-actions-3b5h</link>
      <guid>https://dev.to/rodrigo_sidneycolquequi/implementing-sast-in-your-infrastructure-detecting-vulnerabilities-with-checkov-and-github-actions-3b5h</guid>
      <description>&lt;p&gt;&lt;strong&gt;Abstract&lt;/strong&gt;&lt;br&gt;
This article explores the implementation of Static Application Security Testing (SAST) for Infrastructure as Code (IaC) using Checkov. We demonstrate how to identify common security misconfigurations, such as publicly accessible S3 buckets, and seamlessly integrate the scanning process into a CI/CD pipeline using GitHub Actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Checkov?&lt;/strong&gt;&lt;br&gt;
Checkov is an open-source static analysis tool (maintained by Bridgecrew / Prisma Cloud) designed specifically for Infrastructure as Code.&lt;/p&gt;

&lt;p&gt;Unlike tools aimed at application code (like Bandit for Python), Checkov scans configuration files for misconfigurations that could lead to security vulnerabilities or compliance issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use Checkov?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Built-in policies:&lt;/strong&gt; It comes with hundreds of out-of-the-box policies covering AWS, Azure, and GCP best practices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-framework:&lt;/strong&gt; Supports Terraform, CloudFormation, Kubernetes, Dockerfiles, Serverless, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy integration:&lt;/strong&gt; Runs from the command line or directly in your CI/CD pipelines.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Scenario: Vulnerable Infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you have a Terraform file (main.tf) where you define an S3 bucket. By mistake (or for a quick test), you configure it to have public read access:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# main.tf&lt;br&gt;
resource "aws_s3_bucket" "my_vulnerable_bucket" {&lt;br&gt;
  bucket = "my-dev-test-bucket"&lt;br&gt;
  acl    = "public-read" # ❌ Security risk!&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If we deploy this, anyone on the internet could access our data. Let's make our repository catch this error before it gets merged into the main branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automating with GitHub Actions&lt;/strong&gt;&lt;br&gt;
The real magic happens when we integrate Checkov into our CI/CD workflow. This way, every time someone pushes code or opens a Pull Request, Checkov will analyze the infrastructure automatically.&lt;/p&gt;

&lt;p&gt;In your repository, create a file at &lt;code&gt;.github/workflows/checkov.yml&lt;/code&gt; and add the following:&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.amazonaws.com%2Fuploads%2Farticles%2F8qp1evxaxaduixsu81s4.png" 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.amazonaws.com%2Fuploads%2Farticles%2F8qp1evxaxaduixsu81s4.png" alt=" " width="540" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What exactly does this workflow do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Checkout:&lt;/strong&gt; Clones your code into the GitHub Actions virtual environment.&lt;br&gt;
&lt;strong&gt;2. Run Checkov:&lt;/strong&gt; Uses the official Bridgecrew action. The directory: . parameter tells it to look for infrastructure files throughout the repository.&lt;br&gt;
&lt;strong&gt;3. soft_fail:&lt;/strong&gt; false: This is the key to DevSecOps. If Checkov finds a failing policy (like our public bucket), the pipeline will fail, preventing vulnerable code from being integrated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
Shifting security to the left (Shift-Left Security) by implementing SAST in your Infrastructure as Code is no longer optional. With tools like Checkov and GitHub Actions, it's a fast and highly effective process. With just a few lines of code, you can ensure your team doesn't accidentally deploy insecure configurations.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>github</category>
      <category>security</category>
    </item>
  </channel>
</rss>
