<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ guide - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ guide - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 23 Jun 2026 22:45:00 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/guide/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ A Beginner Developer's Guide to Scrum ]]>
                </title>
                <description>
                    <![CDATA[ Let me guess: you’re learning to code…alone. You’ve been grinding through tutorials. You've built a portfolio site, maybe deployed a few projects on GitHub. And now you're trying to land a job or join a team. Then the interviews start. Suddenly, peop... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-beginner-developers-guide-to-scrum/</link>
                <guid isPermaLink="false">68813c7579e092b166d373b6</guid>
                
                    <category>
                        <![CDATA[ Scrum ]]>
                    </category>
                
                    <category>
                        <![CDATA[ agile development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ project management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer ]]>
                    </category>
                
                    <category>
                        <![CDATA[ interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ guide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ education ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Product Management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Data Science ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Career ]]>
                    </category>
                
                    <category>
                        <![CDATA[ workflow ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Aditya Vikram Kashyap ]]>
                </dc:creator>
                <pubDate>Wed, 23 Jul 2025 19:48:05 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753300058064/7046dd6c-1d9e-4f06-9ca1-65b3bb7eec83.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let me guess: you’re learning to code…alone.</p>
<p>You’ve been grinding through tutorials. You've built a portfolio site, maybe deployed a few projects on GitHub. And now you're trying to land a job or join a team.</p>
<p>Then the interviews start.</p>
<p>Suddenly, people ask:</p>
<ul>
<li><p>"Are you familiar with Agile?"</p>
</li>
<li><p>"Have you worked in a Scrum environment?"</p>
</li>
<li><p>"What’s your experience with sprints?"</p>
</li>
</ul>
<p>Cue the imposter syndrome. Because no one teaches this stuff in JavaScript 101.</p>
<p>This guide is for you.</p>
<p>I’ll help make the Scrum process – a very common way developers work together – <em>make actual sense</em>. I’ll walk you through the basics, but also tell you what developers actually <em>do</em>, how standups feel when you're new, and what’s expected of you when you’re no longer coding in a vacuum.</p>
<p>Let’s break it down.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-even-is-scrum">What Even Is Scrum?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-three-roles-in-scrum-and-who-does-what">The Three Roles in Scrum (and Who Does What)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-scrum-rhythm-what-a-sprint-actually-looks-like">The Scrum Rhythm: What a Sprint Actually Looks Like</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-who-attends-the-ceremonies">Who attends the Ceremonies:</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-standups-where-you-talk-like-a-human-not-a-robot">Standups: Where You Talk Like a Human, Not a Robot</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sprint-planning">Sprint Planning</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-whats-a-user-story-and-why-does-it-sound-like-a-childrens-book">What’s a User Story and Why Does It Sound Like a Children’s Book?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-counts-as-done-definition-of-done-and-why-its-important">What Counts as “Done”? Definition of Done and Why It’s Important</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-demos-retros-and-saying-the-hard-things">Demos, Retros, and Saying the Hard Things</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tools-you-might-encounter">Tools You Might Encounter</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-if-youre-preparing-for-a-job-heres-what-you-can-do">If You’re Preparing for a Job, Here’s What You Can Do</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-what-even-is-scrum"><strong>What Even Is Scrum?</strong></h2>
<p>Scrum is not a tool. It’s not a software. It’s not some elite thing only PMs care about.</p>
<p>It’s a lightweight framework that helps software teams build things incrementally, together, in short focused cycles called sprints.</p>
<p>Scrum is used by everyone from FAANG teams to indie dev shops because it helps:</p>
<ul>
<li><p>Keep teams aligned</p>
</li>
<li><p>Deliver working software fast</p>
</li>
<li><p>Course-correct often</p>
</li>
<li><p>Spot problems early (before they go nuclear)</p>
</li>
</ul>
<p>It’s the opposite of the old-school “build for a year and pray it works” model.</p>
<h2 id="heading-the-three-roles-in-scrum-and-who-does-what"><strong>The Three Roles in Scrum (and Who Does What)</strong></h2>
<p>Scrum officially defines three roles. Here's what that means in practice:</p>
<h3 id="heading-1-product-owner-po"><strong>1. Product Owner (PO)</strong></h3>
<p>Think: Vision-holder. They decide <em>what</em> the team builds and <em>why</em>. A product owner:</p>
<ul>
<li><p>Writes user stories (think of these as feature requests written from a user’s point of view)</p>
</li>
<li><p>Prioritizes the work</p>
</li>
<li><p>Clarifies what success looks like</p>
</li>
<li><p>Says “yes” or “not yet” to features</p>
</li>
</ul>
<h3 id="heading-2-scrum-master-sm"><strong>2. Scrum Master (SM)</strong></h3>
<p>Think: Air-traffic controller meets therapist. They make sure the process works. The are master Facilitators, like between Dev and PO’s. A Scrum Master:</p>
<ul>
<li><p>Facilitates meetings</p>
</li>
<li><p>Removes blockers (“Your AWS access is stuck? I’ll escalate it.”)</p>
</li>
<li><p>Coaches the team on Scrum practices</p>
</li>
<li><p>Doesn’t manage people – manages <em>flow</em></p>
</li>
</ul>
<h3 id="heading-3-developers-you"><strong>3. Developers (YOU!)</strong></h3>
<p>Think: Builders. You write code, test it, ship it, fix it, and improve it. You also:</p>
<ul>
<li><p>Break down stories into tasks</p>
</li>
<li><p>Pick up work from the team board (like Jira or Trello)</p>
</li>
<li><p>Communicate progress</p>
</li>
<li><p>Demo what you’ve built at the end of the sprint</p>
</li>
</ul>
<p>You might also work with designers, testers, or DevOps folks – but within Scrum, you’re all “developers” building a product together.</p>
<h2 id="heading-the-scrum-rhythm-what-a-sprint-actually-looks-like"><strong>The Scrum Rhythm: What a Sprint Actually Looks Like</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752809790048/253fd92b-1ebe-4f3e-bfbc-48719676dc82.png" alt="253fd92b-1ebe-4f3e-bfbc-48719676dc82" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Image Source: <a target="_blank" href="https://www.invensislearning.com/blog/what-are-scrum-ceremonies/">https://www.invensislearning.com/blog/what-are-scrum-ceremonies/</a></p>
<h3 id="heading-understanding-the-scrum-cycle"><strong>Understanding the Scrum Cycle</strong></h3>
<p>So, what does it <em>actually</em> look like when a team uses Scrum to build software?</p>
<p>Let’s walk through a full sprint – not just the buzzwords, but what really happens when a group of humans tries to plan, build, review, and improve together. Think of this as your backstage pass to the rhythm of modern teamwork.</p>
<h3 id="heading-step-1-build-and-refine-the-product-backlog">📦 Step 1: Build and Refine the Product Backlog</h3>
<p>Before any coding starts, the team needs to agree on <em>what</em> they might build – not just this week, but in the near future too.</p>
<p>That’s where the <strong>Product Backlog</strong> comes in. This is a big, running list of everything the product might need – features, bug fixes, improvements, ideas, and maybe a few wild dreams. It’s like the wishlist for the product, but more organized (ideally).</p>
<p>The Product Owner is responsible for maintaining and prioritizing this list. They decide what’s most important to work on based on customer needs, business goals, and feedback.</p>
<p>But the PO doesn’t do this in isolation. Enter the <strong>Backlog Refinement meeting</strong>.</p>
<p>In these sessions, the Scrum Team – that’s the PO, the Scrum Master (SM), and the Developers – come together to:</p>
<ul>
<li><p><strong>Review</strong> the most important upcoming items</p>
</li>
<li><p><strong>Clarify</strong> any vague or confusing parts of each task</p>
</li>
<li><p><strong>Break big items</strong> down into smaller, buildable pieces called <strong>user stories</strong></p>
</li>
<li><p><strong>Estimate effort</strong> (how much time or complexity is involved for each story)</p>
</li>
</ul>
<p>This meeting makes sure the team isn’t caught off guard in the sprint – that they understand the work ahead and can actually start sprinting when the time comes.</p>
<h3 id="heading-step-2-sprint-planning-what-are-we-building-this-time">🧭 Step 2: Sprint Planning – What Are We Building This Time?</h3>
<p>Now that we’ve got a solid backlog, it’s time to pick what to build <em>right now</em>.</p>
<p>At the start of each sprint (which typically lasts 1 to 4 weeks), the team holds a <strong>Sprint Planning meeting</strong>. This meeting sets the stage for the entire sprint – it’s like the huddle before the big game.</p>
<p>In Sprint Planning, the team:</p>
<ul>
<li><p>Reviews the top items from the backlog</p>
</li>
<li><p>Discusses what can realistically be completed based on their availability and capacity</p>
</li>
<li><p>Chooses a handful of these stories to commit to</p>
</li>
<li><p><strong>Defines a Sprint Goal</strong> – a simple statement that captures the purpose of this sprint</p>
</li>
</ul>
<p>For example, the Sprint Goal might be:<br>🎯 <em>“Allow users to reset their passwords.”</em></p>
<p>Every user story chosen should contribute to that goal. The collection of these stories becomes the <strong>Sprint Backlog</strong> – basically, the to-do list for the sprint.</p>
<p>So when we say:</p>
<p>“The team selects an ordered list of user stories to comprise the Sprint Backlog for the next sprint, which will be achievable to satisfy the Sprint Goal...”</p>
<p>We’re really just saying:<br>👉 <em>“Pick a realistic number of important tasks that, if completed, will help us hit our target for the sprint.”</em></p>
<p>Not too vague. Not too ambitious. Just achievable and focused.</p>
<h3 id="heading-step-3-daily-standups-stay-in-sync">☀️ Step 3: Daily Standups – Stay in Sync</h3>
<p>Now the sprint is underway! But how does everyone stay aligned and avoid working in silos?</p>
<p>That’s where the <strong>Daily Standup</strong> comes in. Every day – usually in the morning – the team has a quick check-in (about 15 minutes) where each person answers three questions:</p>
<ol>
<li><p><strong>What did I do yesterday?</strong></p>
</li>
<li><p><strong>What am I working on today?</strong></p>
</li>
<li><p><strong>Is anything blocking me?</strong> (that is, am I stuck?)</p>
</li>
</ol>
<p>Example:</p>
<p>“Yesterday I set up the login API integration. Today I’ll work on the UI validation. I’m blocked on getting access to the staging database — may need help.”</p>
<p>These standups keep the team in sync and surface blockers early so they can be addressed quickly. They’re not about micromanaging or showing off. They’re about visibility and support.</p>
<h3 id="heading-whats-a-sprint-burndown-chart">📉 What’s a Sprint Burndown Chart?</h3>
<p>You might hear your team mention a “burndown chart.” No, this isn’t about things going down in flames (hopefully).</p>
<p>A <strong>Sprint Burndown Chart</strong> is a graph that shows how much work is left in the sprint – day by day.</p>
<ul>
<li><p>The <strong>y-axis</strong> is the amount of work remaining (often measured in story points or tasks)</p>
</li>
<li><p>The <strong>x-axis</strong> is the number of days left in the sprint</p>
</li>
</ul>
<p>The line should ideally trend downward as work gets completed – hence “burning down.” If it flattens out or slopes up, that’s a red flag that the team might be stuck, behind schedule, or not updating the board.</p>
<p>Think of it as a visual heartbeat of the sprint. You can learn more via a practical example <a target="_blank" href="https://youtu.be/2K84aZn9AY8?si=tS8oMGxVD0CYtnlw">in this video</a>.</p>
<h3 id="heading-step-4-sprint-review-show-what-youve-built">🖥️ Step 4: Sprint Review – Show What You’ve Built</h3>
<p>At the end of the sprint, the team holds a <strong>Sprint Review</strong> (also called a “demo”). This is where you show what was actually built during the sprint.</p>
<ul>
<li><p>The <strong>Developers</strong> demo working features – live, not just screenshots</p>
</li>
<li><p>The <strong>Product Owner</strong> reviews whether the Sprint Goal was achieved</p>
</li>
<li><p>Stakeholders may ask questions, give feedback, or suggest tweaks</p>
</li>
</ul>
<p>This meeting isn’t just for show – it’s a feedback loop. It helps the team validate that what they built is useful, usable, and meets expectations. If changes are needed, those get added to the backlog for future sprints.</p>
<h3 id="heading-step-5-sprint-retrospective-look-back-to-move-forward">🔍 Step 5: Sprint Retrospective – Look Back to Move Forward</h3>
<p>Once the review is done, the team shifts focus from <em>what</em> they built to <em>how</em> they worked together.</p>
<p>Enter the <strong>Sprint Retrospective</strong> – a meeting to reflect on the process, not the product.</p>
<p>The team discusses:</p>
<ul>
<li><p>✅ What went well</p>
</li>
<li><p>❌ What didn’t go so well</p>
</li>
<li><p>🔁 What could be improved next time</p>
</li>
</ul>
<p>This isn’t about pointing fingers. It’s about learning, adapting, and continuously improving how the team collaborates.</p>
<p>The <strong>Scrum Master</strong> often facilitates this meeting and helps turn feedback into action items for the next sprint. For example:</p>
<p>“We underestimated testing time. Next sprint, let’s budget for QA earlier.”</p>
<p>The best teams take retros seriously. Why? Because even if your code is perfect, your <em>process</em> needs tuning too – and small process changes often lead to big gains.</p>
<h3 id="heading-scrum-is-a-loop">♻️ Scrum Is a Loop</h3>
<p>Here’s the rhythm:</p>
<ol>
<li><p>Plan the sprint</p>
</li>
<li><p>Check in daily</p>
</li>
<li><p>Build and demo the product</p>
</li>
<li><p>Reflect and improve</p>
</li>
</ol>
<p>Then do it all over again – with slightly better coordination and slightly more trust each time.</p>
<p>It’s not about being fast. It’s about being intentional, consistent, and collaborative.</p>
<h3 id="heading-example-sprint">Example Sprint</h3>
<p>Let’s say, for example, that your team does 4-week sprints. (Keep in mind that Sprints can differ by team, nature of product, release cycles, and so on.)</p>
<p>Here’s the rough beat:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Week</strong></td><td><strong>What Happens (Sprint Ceremonies)</strong></td><td><strong>Your Role</strong></td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td><strong>Sprint Planning</strong></td><td>Help estimate effort, pick what to build</td></tr>
<tr>
<td>1-4</td><td><strong>Daily Stand ups</strong> (15 mins)</td><td>Share what you’re doing &amp; any blockers</td></tr>
<tr>
<td>1-3</td><td><strong>Development Time</strong></td><td>Code, test, commit, fix, push, repeat</td></tr>
<tr>
<td>3.5-4</td><td><strong>Sprint Review</strong></td><td>Demo what you built</td></tr>
<tr>
<td>4</td><td><strong>Sprint Retrospective</strong></td><td>Reflect on how the sprint went as a team</td></tr>
</tbody>
</table>
</div><p>Scrum works in <strong>loops</strong>. Every 2-4 weeks (depending on your cadence and sprint cycle), your team should have working, demo-able software to show for it – even if it’s small.</p>
<p>And no, it’s not about “speed.” It’s about consistency, communication, and collaboration.</p>
<h2 id="heading-who-attends-the-ceremonies"><strong>Who attends the Ceremonies:</strong></h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Ceremony</strong></td><td><strong>Who Attends</strong></td><td><strong>Why They’re There</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Sprint Planning</strong></td><td>Product Owner (PO), Scrum Master (SM), Development Team</td><td>To define what will be delivered and how the work will be accomplished</td></tr>
<tr>
<td><strong>Daily Standup</strong></td><td>Development Team, Scrum Master (optional), PO (optional)</td><td>To sync on progress, share blockers, and coordinate efforts</td></tr>
<tr>
<td><strong>Sprint Review</strong></td><td>Development Team, Scrum Master, Product Owner, Stakeholders</td><td>To demo the work, get feedback, and assess if goals were met</td></tr>
<tr>
<td><strong>Sprint Retrospective</strong></td><td>Development Team, Scrum Master, Product Owner (optional)</td><td>To reflect on the process, identify what worked/what didn’t, and improve the next sprint</td></tr>
<tr>
<td><strong>Backlog Refinement</strong></td><td>Product Owner, Development Team, Scrum Master (optional)</td><td>To clarify upcoming stories, estimate work, and prepare for future sprint planning</td></tr>
</tbody>
</table>
</div><p>Now lets dive deeper and understand practically how each of these ceremonies work:</p>
<h2 id="heading-standups-where-you-talk-like-a-human-not-a-robot"><strong>Standups: Where You Talk Like a Human, Not a Robot</strong></h2>
<p>So how does the team actually stay connected day to day? That’s where standups come in.</p>
<p>Every morning, your team meets briefly – usually on Zoom or in a circle – and you answer 3 questions:</p>
<ol>
<li><p>What did I work on yesterday?</p>
</li>
<li><p>What will I work on today?</p>
</li>
<li><p>What’s blocking me? Any impediments?</p>
</li>
</ol>
<p>Example:</p>
<p>"Yesterday I cleaned up the signup validation logic. Today I’m working on the email verification flow. I’m stuck on SendGrid config – might need help setting up credentials."</p>
<p>It’s not about impressing anyone. It’s about keeping everyone in sync. Some days you’ll say, “I spent the whole day debugging a CSS bug that turned out to be a semicolon.” That’s okay.</p>
<p>How does it work?</p>
<p>The Scrum Master gathers everyone in a huddle room, the PO and Dev Team included, and opens the the Standup. They are the facilitator of the ceremony. Everyone gets a chance to answer the 3 questions above (usually about 2-5 minutes each). It’s not a full report – it’s quick. When one person is done, they pass it on to someone else.</p>
<p>This ensures there is team cohesion and transparency.</p>
<p><a target="_blank" href="https://youtu.be/q_R9wQY4G5I?si=W1AcvcLXB-mnUM1f">Here is a video example of a standup</a>.</p>
<h2 id="heading-sprint-planning"><strong>Sprint Planning</strong></h2>
<p>The goal of the planning meeting is to answer the questions “What are we going to work on, and how are we going to do it?” It is critical for the team to have a shared goal and a shared commitment to this goal before beginning this ceremony.</p>
<p>Participants should:</p>
<ul>
<li><p>Measure growth</p>
</li>
<li><p>Sync with the Scrum Master</p>
</li>
<li><p>Sync with the Product Owner</p>
</li>
</ul>
<p>Sprint planning happens just before the sprint starts, and usually lasts for an hour or two. In this meeting, the team goes over a collection of <strong>user stories</strong> and discuss, plan, measure, and prioritize. This is where they decide what is going to be in scope for their upcoming sprint cycle.</p>
<p>The Product Owner will have a prioritized view of things in the backlog. They work with the team on each object or customer experience. Together, as a group they go through and make calculations, deciding to what they can commit.</p>
<h2 id="heading-whats-a-user-story-and-why-does-it-sound-like-a-childrens-book"><strong>What’s a User Story and Why Does It Sound Like a Children’s Book?</strong></h2>
<p>So you might be wondering: how do you know what to work on? What to build? So much work, so little time? Thats where <strong>user stories</strong> come in.</p>
<p>In Scrum, teams don’t just write vague tasks like “code the login.” Instead, they write user stories – short, human-centered feature descriptions that describe what the user needs, why they need it, and what success looks like.</p>
<p>Here’s an example:</p>
<p><em>As a user, I want to be able to reset my password, so I can access my account if I forget it.</em></p>
<p>User stories are the scaffolding of teamwork. They’re written with empathy, not just efficiency. And each one comes with <strong>acceptance criteria</strong> – a checklist that clarifies what “done” actually means:</p>
<ul>
<li><p>A “Forgot Password” link is visible</p>
</li>
<li><p>Clicking it shows a form</p>
</li>
<li><p>An email gets sent with a reset link</p>
</li>
</ul>
<p>Once a story is agreed upon, developers break it down into tasks, like “build form,” “hook into backend,” or “handle email validation.” It’s collaborative, not prescriptive. And user stories have priority so you know what’s the most important and what’s the least.</p>
<p>A helpful rule of thumb many teams use is the <a target="_blank" href="https://medium.com/@nic/writing-user-stories-with-gherkin-dda63461b1d2"><strong>Gherkin</strong>-style "Given–When–Then"</a> format:</p>
<ul>
<li><p><strong>Given</strong> some initial context</p>
</li>
<li><p><strong>When</strong> an event occurs</p>
</li>
<li><p><strong>Then</strong> a specific outcome should happen</p>
</li>
</ul>
<p>This ensures that everyone – devs, testers, and product owners – shares the same understanding of behavior and expectations.</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=7hoGqhb6qAs">Here is a great video example</a> thats outlines how to draft effective and powerful user stories.</p>
<h2 id="heading-what-counts-as-done-definition-of-done-and-why-its-important"><strong>What Counts as “Done”? Definition of Done and Why It’s Important</strong></h2>
<p>Now you might be wondering – how do I know when a task is done and can be closed out?</p>
<p>The <strong>Definition of Done</strong> is a type of documentation in the form of a <strong>team agreement</strong>. The Definition of Done identifies the conditions that need to be achieved in order for the product to be considered done (as in <strong>potentially shippable</strong>).</p>
<p>This is how we know that we "did the thing right". Meaning, we built the correct level of quality into the product. The Definition of Done is not the same as the acceptance criteria, which are written by the product owner to help us know we did the "right thing".</p>
<p>Every team has a Definition of Done – it’s not just “I pushed code.” It could mean:</p>
<ul>
<li><p>Code is written</p>
</li>
<li><p>Reviewed by a peer</p>
</li>
<li><p>Merged into main</p>
</li>
<li><p>Tested on staging</p>
</li>
<li><p>Possibly deployed</p>
</li>
</ul>
<p>This clarity keeps teams honest and accountable. No “it works on my machine” energy here. The DoD sets a quality bar. It prevents ambiguity, rework, and “it works on my machine” moments. When every card on the board passes the same finish line, teams move faster – and trust each other more.</p>
<p>Everyone should know what done is in a team. Either its Done as per DoD standards or its not.</p>
<p><a target="_blank" href="https://youtu.be/pYOJyQoBT3U?si=nVygkQQx79NaAOo4">Here is a beautiful video</a> highlighting the impotence of DoD.</p>
<h2 id="heading-demos-retros-and-saying-the-hard-things"><strong>Demos, Retros, and Saying the Hard Things</strong></h2>
<p>Once you’ve built the product, then comes demos (showcasing your work) and retros (analysis as a team on what when well and what areas to improve on).</p>
<p>In the retro, everyone’s encouraged to speak up:</p>
<ul>
<li><p>What went well?</p>
</li>
<li><p>What didn’t?</p>
</li>
<li><p>What should we try next time?</p>
</li>
</ul>
<p>Example:</p>
<p>“We missed a lot of stories because we didn’t account for testing time. Maybe we buffer next sprint with fewer tasks.”</p>
<p>The goal is not to blame – it’s to <em>improve</em>. Over time, this feedback loop becomes gold. The Scrum Master usually facilitates, collects feedback (via tools like Parabol, Miro, or sticky notes), and helps turn insights into actionable experiments for the next sprint.</p>
<p>Over time, retros become the heartbeat of team evolution.</p>
<p><a target="_blank" href="https://youtu.be/5eu1HotNmWs?si=1DZaSmztB6rHyawj">Here is a video</a> highlighting the importance of a Retro and Sprint Review.</p>
<h3 id="heading-why-retrospection-matters-more-than-you-think">🧠 Why Retrospection Matters More Than You Think</h3>
<p>The Sprint Retrospective is more than just another meeting. It’s a mirror for your team – a safe, structured space to pause, reflect, and improve together.</p>
<p>You discuss:</p>
<p>✅ what went well</p>
<p>❌ what did not go well</p>
<p>🔁 what could we do better next time</p>
<p>Great teams don't just deliver great software, they continually deliver better ways of working.</p>
<p>This is why many experienced Scrum practitioners consider the retro to be the most important event in Scrum. Code is deployed once, but process improvements grow exponentially, sprint after sprint.</p>
<h2 id="heading-tools-you-might-encounter"><strong>Tools You Might Encounter</strong></h2>
<p>Scrum doesn’t require software, but real-world teams use a variety of tools:</p>
<ul>
<li><p><strong>Jira</strong> – Tracks sprints, issues, velocity</p>
</li>
<li><p><strong>Trello</strong> – Simple board, good for small teams</p>
</li>
<li><p><strong>Slack</strong> – Where standups often happen if async</p>
</li>
<li><p><strong>Notion / Confluence</strong> – Docs, retros, notes</p>
</li>
<li><p><strong>GitHub Projects</strong> – Lightweight planning for devs</p>
</li>
</ul>
<p>Don’t worry if you’re not fluent in these yet. They’re tools – you’ll learn them on the job.</p>
<h2 id="heading-if-youre-preparing-for-a-job-heres-what-you-can-do"><strong>If You’re Preparing for a Job, Here’s What You Can Do</strong></h2>
<ul>
<li><p>✍️ Practice writing user stories from your side projects</p>
</li>
<li><p>🧪 Run a mini-sprint: Plan your weekend project, set goals, and “review” it at the end</p>
</li>
<li><p>🤝 Contribute to an open-source project that uses Scrum or Agile workflows</p>
</li>
<li><p>🧾 Write about what you learned – maybe as a tutorial (<em>hint hint</em>)</p>
</li>
</ul>
<h2 id="heading-final-thoughts"><strong>Final Thoughts</strong></h2>
<p>So to recap, Scrum is a simple yet powerful way for teams to work together, stay organized, and deliver results quickly. It runs in short cycles called <strong>sprints</strong>, where the team plans what to do, checks in daily, shows their progress at the end, and reflects on how to improve.</p>
<p>The four key ceremonies – <strong>Sprint Planning</strong>, <strong>Daily Scrum</strong>, <strong>Sprint Review</strong>, and <strong>Sprint Retrospective</strong> – help keep everyone aligned and focused. With clear roles and regular feedback, Scrum makes it easier to handle changes, solve problems early, and continuously get better as a team.</p>
<p>But scrum isn’t a magic spell. It’s just a way for humans to build complex things – together – without falling apart.</p>
<p>You don’t need to be a Scrum Master. You don’t need a certification. But if you understand how sprints work, what’s expected of you, and how to show up to meetings with clarity and candor, you’re 10 steps ahead of most.</p>
<p>Scrum helps teams talk, plan, build, and learn. And now? You can too.</p>
<p>If you liked this, please do share. You never know who it might help out.</p>
<p>Until then…keep learning, unlearning, and relearning!!!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use pytest: A Simple Guide to Testing in Python ]]>
                </title>
                <description>
                    <![CDATA[ With the recent advancements in AI, tools like ChatGPT have made the development process faster and more accessible. Developers can now write code and build web apps with some well-articulated prompts and careful code reviews. While this brings an in... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-pytest-a-guide-to-testing-in-python/</link>
                <guid isPermaLink="false">686d82b56332ba136ecc139e</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ pytest ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TDD (Test-driven development) ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unit testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ guide ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Olowo Jude ]]>
                </dc:creator>
                <pubDate>Tue, 08 Jul 2025 20:42:29 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752007334998/e196493e-f3e0-4e63-b6eb-ce66c5481d9c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>With the recent advancements in AI, tools like ChatGPT have made the development process faster and more accessible. Developers can now write code and build web apps with some well-articulated prompts and careful code reviews.</p>
<p>While this brings an increase in productivity, there's a growing downside. AI-generated code is prone to errors, unexpected bugs, or poor integration with the rest of your code.</p>
<p>Because of these risks, it’s more important than ever to establish robust testing practices to make sure your code is high quality and properly functioning. Various testing tools are available to help solve these challenges, and pytest stands out in the Python ecosystem for its simplicity, flexibility, and powerful features.</p>
<p>In this article, we'll explore the following topics:</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-use-pytest">Why Use pytest?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-your-first-tests-with-pytest">How to Write Your First Tests with pytest</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-run-pytest-tests">How to Run pytest Tests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-interpret-pytest-results">How to Interpret pytest Results</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-exceptions-in-pytest">How to Handle Exceptions in pytest</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-advanced-pytest-features">Advanced pytest Features</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-pytest-markers">1. pytest Markers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-pytest-fixtures">2. pytest Fixtures</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-parametrization">3. Parametrization</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-pytest-plugins">4. pytest Plugins</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<p>By the end of this article, you will have a comprehensive knowledge of pytest and be able to use it in your Python development process.</p>
<h2 id="heading-pre-requisites"><strong>Pre-requisites</strong></h2>
<ul>
<li><p>Must have Python installed</p>
</li>
<li><p>An understanding of the Python programming language</p>
</li>
</ul>
<h2 id="heading-why-use-pytest">Why Use pytest?</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751221734601/f5d6093a-37d2-4d49-85f2-c1a41a98ab67.png" alt="An image of pytest logo." class="image--center mx-auto" width="742" height="677" loading="lazy"></p>
<p>pytest is a popular testing framework for Python that makes it easy to write and run tests. Unlike unittest and other Python testing frameworks, pytest’s simple syntax allows developers to write tests directly as functions or within classes. This lets you write clean, readable code without complexities.</p>
<p>pytest also supports popular Python frameworks like Flask, Django, and more. Combined with other rich features, pytest equips you with the tools you need to ship reliable software in today’s AI-driven era.</p>
<p>Key features of pytest that make it a preferred testing tool include:</p>
<ul>
<li><p><strong>Flexibility:</strong> it provides flexibility in test structure by supporting tests for functions, classes, and modules.</p>
</li>
<li><p><strong>Detailed test output:</strong> it provides a detailed and readable test output, making it easy to understand test failures and errors.</p>
</li>
<li><p><strong>Automatic test discovery:</strong> it automatically discovers tests by looking for files that start with "<code>test_</code>" or end with "<code>_test.py</code>". This eliminates the need for manually specifying test files**.**</p>
</li>
<li><p><strong>Parameterization:</strong> it supports parameterized tests, which allow you to run a single test function with multiple sets of inputs.</p>
</li>
<li><p><strong>Fixtures:</strong> it fixtures provide <code>setup</code> and <code>tearDown</code> methods that help prevent code repetition. This enables you to set up baseline conditions for your tests and also delete them after each test.</p>
</li>
<li><p><strong>Plugins and extensions:</strong> it has a rich ecosystem of plugins and extensions that add extra functionalities, such as detailed tests reporting, and integration with other tools and Python frameworks like Django and Flask.</p>
</li>
<li><p><strong>Compatibility:</strong> it is compatible with other testing frameworks like <code>unittest</code> , allowing you to migrate tests from different testing frameworks and run them seamlessly on it.</p>
</li>
</ul>
<h2 id="heading-how-to-write-your-first-tests-with-pytest">How to Write Your First Tests with pytest</h2>
<p>This section will guide you through writing your first set of tests using the pytest framework.</p>
<p>pytest is a Python package, and you’ll need to install it before using it. You can do that with the following command:</p>
<pre><code class="lang-python">pip install pytest
</code></pre>
<p><strong>NOTE:</strong> Following Python's best practices, it’s recommended you install pytest within a virtual environment. <a target="_blank" href="https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/">Here's a guide</a> to help you set it up.</p>
<p>Next, create a Python file where you will write your tests and import pytest into it using:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pytest
</code></pre>
<p>pytest has 2 basic methods of writing tests, which include:</p>
<ul>
<li><p><strong>The function-based method:</strong> This method is straightforward for writing tests because you write the tests in individual functions.</p>
<p>  <strong>Note:</strong> Each function name must be prefixed with the word <code>test_</code> for pytest to discover and run these tests automatically.</p>
<p>  Here’s an example of a function-based test:</p>
<pre><code class="lang-python">  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_addition</span>():</span>
      <span class="hljs-keyword">assert</span> <span class="hljs-number">1</span> + <span class="hljs-number">1</span> == <span class="hljs-number">2</span>
</code></pre>
<p>  <strong>Note:</strong> In the code above, the <code>assert</code> statement used here in pytest is Python’s built-in “<code>assert</code>”. It’s more convenient and doesn’t require the specific methods like <code>assertEqual</code> and <code>assertTrue</code> which are common with unittest. Another advantage of using the <code>assert</code> statement is that it provides more detailed error messages when an assertion fails.</p>
</li>
<li><p><strong>Class-based method:</strong> This method is similar to the way of writing tests in <code>unittest</code>, except that your test class does not inherit any methods. An example is shown below:</p>
<pre><code class="lang-python">  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestMathOperations</span>:</span>
      <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_addition</span>(<span class="hljs-params">self</span>):</span>
          <span class="hljs-keyword">assert</span> <span class="hljs-number">1</span> + <span class="hljs-number">1</span> == <span class="hljs-number">2</span>
</code></pre>
<p>  This method of writing tests in pytest is useful when you want to group related tests together.</p>
</li>
</ul>
<h2 id="heading-how-to-run-pytest-tests">How to Run pytest Tests</h2>
<p>Running pytest differs slightly from the normal convention of running regular Python scripts.</p>
<p>The general method of running pytest tests is by running the <code>pytest</code> command in your terminal. pytest will automatically look for and run all files of the form <code>test_*.py</code> or <code>*_test.py</code> in the current directory and subdirectories. But while this may be a great way to run tests, pytest offers more flexibility beyond this general method of running tests.</p>
<p>Depending on preferences, you may want to run your test files based on the following:</p>
<ol>
<li><p><strong>To run a specific test file</strong>: To run tests in a specific file, use the <code>pytest</code> command followed by the file name. For example: <code>pytest test_example.py</code>.</p>
</li>
<li><p><strong>To run tests in a directory:</strong> Let’s say you have a directory named Tests that contains some test files. To run all the tests in that directory, use the <code>pytest</code> command followed by the directory and a forward slash. For example: <code>pytest Tests/</code>.</p>
</li>
<li><p><strong>To run tests using specific keywords:</strong> To run tests based on a certain keyword, use the command <code>pytest -k "keyword"</code>. Pytest will automatically look for and run function names, class names, or file names matching that keyword in the current directory and subdirectories. But to run tests matching a certain keyword in a specific file, you’d have to specify the file name after the <code>pytest</code> command. For example: <code>pytest test_example.py -k "keyword"</code>.</p>
</li>
<li><p><strong>Run a specific test within a test file:</strong> To run only a specific test inside a test file, use the command <code>pytest test_example.py::test_addition</code>. This will run only the <code>test_addition</code> test function within the <code>test_example.py</code> module.</p>
</li>
<li><p><strong>To run all test methods in a specific class</strong>: To run all the tests within a specific class, use <code>pytest test_example.py::TestClass</code>. This command would run all the test methods inside the <code>TestClass</code> class in the <code>test_example.py</code> module.</p>
</li>
<li><p><strong>To run a specific test method inside a specific class:</strong> To run a specific test inside a specific class, use <code>pytest test_example.py::TestClass::test_addition</code>. This command would run the specific <code>test_addition</code> method within the <code>TestClass</code> class in the <code>test_example.py</code> module.</p>
</li>
</ol>
<h2 id="heading-how-to-interpret-pytest-results">How to Interpret pytest Results</h2>
<p>One major advantage pytest has over other Python testing frameworks is the rich output it provides, which gives very detailed information about the status of your tests.</p>
<p>Let’s use a basic test to understand how to interpret pytest’s output:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pytest

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_addition</span>():</span>
    <span class="hljs-keyword">assert</span> <span class="hljs-number">1</span> + <span class="hljs-number">1</span> == <span class="hljs-number">3</span>
</code></pre>
<p>Run this test, and we get an output similar to the one below:</p>
<pre><code class="lang-python">============================== test session starts ====================================
platform win32 -- Python <span class="hljs-number">3.10</span><span class="hljs-number">.5</span>, pytest<span class="hljs-number">-8.4</span><span class="hljs-number">.1</span>, pluggy<span class="hljs-number">-1.6</span><span class="hljs-number">.0</span>
rootdir: C:\\Users\\hp\\Desktop\\Pytest
collected <span class="hljs-number">1</span> items

                                                                                  [ <span class="hljs-number">50</span>%]
test_example.py F                                                                 [<span class="hljs-number">100</span>%]

===================================== FAILURES =========================================
____________________________________test_addition ______________________________________

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_addition</span>():</span>
&gt;       <span class="hljs-keyword">assert</span> <span class="hljs-number">1</span> + <span class="hljs-number">1</span> == <span class="hljs-number">3</span>
E       <span class="hljs-keyword">assert</span> (<span class="hljs-number">1</span> + <span class="hljs-number">1</span>) == <span class="hljs-number">3</span>

test_example.py:<span class="hljs-number">4</span>: AssertionError
============================== short test summary info =================================
FAILED test_example.py::test_addition - <span class="hljs-keyword">assert</span> (<span class="hljs-number">1</span> + <span class="hljs-number">1</span>) == <span class="hljs-number">3</span>
========================= <span class="hljs-number">1</span> failed, <span class="hljs-number">1</span> passed <span class="hljs-keyword">in</span> <span class="hljs-number">0.13</span>s ==================================
</code></pre>
<p>The above output is divided into several sections. Here’s a breakdown of what each section means:</p>
<ol>
<li><p>Test session information:</p>
<pre><code class="lang-python"> =============================== test session starts ===============================
 platform win32 -- Python <span class="hljs-number">3.10</span><span class="hljs-number">.5</span>, pytest<span class="hljs-number">-8.4</span><span class="hljs-number">.1</span>, pluggy<span class="hljs-number">-1.6</span><span class="hljs-number">.0</span>
 rootdir: C:\\Users\\hp\\Desktop\\TDD pytest
 collected <span class="hljs-number">1</span> item
</code></pre>
<ul>
<li><p>This section displays a summary of the test environment. It begins with a line marker that indicates the beginning of the test session.</p>
</li>
<li><p>Below the marker, pytest displays information about the operating system, along with the installed versions of Python, pytest and pluggy. (Pluggy is a pytest dependency used to manage plugins.)</p>
</li>
<li><p>The next line indicates the root directory where the test is being run.</p>
</li>
<li><p>The last line in this section displays the number of tests found in this directory.</p>
</li>
</ul>
</li>
<li><p>Test status:</p>
<pre><code class="lang-python"> test_example.py F                                                              [<span class="hljs-number">100</span>%]

 ================================== FAILURES =========================================
 ________________________________ test_addition ______________________________________

     <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_addition</span>():</span>
 &gt;       <span class="hljs-keyword">assert</span> <span class="hljs-number">1</span> + <span class="hljs-number">1</span> == <span class="hljs-number">3</span>
 E       <span class="hljs-keyword">assert</span> (<span class="hljs-number">1</span> + <span class="hljs-number">1</span>) == <span class="hljs-number">3</span>

 test_example.py:<span class="hljs-number">4</span>: AssertionError
</code></pre>
<ul>
<li><p>This section displays information about the status of our tests</p>
</li>
<li><p>The first line in this section specifies the test file which is being run, followed by the status (F in this case, which indicates a test failure).</p>
</li>
<li><p>The next set of lines gives specific information about the failed tests. This includes the function where the failure occurred (<code>test_addition</code>), and the exact line of code responsible for the error.</p>
</li>
<li><p>The last line gives a concise summary of this section. It indicates that the error occurred in <code>test_example.py</code> on line <code>4</code> and it was an <code>AssertionError</code>.</p>
</li>
</ul>
</li>
<li><p>Test summary:</p>
<pre><code class="lang-python"> ============================= short test summary info =============================
 FAILED test_example.py::test_addition - <span class="hljs-keyword">assert</span> (<span class="hljs-number">1</span> + <span class="hljs-number">1</span>) == <span class="hljs-number">3</span>
 ================================ <span class="hljs-number">1</span> failed <span class="hljs-keyword">in</span> <span class="hljs-number">0.13</span>s ================================
</code></pre>
<ul>
<li><p>This section provides an overall summary of the test.</p>
</li>
<li><p>It indicates that the failed test occurred in <code>test_example.py</code> file in the <code>test_addition</code> function because of an incorrect assertion <code>(1 + 1) == 3</code> which isn’t true.</p>
</li>
</ul>
</li>
</ol>
<p>Edit the code with the correct assertion <code>assert(1 + 1) == 2</code> and rerun the code. This time, the code passes with a different output.</p>
<pre><code class="lang-python">=============================== test session starts ==================================
platform win32 -- Python <span class="hljs-number">3.10</span><span class="hljs-number">.5</span>, pytest<span class="hljs-number">-8.3</span><span class="hljs-number">.2</span>, pluggy<span class="hljs-number">-1.5</span><span class="hljs-number">.0</span>
rootdir: C:\\Users\\hp\\Desktop\\TDD pytest
collected <span class="hljs-number">1</span> items

test_example.py .                                                               [<span class="hljs-number">100</span>%]

=============================== <span class="hljs-number">1</span> passed <span class="hljs-keyword">in</span> <span class="hljs-number">0.01</span>s =================================
</code></pre>
<h3 id="heading-how-to-handle-exceptions-in-pytest">How to Handle Exceptions in pytest</h3>
<p>Exceptions are unexpected errors that occur while running our tests, and they prevent our code from performing as expected. As a result, pytest offers several built-in mechanisms for handling these exceptions (but we’ll just cover one of them in this article).</p>
<p><code>pytest.raises</code> <strong>Context Manager</strong> is a tool that checks if your code raises specific exceptions. If the specified exception is raised, that test passes, confirming that the expected error occurred. But if the specified exception is not raised, that test fails.</p>
<p><strong>Usage Examples of</strong> <code>pytest.raises</code></p>
<ol>
<li><p><strong>Checking for</strong> <code>ValueError</code>: In Python, a <code>ValueError</code> is raised when a function receives an argument with an incorrect value. In the example below, we can verify that a <code>ValueError</code> is raised when attempting to calculate the square root of a negative number.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> pytest
 <span class="hljs-keyword">import</span> math

 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">calculate_square_root</span>(<span class="hljs-params">value</span>):</span>
     <span class="hljs-keyword">if</span> value &lt; <span class="hljs-number">0</span>:
         <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Cannot calculate the square root of a negative number"</span>)
     <span class="hljs-keyword">return</span> math.sqrt(value)

 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_calculate_square_root</span>():</span>
     <span class="hljs-keyword">with</span> pytest.raises(ValueError):
         calculate_square_root(<span class="hljs-number">-1</span>)
</code></pre>
</li>
<li><p><strong>Checking for</strong> <code>ZeroDivisionError</code>: Dividing a number by zero raises a <code>ZeroDivisionError</code>. In this example, we check that this error is raised when dividing a number by zero.</p>
<pre><code class="lang-jsx"> <span class="hljs-keyword">import</span> pytest

 def divide_numbers(numerator, denominator):
     <span class="hljs-keyword">return</span> numerator / denominator

 def test_divide_numbers():
     <span class="hljs-keyword">with</span> pytest.raises(ZeroDivisionError):
         divide_numbers(<span class="hljs-number">10</span>, <span class="hljs-number">0</span>)
</code></pre>
</li>
<li><p><strong>Checking for</strong> <code>TypeError</code>: A <code>TypeError</code> is raised when an operation is applied to an object of an inappropriate type. Here, we check that this error is raised when adding incompatible data types, such as a string and an integer given in the example.</p>
<pre><code class="lang-jsx"> <span class="hljs-keyword">import</span> pytest

 def add_numbers(a, b):
     <span class="hljs-keyword">return</span> a + b

 def test_add_numbers():
     <span class="hljs-keyword">with</span> pytest.raises(<span class="hljs-built_in">TypeError</span>):
         add_numbers(<span class="hljs-string">"10"</span>, <span class="hljs-number">5</span>)
</code></pre>
</li>
<li><p><strong>Checking for</strong> <code>KeyError</code>: A <code>KeyError</code> is raised when we try to access a dictionary key that doesn’t exist. We can verify and handle this error using the following code:</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> pytest

 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_value</span>(<span class="hljs-params">dictionary, key</span>):</span>
     <span class="hljs-keyword">return</span> dictionary[key]

 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_get_value</span>():</span>
     <span class="hljs-keyword">with</span> pytest.raises(KeyError):
         get_value({<span class="hljs-string">"name"</span>: <span class="hljs-string">"Alice"</span>}, <span class="hljs-string">"age"</span>)
</code></pre>
</li>
</ol>
<h2 id="heading-advanced-pytest-features">Advanced pytest Features</h2>
<p>As a robust testing framework, pytest offers some advanced features that help you manage complex test scenarios. In this section, we will explore some of these advanced features at a beginner-friendly level and demonstrate how you can start applying them in your tests.</p>
<h3 id="heading-1-pytest-markers">1. pytest Markers</h3>
<p>When working with a large codebase, sometimes running every single test can be time-consuming. This is where pytest markers come in handy.</p>
<p>A marker is just like a label that you can attach to a test function to categorise it. Once a test is labelled, you can instruct pytest to run only tests with certain markers. For example, you may label some tests as "slow" if they take longer to execute and run them separately from the faster ones.</p>
<p>One advantage to using Markers is that it allows you to run specific tests based on categories or specific parameters, and also skip tests if certain conditions aren’t met.</p>
<p>pytest comes along with some built-in markers that can be quite useful:</p>
<ol>
<li><p><code>@pytest.mark.skip</code>: This marker allows you to skip a test unconditionally, and can be useful when you know a test will fail due to an external issue or incomplete code.</p>
<p> <strong>Example:</strong></p>
<pre><code class="lang-python"><span class="hljs-meta"> @pytest.mark.skip(reason="Feature not yet implemented")</span>
 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_feature</span>():</span>
     <span class="hljs-keyword">pass</span>
</code></pre>
</li>
<li><p><code>@pytest.mark.skipif</code>: This marker allows you to skip a test conditionally if certain conditions are met.</p>
<p> <strong>Example:</strong></p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> sys

<span class="hljs-meta"> @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")</span>
 <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestClass</span>:</span>
     <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_function</span>(<span class="hljs-params">self</span>):</span>
         <span class="hljs-string">"This test will not run under 'win32' platform"</span>
</code></pre>
</li>
<li><p><code>@pytest.mark.xfail</code>: This marker is attached to tests that are expected to fail, probably due to a bug or incomplete feature. So when pytest runs such tests, it won’t count it as a failure.</p>
<p> <strong>Example:</strong></p>
<pre><code class="lang-python"><span class="hljs-meta"> @pytest.mark.xfail(reason="division by zero not handled yet")</span>
 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_divide_by_zero</span>():</span>
     <span class="hljs-keyword">assert</span> divide(<span class="hljs-number">10</span>, <span class="hljs-number">0</span>) == <span class="hljs-number">0</span>
</code></pre>
<p> <strong>Note:</strong> Detailed information about skipped/failed tests is not shown by default to avoid cluttering the output.</p>
</li>
</ol>
<p>While pytest comes along with some built-in markers, you can also create your own custom marker (but we won’t cover that in this tutorial). Kindly refer to the documentation for more information on <a target="_blank" href="https://docs.pytest.org/en/stable/example/markers.html">working with custom markers</a></p>
<h3 id="heading-2-pytest-fixtures">2. pytest Fixtures</h3>
<p>In pytest, fixtures allow you to create reusable default data that can be shared across multiple tests. By using fixtures, you can reduce code repetition, making your tests cleaner and more maintainable.</p>
<p>In pytest, fixtures are defined with the <code>@pytest.fixture</code> decorator as shown in the example below:</p>
<p>Let’s say we have several tests that rely on a list of user data. Instead of repeating the same data in each test, we can create a fixture to hold this data, and the fixture is passed across the tests that need it.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pytest

<span class="hljs-meta">@pytest.fixture</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">user_data</span>():</span>
    <span class="hljs-keyword">return</span> [
        {<span class="hljs-string">"name"</span>: <span class="hljs-string">"Alice"</span>, <span class="hljs-string">"age"</span>: <span class="hljs-number">30</span>},
        {<span class="hljs-string">"name"</span>: <span class="hljs-string">"Bob"</span>, <span class="hljs-string">"age"</span>: <span class="hljs-number">25</span>},
        {<span class="hljs-string">"name"</span>: <span class="hljs-string">"Charlie"</span>, <span class="hljs-string">"age"</span>: <span class="hljs-number">35</span>}
    ]

<span class="hljs-comment"># Test function to check for a specific user by name and age</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_user_exists</span>(<span class="hljs-params">user_data</span>):</span>
    user = {<span class="hljs-string">"name"</span>: <span class="hljs-string">"Alice"</span>, <span class="hljs-string">"age"</span>: <span class="hljs-number">30</span>}

    <span class="hljs-comment"># Check if the target user is in the list</span>
    <span class="hljs-keyword">assert</span> user <span class="hljs-keyword">in</span> user_data

<span class="hljs-comment"># Test average age of users</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_average_age</span>(<span class="hljs-params">user_data</span>):</span>
    ages = [user[<span class="hljs-string">"age"</span>] <span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> user_data]
    avg_age = sum(ages) / len(ages)
    <span class="hljs-keyword">assert</span> avg_age == <span class="hljs-number">30</span>
</code></pre>
<p><strong>Note:</strong> The <code>@pytest.fixture</code> decorator in the code above marks the <code>user_data</code> function as a fixture in pytest. This fixture provides reusable data that can be shared across multiple test functions, allowing them to share the same setup without repeating code.</p>
<h3 id="heading-3-parametrization">3. Parametrization</h3>
<p>Parametrization is a pytest feature that allows you to run a test function with different sets of data at once.</p>
<p>For example: Let’s say you have a function that calculates the square of a number. To provide enough coverage while testing, you would want to test the function with zero, positive, and negative numbers.</p>
<p>Instead of writing separate test functions for each scenario, you can use parametrization to run a test function with different sets of data at once. This approach is more concise, and reduces code duplication.</p>
<p>To use parametrization in pytest, we use the <code>@pytest.mark.parametrize</code> decorator as shown in the example below:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pytest

<span class="hljs-comment"># Function to calculate the square of a number</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">square_numbers</span>(<span class="hljs-params">num</span>):</span>
    <span class="hljs-keyword">return</span> num * num

<span class="hljs-comment">#Parametrize decorator to test the square function with different inputs</span>
<span class="hljs-meta">@pytest.mark.parametrize("input_value, expected_output", [</span>
    (<span class="hljs-number">2</span>, <span class="hljs-number">4</span>),     
    (<span class="hljs-number">-3</span>, <span class="hljs-number">9</span>),    
    (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)    
])

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_square</span>(<span class="hljs-params">input_value, expected_output</span>):</span>
    <span class="hljs-keyword">assert</span> square_numbers(input_value) == expected_output
</code></pre>
<p>In the example above, the different input values and expected values are listed in the <code>@pytest.mark.parametrize</code> decorator. We’re testing the <code>square_numbers()</code> function with three different input values: <code>2</code>, <code>-3</code>, and <code>0</code>.</p>
<p>For each value, pytest calls the <code>test_square()</code> function and compares the result of <code>square_numbers(input_value)</code> to <code>expected_output</code>.</p>
<p>This approach is more efficient and ensures the function behaves as expected across a variety of cases.</p>
<h3 id="heading-4-pytest-plugins">4. pytest Plugins</h3>
<p>Plugins are an extension mechanism that allows you to add new functionality to pytest or modify its existing behaviour. These plugins work by providing additional features that extend pytest’s capabilities, which can be useful, especially in complex test scenarios.</p>
<p>pytest has a vast ecosystem of plugins, each designed to suit your different testing needs. You can find the full list of available plugins on <a target="_blank" href="https://pypi.org/">PyPI</a> in the <a target="_blank" href="https://docs.pytest.org/en/stable/reference/plugin_list.html#plugin-list">pytest Plugin List</a>.</p>
<p>To use a plugin, simply install it with <code>pip</code>.</p>
<p><strong>For example:</strong></p>
<pre><code class="lang-python">pip install pytest-NAME
pip uninstall pytest-NAME
</code></pre>
<p><strong>Note:</strong> <code>NAME</code> in the code above should be replaced with the name of the plugin you want to install.</p>
<p>After installing a plugin, pytest automatically finds and integrates it. There’s no need for any additional configuration.</p>
<p>In this section, we explored some of pytest's advanced features. By leveraging these features, you can now significantly improve the quality of your tests by ensuring they’re more efficient, scalable, and easier to maintain over time.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, you’ve learned the basics of testing with pytest, from writing and interpreting tests to handling exceptions and using advanced features like fixtures and parametrization.</p>
<p>Whether your code is written manually or generated by AI, learning how to write tests empowers you to detect bugs early, and build more reliable software. Testing acts as a safety net that boosts you confidence during development and ensures your code works as expected.</p>
<p>If you're ready to go a step further, I’ve written an in-depth article on <a target="_blank" href="https://judeolowo.hashnode.dev/test-driven-development-in-python-a-complete-guide-to-unittest">Test Driven Development in Python</a>. It is a powerful approach where writing tests guides your entire coding process.</p>
<p>If you found this helpful, let me know, share it with your network, or give it a like to help others discover it too.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
