<?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[ Software Testing - 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[ Software Testing - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 25 Aug 2026 22:03:32 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/software-testing/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Test Conversational AI: A Practical Guide for QA Engineers ]]>
                </title>
                <description>
                    <![CDATA[ When I first started learning about conversational AI testing, one question kept bothering me: Where is the expected result? Coming from traditional software testing, I was used to a familiar pattern. ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-test-conversational-ai-practical-guide-for-qa-engineers/</link>
                <guid isPermaLink="false">6a8c5708cc6859117f26da4f</guid>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Quality Assurance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ai testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ QA engineering ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ GAYATHRI BOLINENI ]]>
                </dc:creator>
                <pubDate>Mon, 24 Aug 2026 14:36:56 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/61896a64-69bb-46cb-87dd-0076c4aa1b50.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When I first started learning about conversational AI testing, one question kept bothering me: <strong>Where is the expected result?</strong></p>
<p>Coming from traditional software testing, I was used to a familiar pattern.</p>
<p>A requirement tells us what the system should do. We create a test case, provide an input, define an expected result, execute the test, and compare the actual result with what we expected.</p>
<p>For example:</p>
<table>
<thead>
<tr>
<th>Test</th>
<th>Input</th>
<th>Expected Result</th>
</tr>
</thead>
<tbody><tr>
<td>Valid login</td>
<td>Correct username and password</td>
<td>User logs in</td>
</tr>
<tr>
<td>Invalid login</td>
<td>Incorrect password</td>
<td>Error message displayed</td>
</tr>
<tr>
<td>API request</td>
<td>Valid request payload</td>
<td>HTTP 200 with expected response</td>
</tr>
</tbody></table>
<p>Then I started learning conversational AI. Suddenly, the same approach didn't fit quite as neatly.</p>
<p>If I ask an AI agent "How can I reset my password?", it might answer, "You can reset your password using the Forgot Password option on the login page."</p>
<p>Ask the same question again and it might say: "Select Forgot Password from the sign-in screen and follow the instructions sent to your registered email."</p>
<p>The wording is different, but both responses may be perfectly acceptable. So how do we test something when the exact response can change?</p>
<p>That question changed the way I approached conversational AI testing.</p>
<p>In this article, I'll walk through the testing areas I found most important while learning how conversational systems behave, and show how traditional QA techniques can be adapted for AI agents.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-1-start-with-intent-not-exact-wording">1. Start With Intent, Not Exact Wording</a></p>
</li>
<li><p><a href="#heading-2-dont-use-exact-text-matching-for-every-response">2. Don't Use Exact Text Matching for Every Response</a></p>
</li>
<li><p><a href="#heading-3-evaluate-response-quality-across-multiple-dimensions">3. Evaluate Response Quality Across Multiple Dimensions</a></p>
</li>
<li><p><a href="#heading-4-test-the-conversation-not-just-the-response">4. Test the Conversation, Not Just the Response</a></p>
</li>
<li><p><a href="#heading-5-test-whether-the-ai-can-handle-corrections">5. Test Whether the AI Can Handle Corrections</a></p>
</li>
<li><p><a href="#heading-6-test-ambiguity">6. Test Ambiguity</a></p>
</li>
<li><p><a href="#heading-7-test-the-knowledge-behind-the-answer">7. Test the Knowledge Behind the Answer</a></p>
</li>
<li><p><a href="#heading-8-test-for-hallucinations">8. Test for Hallucinations</a></p>
</li>
<li><p><a href="#heading-9-test-fallback-behavior">9. Test Fallback Behavior</a></p>
</li>
<li><p><a href="#heading-10-test-human-escalation">10. Test Human Escalation</a></p>
</li>
<li><p><a href="#heading-11-test-integrations-like-you-would-in-any-other-application">11. Test Integrations Like You Would in Any Other Application</a></p>
</li>
<li><p><a href="#heading-12-build-a-golden-dataset">12. Build a Golden Dataset</a></p>
</li>
<li><p><a href="#heading-13-dont-only-measure-pass-rate">13. Don't Only Measure Pass Rate</a></p>
</li>
<li><p><a href="#heading-14-create-risk-based-conversational-tests">14. Create Risk-Based Conversational Tests</a></p>
</li>
<li><p><a href="#heading-15-a-practical-conversational-ai-test-strategy">15. A Practical Conversational AI Test Strategy</a></p>
</li>
<li><p><a href="#heading-what-traditional-qa-engineers-already-bring-to-ai-testing">What Traditional QA Engineers Already Bring to AI Testing</a></p>
</li>
<li><p><a href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-1-start-with-intent-not-exact-wording">1. Start With Intent, Not Exact Wording</h2>
<p>Consider these three messages:</p>
<ol>
<li><p>"How do I reset my password?"</p>
</li>
<li><p>"I can't access my account."</p>
</li>
<li><p>"Forgot password."</p>
</li>
</ol>
<p>They look different. But depending on the application, they may all represent the same underlying user goal:</p>
<p><strong>PASSWORD_RESET</strong></p>
<p>In conversational AI, the sentence a user types is often called an <strong>utterance</strong>, while the goal behind that message can be represented as an <strong>intent</strong>.</p>
<p>This creates an important testing question: Can the system understand the same intent when users express it differently?</p>
<p>A simple test set could look like this:</p>
<table>
<thead>
<tr>
<th>Utterance</th>
<th>Expected Intent</th>
</tr>
</thead>
<tbody><tr>
<td>I forgot my password</td>
<td>PASSWORD_RESET</td>
</tr>
<tr>
<td>How do I change my password?</td>
<td>PASSWORD_RESET</td>
</tr>
<tr>
<td>Can't get into my account</td>
<td>PASSWORD_RESET</td>
</tr>
<tr>
<td>Help me recover my login</td>
<td>PASSWORD_RESET</td>
</tr>
<tr>
<td>Password isn't working</td>
<td>PASSWORD_RESET</td>
</tr>
</tbody></table>
<p>Let's look at a small example:</p>
<pre><code class="language-python">test_cases = [ 
          { "message": "I forgot my password", 
            "expected_intent": "PASSWORD_RESET", 
          }, 
          { "message": "How do I change my password?", 
            "expected_intent": "PASSWORD_RESET", 
          }, 
          { "message": "Can't get into my account",  
            "expected_intent": "PASSWORD_RESET", 
          }, 
]

for test in test_cases: 
    response = ai_agent.send(test["message"])
    assert response.intent == test["expected_intent"]
</code></pre>
<p>Before running this test, you need to know the expected intent. It usually comes from the application's approved intent definitions or a reviewed test dataset. The automation isn't deciding what the correct intent should be. It's checking whether the AI agent classified the user's message according to the behavior the team already defined:</p>
<p><code>Input: "Can't get into my account"</code></p>
<p><code>Expected intent: PASSWORD_RESET Actual intent: PASSWORD_RESET</code></p>
<p><code>PASS</code></p>
<p>Don't stop with clean sentences.</p>
<p>Real users make spelling mistakes, use abbreviations, provide incomplete information, and sometimes type only a few words.</p>
<p>So I would also test:</p>
<p>"forgot pwd"</p>
<p>"cant login"</p>
<p>"password help"</p>
<p>"locked out"</p>
<p>This is where conversational AI testing starts becoming interesting. We're not testing whether the system recognizes one predefined sentence. We're testing whether it understands variations of a user's goal.</p>
<h2 id="heading-2-dont-use-exact-text-matching-for-every-response">2. Don't Use Exact Text Matching for Every Response</h2>
<p>One of the first habits I had to reconsider was comparing actual and expected responses word for word.</p>
<p>Suppose the expected answer is: "You can reset your password using the Forgot Password link."</p>
<p>But the AI responds: "Select Forgot Password on the login screen to begin resetting your password."</p>
<p>An exact string comparison fails. But from a user's perspective, the response may be completely correct.</p>
<p>Instead of defining the expected result as one sentence, define the <strong>properties a good response must contain</strong>.</p>
<p>For example, the response should:</p>
<ul>
<li><p>explain how to start the password-reset process</p>
</li>
<li><p>provide an actionable next step</p>
</li>
<li><p>not ask the user to reveal their password</p>
</li>
<li><p>avoid inventing account information</p>
</li>
<li><p>stay relevant to password recovery</p>
</li>
</ul>
<p>Now multiple responses can pass without being identical. This was one of the biggest changes for me.</p>
<p>For deterministic applications, expected output is often a value. For conversational AI, the expected result may need to be a <strong>set of evaluation criteria</strong>.</p>
<h2 id="heading-3-evaluate-response-quality-across-multiple-dimensions">3. Evaluate Response Quality Across Multiple Dimensions</h2>
<p>Correctness is important, but it shouldn't be the only thing you evaluate. I find it useful to break response quality into several dimensions.</p>
<ol>
<li><p>Accuracy: is the information correct? If the AI says customers can reset passwords through email when the actual process requires contacting support, the response fails even if it sounds convincing.</p>
</li>
<li><p>Relevance: did the AI answer what the user actually asked? A response can contain accurate information and still be irrelevant.</p>
</li>
<li><p>Completeness: did the response include the information required for the user to move forward?</p>
</li>
<li><p>Clarity: can the user easily understand the answer?</p>
</li>
<li><p>Helpfulness: does the response actually help the user accomplish their goal?</p>
</li>
</ol>
<p>A simple scoring rubric could look like this:</p>
<table>
<thead>
<tr>
<th>Criterion</th>
<th>Score</th>
</tr>
</thead>
<tbody><tr>
<td>Accuracy</td>
<td>0–2</td>
</tr>
<tr>
<td>Relevance</td>
<td>0–2</td>
</tr>
<tr>
<td>Completeness</td>
<td>0–2</td>
</tr>
<tr>
<td>Clarity</td>
<td>0–2</td>
</tr>
<tr>
<td>Helpfulness</td>
<td>0–2</td>
</tr>
<tr>
<td><strong>Total</strong></td>
<td><strong>0–10</strong></td>
</tr>
</tbody></table>
<p>You can then define a threshold appropriate for your application.</p>
<p>The exact scoring system isn't the important part. What's important is making the evaluation criteria explicit instead of relying on: "This answer looks good to me."</p>
<h2 id="heading-4-test-the-conversation-not-just-the-response">4. Test the Conversation, Not Just the Response</h2>
<p>Single-turn testing is useful, but users rarely interact with an AI agent using perfectly isolated questions.</p>
<p>Consider this conversation:</p>
<p><strong>User:</strong> I need to update my address.</p>
<p>The AI explains the process.</p>
<p>Then:</p>
<p><strong>User:</strong> Can I do that online?</p>
<p>What does <strong>"that"</strong> mean? The second message depends entirely on the first.</p>
<p>Now imagine:</p>
<p><strong>User:</strong> I need to update my address.</p>
<p><strong>AI:</strong> Sure. I can help with that.</p>
<p><strong>User:</strong> Actually, before that, can you tell me when my next payment is due?</p>
<p><strong>AI:</strong> Your next payment is due on September 15.</p>
<p><strong>User:</strong> Thanks. Now back to the address.</p>
<p>Can the system return to the original topic?</p>
<p>That's a different type of test.</p>
<p>Your multi-turn test suite should include scenarios such as:</p>
<ul>
<li><p>follow-up questions</p>
</li>
<li><p>references to earlier messages</p>
</li>
<li><p>topic switching</p>
</li>
<li><p>returning to a previous topic</p>
</li>
<li><p>user corrections</p>
</li>
<li><p>incomplete information</p>
</li>
<li><p>ambiguous questions</p>
</li>
<li><p>repeated questions</p>
</li>
</ul>
<p>This changed the unit of testing for me. Sometimes you're testing a response, while other times you're testing the entire conversation.</p>
<p>Here's an example:</p>
<pre><code class="language-python">ai_agent.send("My order number is A10245")
response = ai_agent.send("When will it arrive?")

assert response.order_id == "A10245"
</code></pre>
<p>The second message doesn't contain the order number. This check verifies that the AI agent retained information from the earlier turn instead of treating “When will it arrive?” as an unrelated question.</p>
<h2 id="heading-5-test-whether-the-ai-can-handle-corrections">5. Test Whether the AI Can Handle Corrections</h2>
<p>People change their minds and make mistakes.</p>
<p>For example:</p>
<p><strong>User:</strong> My account number ends in 4567.</p>
<p>Then:</p>
<p><strong>User:</strong> Sorry, I meant 4576.</p>
<p>What happens next?</p>
<p>The system should ideally use the corrected information rather than continuing with the original value.</p>
<p>The same principle applies to other conversational details.</p>
<p>"I'm traveling to Boston."</p>
<p>followed by:</p>
<p>"Actually, make that Chicago."</p>
<p>Or:</p>
<p>"I need the report for June."</p>
<p>followed by:</p>
<p>"Sorry, July."</p>
<p>These are useful tests because they expose whether the agent is genuinely maintaining conversational context or simply accumulating information without understanding which information is current.</p>
<h2 id="heading-6-test-ambiguity">6. Test Ambiguity</h2>
<p>Users don't always provide enough information.</p>
<p>Imagine someone types: "I want to change it."</p>
<p>Change what? Their address? Password? Payment method? Notification preference?</p>
<p>A poor conversational system may guess. A better one may ask: "What would you like to change?"</p>
<p>This gives us another important test category: <strong>clarification behavior.</strong></p>
<p>Create intentionally ambiguous utterances such as:</p>
<ul>
<li><p>"How do I update it?"</p>
</li>
<li><p>"It's not working."</p>
</li>
<li><p>"Can you change that?"</p>
</li>
<li><p>"I need help with my account."</p>
</li>
</ul>
<p>Then evaluate whether the AI recognizes that information is missing, avoids making unsupported assumptions, and asks an appropriate clarification question.</p>
<p>Sometimes the best AI response isn't an answer, it's another question.</p>
<h2 id="heading-7-test-the-knowledge-behind-the-answer">7. Test the Knowledge Behind the Answer</h2>
<p>At first, I focused almost entirely on what the AI said. Then I realized that a poor answer doesn't automatically mean the language model itself is the problem.</p>
<p>The system may be using a knowledge base, retrieval system, documentation, APIs, or other enterprise data sources.</p>
<p>If that information is wrong, incomplete, conflicting, or outdated, the AI may produce a poor response even when the model is behaving as designed.</p>
<p>Suppose the official policy says: Customers have 30 days to return a product. But an outdated knowledge article says that customers have 60 days.</p>
<p>If the AI retrieves the outdated article and confidently answers "60 days," the response is wrong.</p>
<p>But the investigation shouldn't end with: <strong>"The AI hallucinated."</strong> The tester needs to determine where the wrong information came from.</p>
<p>Questions I would investigate include:</p>
<ul>
<li><p>What source did the response use?</p>
</li>
<li><p>Was the source approved?</p>
</li>
<li><p>Is the information current?</p>
</li>
<li><p>Were multiple sources contradictory?</p>
</li>
<li><p>Did retrieval return the correct document?</p>
</li>
<li><p>Did the final response accurately represent the retrieved information?</p>
</li>
</ul>
<p>This becomes particularly important in systems using retrieval-augmented generation, or RAG.</p>
<h2 id="heading-8-test-for-hallucinations">8. Test for Hallucinations</h2>
<p>One of the most important conversational AI tests is surprisingly simple: <strong>Ask about something the system doesn't know.</strong></p>
<p>Suppose an internal support assistant contains documentation for Products A, B, and C. Ask: "What is the cancellation policy for Product Z?"</p>
<p>Product Z doesn't exist.</p>
<p>So what should happen? The worst outcome is for the system to confidently invent a cancellation policy.</p>
<p>Depending on the application, better behavior might be:</p>
<p>"I don't have information about Product Z."</p>
<p>or:</p>
<p>"I couldn't find that information. Would you like me to connect you with support?"</p>
<p>A useful hallucination test suite should include:</p>
<ul>
<li><p>nonexistent products</p>
</li>
<li><p>fake policy names</p>
</li>
<li><p>unsupported features</p>
</li>
<li><p>deliberately incorrect assumptions</p>
</li>
<li><p>questions outside the knowledge domain</p>
</li>
<li><p>requests for information unavailable to the system</p>
</li>
</ul>
<p>You're testing whether the AI knows when <strong>not</strong> to answer.</p>
<h2 id="heading-9-test-fallback-behavior">9. Test Fallback Behavior</h2>
<p>Every conversational system will eventually receive something it doesn't understand. But that isn't necessarily a failure.</p>
<p>The important question is what happens next?</p>
<p>Imagine a scenario like this:</p>
<p><strong>User:</strong> I need help with my ZXP adjustment.</p>
<p>The system doesn't recognize "ZXP."</p>
<p>A poor fallback might repeatedly say: "Sorry, I don't understand." A better fallback might ask: "Could you tell me a little more about what you mean by ZXP adjustment?"</p>
<p>If the system still can't understand the request, it may need to offer another path.</p>
<p>Fallback tests should cover:</p>
<ul>
<li><p>unknown intents</p>
</li>
<li><p>misspellings</p>
</li>
<li><p>incomplete requests</p>
</li>
<li><p>unsupported topics</p>
</li>
<li><p>conflicting requests</p>
</li>
<li><p>repeated misunderstanding</p>
</li>
</ul>
<p>Also test what happens after multiple failures. An AI agent shouldn't trap the user in an endless loop of: "Sorry, I didn't understand that."</p>
<h2 id="heading-10-test-human-escalation">10. Test Human Escalation</h2>
<p>Sometimes the AI agent should recognize that it can no longer handle the conversation reliably. This might happen when the user explicitly asks for a person, the request falls outside the agent's capabilities, or the situation requires human judgment. In those cases, continuing to generate answers may be worse than handing the conversation over</p>
<p>Consider situations involving:</p>
<ul>
<li><p>repeated misunderstanding</p>
</li>
<li><p>unsupported account problems</p>
</li>
<li><p>user requests for a human</p>
</li>
<li><p>sensitive workflows</p>
</li>
<li><p>exceptions the automated process cannot handle</p>
</li>
</ul>
<p>If escalation is part of the product design, test the entire transition.</p>
<p>For example:</p>
<p><strong>User:</strong> I want to speak to someone.</p>
<p>Does the AI recognize the request? Does it transfer the conversation correctly? Does the human agent receive the relevant conversation history? Does the user have to explain everything again? Does the AI continue trying to answer after escalation should have occurred?</p>
<p>A technically successful transfer can still create a poor experience if all the context is lost.</p>
<h2 id="heading-11-test-integrations-like-you-would-in-any-other-application">11. Test Integrations Like You Would in Any Other Application</h2>
<p>Conversational interfaces can make complex systems look simple.</p>
<p>The user sees:</p>
<p>"What's the status of my order?"</p>
<p>But behind that sentence, the agent might:</p>
<ol>
<li><p>identify the user's intent</p>
</li>
<li><p>authenticate the customer</p>
</li>
<li><p>call an order API</p>
</li>
<li><p>retrieve the order</p>
</li>
<li><p>interpret the response</p>
</li>
<li><p>generate a natural-language answer</p>
</li>
</ol>
<p>Traditional testing skills become extremely valuable here.</p>
<p>If the API returns:</p>
<pre><code class="language-json">{
  "order_id": "A10245",
  "status": "SHIPPED"
}
</code></pre>
<p>the AI shouldn't tell the user: "Your order is still processing."</p>
<p>To check these integrations, make sure you test:</p>
<ul>
<li><p>correct API mapping</p>
</li>
<li><p>authentication failures</p>
</li>
<li><p>timeouts</p>
</li>
<li><p>empty responses</p>
</li>
<li><p>malformed responses</p>
</li>
<li><p>unavailable services</p>
</li>
<li><p>incorrect status codes</p>
</li>
<li><p>partial data</p>
</li>
</ul>
<p>AI doesn't eliminate conventional integration testing. It adds another layer on top of it.</p>
<h2 id="heading-12-build-a-golden-dataset">12. Build a Golden Dataset</h2>
<p>Manual exploratory testing is useful when you're learning how an AI behaves, but eventually you need repeatability. This is where a <strong>golden dataset</strong> becomes useful.</p>
<p>A golden dataset is a curated collection of representative test inputs and expected behaviors that can be rerun as the system changes.</p>
<p>For example:</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>User Input</th>
<th>Expected Behavior</th>
</tr>
</thead>
<tbody><tr>
<td>INT-001</td>
<td>Forgot password</td>
<td>Identify password-reset intent</td>
</tr>
<tr>
<td>INT-002</td>
<td>Can't access account</td>
<td>Route to account-access flow</td>
</tr>
<tr>
<td>CTX-001</td>
<td>Can I do that online?</td>
<td>Resolve previous conversational context</td>
</tr>
<tr>
<td>AMB-001</td>
<td>I want to change it</td>
<td>Ask clarification</td>
</tr>
<tr>
<td>HAL-001</td>
<td>Policy for nonexistent Product Z</td>
<td>Don't invent policy</td>
</tr>
<tr>
<td>ESC-001</td>
<td>Let me talk to a person</td>
<td>Initiate escalation</td>
</tr>
<tr>
<td>KB-001</td>
<td>What is the return period?</td>
<td>Answer according to approved knowledge</td>
</tr>
</tbody></table>
<p>Then expand each important intent with paraphrases, edge cases, negative cases, and multi-turn scenarios.</p>
<p>Whenever prompts, knowledge, models, integrations, or conversation logic change, rerun the dataset.</p>
<p>Now you have something much closer to traditional regression testing.</p>
<h2 id="heading-13-dont-only-measure-pass-rate">13. Don't Only Measure Pass Rate</h2>
<p>Suppose you execute 1,000 conversational tests and 950 pass. A 95% pass rate sounds good.</p>
<p>But what failed? Five hundred harmless FAQ questions? Or five critical account-security scenarios?</p>
<p>Aggregate pass rate alone doesn't tell the whole story. Depending on the application, useful metrics might include:</p>
<ul>
<li><p><strong>Intent recognition accuracy:</strong> How often was the user's goal understood correctly?</p>
</li>
<li><p><strong>Fallback rate:</strong> How often did the system fail to understand the user?</p>
</li>
<li><p><strong>Task completion rate:</strong> How often did users successfully accomplish the intended task?</p>
</li>
<li><p><strong>Escalation success rate:</strong> When human help was required, did the transition succeed?</p>
</li>
<li><p><strong>Grounding failures:</strong> How often did responses conflict with approved knowledge?</p>
</li>
<li><p><strong>Context failures:</strong> How often did the system lose important information during multi-turn conversations?</p>
</li>
<li><p><strong>Critical hallucinations:</strong> How often did the system confidently provide unsupported information?</p>
</li>
</ul>
<p>The right metrics depend on the product and its risks. A customer-service FAQ bot and an AI system supporting financial decisions shouldn't necessarily have the same quality thresholds.</p>
<h2 id="heading-14-create-risk-based-conversational-tests">14. Create Risk-Based Conversational Tests</h2>
<p>This is another traditional QA principle that transfers very well.</p>
<p>Not every AI failure has the same impact. If an AI responds awkwardly to: "What are your business hours?", that's inconvenient.</p>
<p>If it gives incorrect information about a payment, account security, healthcare instruction, or financial policy, the impact could be much greater.</p>
<p>So categorize scenarios by risk.</p>
<p>For example:</p>
<table>
<thead>
<tr>
<th>Risk</th>
<th>Example</th>
<th>Testing Priority</th>
</tr>
</thead>
<tbody><tr>
<td>Low</td>
<td>General FAQ</td>
<td>Normal</td>
</tr>
<tr>
<td>Medium</td>
<td>Account navigation</td>
<td>High</td>
</tr>
<tr>
<td>High</td>
<td>Financial/account action</td>
<td>Very High</td>
</tr>
<tr>
<td>Critical</td>
<td>Security/privacy behavior</td>
<td>Mandatory regression</td>
</tr>
</tbody></table>
<p>Then concentrate regression coverage on the scenarios where incorrect AI behavior would cause the greatest harm.</p>
<h2 id="heading-15-a-practical-conversational-ai-test-strategy">15. A Practical Conversational AI Test Strategy</h2>
<p>If I were starting a conversational AI QA effort today, I would organize it into these layers:</p>
<h3 id="heading-layer-1-intent-testing">Layer 1: Intent Testing</h3>
<p>Can the system understand what the user wants despite variations in language?</p>
<h3 id="heading-layer-2-response-evaluation">Layer 2: Response Evaluation</h3>
<p>Are responses accurate, relevant, complete, clear, and useful?</p>
<h3 id="heading-layer-3-conversation-testing">Layer 3: Conversation Testing</h3>
<p>Can the system maintain context across multiple turns?</p>
<h3 id="heading-layer-4-knowledge-and-grounding">Layer 4: Knowledge and Grounding</h3>
<p>Are answers supported by approved and current information?</p>
<h3 id="heading-layer-5-negative-and-hallucination-testing">Layer 5: Negative and Hallucination Testing</h3>
<p>Does the system avoid confidently answering when it doesn't have enough information?</p>
<h3 id="heading-layer-6-fallback-and-escalation">Layer 6: Fallback and Escalation</h3>
<p>Can the system recover when it doesn't understand, and can it hand off to a human when necessary?</p>
<h3 id="heading-layer-7-integration-testing">Layer 7: Integration Testing</h3>
<p>Are APIs, authentication, databases, and downstream systems behaving correctly?</p>
<h3 id="heading-layer-8-regression-testing">Layer 8: Regression Testing</h3>
<p>Can important behaviors be rerun after model, prompt, knowledge, or application changes?</p>
<p>That gives QA teams a much more structured starting point than simply opening a chatbot and asking random questions.</p>
<h2 id="heading-what-traditional-qa-engineers-already-bring-to-ai-testing">What Traditional QA Engineers Already Bring to AI Testing</h2>
<p>When I first started learning conversational AI, I thought I needed to forget everything I knew about traditional testing. But I don't believe that anymore.</p>
<p>A lot of our existing skills transfer extremely well. We already know how to:</p>
<ul>
<li><p>question assumptions</p>
</li>
<li><p>explore edge cases</p>
</li>
<li><p>design negative tests</p>
</li>
<li><p>trace failures through multiple systems</p>
</li>
<li><p>validate integrations</p>
</li>
<li><p>prioritize by risk</p>
</li>
<li><p>build regression suites</p>
</li>
<li><p>investigate unexpected behavior</p>
</li>
</ul>
<p>What changes is the definition of the expected result.</p>
<p>For some AI scenarios, the expected result isn't:</p>
<p><strong>Response = X</strong></p>
<p>It's closer to:</p>
<p><strong>The response must satisfy X, Y, and Z while avoiding A and B.</strong></p>
<p>Once I understood that distinction, conversational AI testing started making much more sense to me.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>My first instinct when learning conversational AI was to search for the test cases.</p>
<p>Now I think that's the wrong place to start. Start with the user.</p>
<ul>
<li><p>What are they trying to accomplish?</p>
</li>
<li><p>What are the different ways they might ask for it?</p>
</li>
<li><p>What information does the AI need?</p>
</li>
<li><p>What would a useful answer contain?</p>
</li>
<li><p>What should the system never say?</p>
</li>
<li><p>What happens if the AI doesn't know?</p>
</li>
<li><p>What happens when the conversation changes direction?</p>
</li>
<li><p>What evidence would make you confident enough to release that experience to real users?</p>
</li>
</ul>
<p>Those questions eventually become your test cases.</p>
<p>Conversational AI may be less deterministic than the applications many QA engineers are used to testing. But that doesn't make it untestable. It simply means we need to move beyond asking:</p>
<p>"Did I get the exact output I expected?"</p>
<p>and start asking:</p>
<p>"Did the system behave correctly, safely, and usefully across the different ways a real person might interact with it?"</p>
<p>For me, that was the biggest shift. The tools are changing, and so is the interface. Even the definition of an expected result is changing. But the fundamental responsibility of quality engineering hasn't changed very much at all: understand how the system can fail before the user has to discover it for you.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What Modern QA Engineers Actually Do: It's More Than Finding Bugs ]]>
                </title>
                <description>
                    <![CDATA[ Ask someone what a QA engineer does, and you'll probably hear a familiar answer: "They test software and find bugs." It's a common perception, and to be fair, finding bugs is an important part of the  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-does-a-modern-qa-engineer-do/</link>
                <guid isPermaLink="false">6a722e60649a71090a9c2307</guid>
                
                    <category>
                        <![CDATA[ Quality Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation testing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ career advice ]]>
                    </category>
                
                    <category>
                        <![CDATA[ QAengineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Quality Assurance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ GAYATHRI BOLINENI ]]>
                </dc:creator>
                <pubDate>Tue, 04 Aug 2026 18:24:32 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/55c4125f-a946-42b9-a694-46ab4cb3f6d6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Ask someone what a QA engineer does, and you'll probably hear a familiar answer: <em>"They test software and find bugs."</em></p>
<p>It's a common perception, and to be fair, finding bugs is an important part of the job. But if you spend even a few weeks working on a modern software team, you'll quickly realize that's only a small part of what QA engineers actually do.</p>
<p>Software development has changed dramatically over the last decade. Teams no longer wait months to release new features. Many organizations deploy updates every week, every day, or even several times a day. Applications have become more complex, with cloud services, APIs, microservices, mobile apps, and third-party integrations all working together behind the scenes.</p>
<p>As software has evolved, the role of QA has evolved with it.</p>
<p>Today's QA engineers are involved long before a feature reaches testing. They help review requirements, identify risks, clarify business expectations, verify APIs and databases, automate repetitive tests, investigate production issues, and work closely with developers throughout the entire development lifecycle.</p>
<p>In other words, QA isn't just about finding problems after software has been built. It's about helping prevent those problems from happening in the first place.</p>
<p>Whether you're thinking about becoming a QA engineer, transitioning from manual testing into automation, or simply curious about what quality engineering looks like today, understanding how the role has changed is an important first step.</p>
<p>In this article, we'll explore what QA engineers actually do, why the profession has evolved, and the skills that have become essential for building reliable software in today's fast-moving development environments.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-how-the-qa-role-has-changed">How the QA Role Has Changed</a></p>
</li>
<li><p><a href="#heading-good-testing-starts-before-the-first-test-case">Good Testing Starts Before the First Test Case</a></p>
</li>
<li><p><a href="#heading-real-world-example">Real-world Example</a></p>
</li>
<li><p><a href="#heading-modern-qa-is-about-more-than-automation">Modern QA Is About More Than Automation</a></p>
</li>
<li><p><a href="#heading-where-different-types-of-testing-fit">Where Different Types of Testing Fit</a></p>
</li>
<li><p><a href="#heading-skills-every-qa-engineer-should-develop">Skills Every QA Engineer Should Develop</a></p>
</li>
<li><p><a href="#heading-common-misconceptions-about-qa">Common Misconceptions About QA</a></p>
</li>
<li><p><a href="#heading-if-youre-considering-a-career-in-qa">If You're Considering a Career in QA</a></p>
</li>
<li><p><a href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-how-the-qa-role-has-changed">How the QA Role Has Changed</h2>
<p>Many software usually teams followed a simple workflow. Business analysts gathered requirements, developers built the application, and once development was complete, the software was handed over to QA for testing. If defects were found, the application went back to the development team before eventually being released.</p>
<p>In that model, QA was often viewed as the final checkpoint before production. Testing happened after most of the important technical decisions had already been made.</p>
<p>That approach worked reasonably well when software releases happened only a few times a year. Teams had enough time to finish development, perform weeks of manual testing, fix defects, and prepare for a scheduled release.</p>
<p>Todays software development looks very different.</p>
<p>Many organizations use Agile methodologies, Continuous Integration (CI), and Continuous Delivery (CD). Instead of delivering software every few months, teams continuously add features, fix bugs, and release improvements in short development cycles.</p>
<p>Because development moves much faster, quality can no longer be treated as the final phase of a project.</p>
<p>Instead, QA engineers work alongside developers, product owners, business analysts, UX designers, and DevOps engineers from the beginning of the development process. They participate in sprint planning, review user stories, discuss acceptance criteria, identify potential risks, and help ensure new features are designed with testing in mind before development even starts.</p>
<p>This shift has changed the role of QA from <strong>testing completed software</strong> to <strong>helping teams build quality into the software from the start</strong>.</p>
<p>For example, imagine a team is building an online banking application that allows customers to transfer money between accounts. A traditional testing approach might focus on verifying whether the transfer succeeds after the feature has been developed.</p>
<p>An experienced QA engineer starts much earlier by asking questions such as:</p>
<ul>
<li><p>What happens if the network connection is interrupted during the transfer?</p>
</li>
<li><p>What should happen if the customer has insufficient funds?</p>
</li>
<li><p>Can the same transfer request be submitted twice accidentally?</p>
</li>
<li><p>How should the application respond if the receiving bank is temporarily unavailable?</p>
</li>
</ul>
<p>Questions like these help uncover potential problems before developers spend time writing code. Addressing these scenarios early is often much less expensive than discovering them during testing—or worse, after the application has been released to customers.</p>
<p>This is one of the biggest reasons QA has evolved from a role focused primarily on testing into one that contributes throughout the entire software development lifecycle.</p>
<p>Quality is no longer something that's checked at the end of a project.</p>
<p>It's something the entire team builds together, one decision at a time.</p>
<p><strong>Traditional Development</strong></p>
<p>Requirements -&gt; Development -&gt; QA Testing -&gt; Production</p>
<p><strong>Modern Development</strong></p>
<p>Requirement -&gt; DEV+QA+Product Owner -&gt; Continuous Development -&gt; Continuous Testing -&gt; Production</p>
<h2 id="heading-good-testing-starts-before-the-first-test-case">Good Testing Starts Before the First Test Case</h2>
<p>One of the biggest surprises for people entering software testing is discovering that a significant part of a QA engineer's work happens before a single test case is written.</p>
<p>Many people picture testing as something that begins only after developers finish building a feature. In reality, experienced QA engineers become involved much earlier because that's often where they can have the greatest impact.</p>
<p>Consider a simple requirement:</p>
<blockquote>
<p><strong>A user must create a password containing at least eight characters.</strong></p>
</blockquote>
<p>At first glance, the requirement seems complete. A developer can implement it, and a tester can verify that passwords shorter than eight characters are rejected.</p>
<p>But software requirements are rarely that straightforward.</p>
<p>A QA engineer naturally starts looking beyond the obvious by asking questions such as:</p>
<ul>
<li><p>Should spaces or count as characters? Or Should leading or trailing spaces be removed automatically?</p>
</li>
<li><p>Is there a maximum password length?</p>
</li>
<li><p>Are special or Unicode characters required?</p>
</li>
<li><p>Will the same validation rules apply on both the web application and the mobile app?</p>
</li>
<li><p>What error message should users see if the password is invalid?</p>
</li>
</ul>
<p>None of these questions are about trying to catch developers making mistakes.</p>
<p>They're about making sure the entire team has the same understanding of how the feature should work before development moves forward.</p>
<p>This process is often called <strong>requirement clarification</strong>, and it's one of the most valuable contributions a QA engineer can make.</p>
<p>Without these discussions, developers may implement one interpretation of the requirement while testers validate another. Business stakeholders may expect something entirely different. Even when everyone is working hard, unclear requirements can lead to unnecessary defects, rework, and frustration.</p>
<p>Asking thoughtful questions early helps avoid those situations.</p>
<p>It also saves time.</p>
<p>Finding an unclear requirement during a planning meeting usually takes a few minutes to resolve. Discovering the same issue after development, testing, and deployment can take hours—or even days—to investigate and fix.</p>
<p>Good QA isn't only about verifying that software works correctly.</p>
<p>It's also about helping ensure the team is building the right software in the first place.</p>
<h2 id="heading-real-world-example">Real-world Example</h2>
<p>Imagine an online shopping website that offers discount coupons.</p>
<p>The requirement says:</p>
<blockquote>
<p><strong>"Users can apply one coupon during checkout."</strong></p>
</blockquote>
<p>At first, it sounds simple.</p>
<p>A QA engineer might ask:</p>
<ul>
<li><p>What happens if the coupon has expired?</p>
</li>
<li><p>Can two browser tabs apply the same coupon simultaneously?</p>
</li>
<li><p>What if the customer removes an item after applying the coupon?</p>
</li>
<li><p>Can multiple coupons be combined?</p>
</li>
<li><p>What happens if the payment fails after the coupon has been applied?</p>
</li>
</ul>
<p>These questions often uncover situations that weren't considered during the initial discussion.</p>
<p>Instead of becoming production defects, they become design decisions that the team can address before writing code.</p>
<p>That's one of the reasons experienced QA engineers spend so much time asking questions. They're not slowing development down. They're helping the team avoid expensive surprises later.</p>
<h2 id="heading-modern-qa-is-about-more-than-automation">Modern QA Is About More Than Automation</h2>
<p>If you browse job postings for QA engineers, you'll probably notice one thing almost immediately.</p>
<p>Many of them mention automation tools such as Selenium, Plawright, Cypress, Appium, Javascript, Java, Python, and so on - the list goes on</p>
<p>Because of that, it's easy to assume that QA engineers spend their entire day writing automated tests. Automation is certainly an important part of the job, but it isn't the job itself.</p>
<p>Think about a new feature being added to a food delivery app. Customers can now save multiple delivery addresses and choose one during checkout. Before any automation script is written, a QA engineer is already involved.</p>
<p>They review the requirement to understand how the feature is expected to work. They discuss different scenarios with developers and product owners, such as what should happen if a customer deletes their default address or enters an invalid ZIP code. They identify edge cases that may not have been considered during planning and think about how the feature interacts with existing functionality.</p>
<p>Only after those discussions do they begin designing test scenarios.</p>
<p>Some of those tests may eventually become automated, especially if the feature will be used in future regression testing. Others may be better suited for exploratory testing because they require human observation and judgment.</p>
<p>QA engineers choose the right testing approach based on the problem they're trying to solve rather than trying to automate everything.</p>
<p>A typical week for a QA engineer might include a variety of responsibilities, such as:</p>
<ul>
<li><p>Reviewing new requirements before development begins.</p>
</li>
<li><p>Designing functional and edge-case test scenarios.</p>
</li>
<li><p>Verifying REST API responses using tools like Postman or Bruno.</p>
</li>
<li><p>Validating backend data with SQL queries.</p>
</li>
<li><p>Building or maintaining automated regression tests.</p>
</li>
<li><p>Investigating issues reported from production.</p>
</li>
<li><p>Working with developers to reproduce and understand defects.</p>
</li>
<li><p>Confirming bug fixes before a release.</p>
</li>
</ul>
<p>Some weeks involve more automation than others. Some involve more investigation, collaboration, or exploratory testing.</p>
<p>That's one of the reasons QA engineering is such a diverse field. No two projects are exactly the same, and the work changes depending on the product, the team, and the stage of development.</p>
<p>Automation simply helps QA engineers spend less time repeating predictable tasks so they can focus on the work that requires critical thinking.</p>
<p>It's a tool that supports quality engineering—not the definition of it.</p>
<h2 id="heading-where-different-types-of-testing-fit">Where Different Types of Testing Fit</h2>
<p>Another common misconception is that QA engineers only test the user interface. In reality, software can be tested at many different levels.</p>
<p>For example:</p>
<table>
<thead>
<tr>
<th>Type of Testing</th>
<th>Purpose</th>
</tr>
</thead>
<tbody><tr>
<td><strong>UI Testing</strong></td>
<td>Verifies that users can successfully interact with the application.</td>
</tr>
<tr>
<td><strong>API Testing</strong></td>
<td>Confirms that services communicate correctly and return the expected responses.</td>
</tr>
<tr>
<td><strong>Database Testing</strong></td>
<td>Validates that data is stored, updated, and retrieved accurately.</td>
</tr>
<tr>
<td><strong>Regression Testing</strong></td>
<td>Ensures that new changes haven't broken existing functionality.</td>
</tr>
<tr>
<td><strong>Exploratory Testing</strong></td>
<td>Helps uncover unexpected issues through human observation and creativity.</td>
</tr>
<tr>
<td><strong>Performance Testing</strong></td>
<td>Evaluates how the application behaves under different workloads.</td>
</tr>
</tbody></table>
<p>QA engineers don't necessarily perform all of these types of testing every day, but understanding when and why each approach is useful helps them choose the right strategy for different situations.</p>
<p>Quality isn't achieved by relying on a single testing technique.</p>
<p>It's achieved by combining multiple testing approaches to build confidence that the software behaves correctly under real-world conditions.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a680c143c3aac7c9e746cad/deb53c30-0cef-4708-ae8e-43ca86e03b0d.png" alt="A flowchart showing how a new software feature is validated through UI testing, API testing, and database testing before entering regression testing and finally being released to production" style="display:block;margin:0 auto" width="1433" height="992" loading="lazy">

<h2 id="heading-skills-every-qa-engineer-should-develop">Skills Every QA Engineer Should Develop</h2>
<p>If you're just starting your QA journey, it's easy to focus on learning specific tools. While tools are important, they're only part of the picture. The most successful QA engineers build a combination of technical skills, problem-solving abilities, and effective communication.</p>
<p>Here are some of the core skills that will help you grow in QA role.</p>
<table>
<thead>
<tr>
<th><strong>Skill</strong></th>
<th><strong>Why It Matters</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>Communication</strong></td>
<td>Helps clarify requirements, explain defects, and collaborate effectively with the team.</td>
</tr>
<tr>
<td><strong>Critical Thinking</strong></td>
<td>Identifies edge cases, risks, and scenarios that may not be immediately obvious.</td>
</tr>
<tr>
<td><strong>API Testing</strong></td>
<td>Verifies how applications communicate and ensures backend services behave correctly.</td>
</tr>
<tr>
<td><strong>SQL</strong></td>
<td>Confirms that data is stored, updated, and retrieved accurately from databases.</td>
</tr>
<tr>
<td><strong>Test Automation</strong></td>
<td>Reduces repetitive testing and improves regression testing efficiency.</td>
</tr>
<tr>
<td><strong>CI/CD Knowledge</strong></td>
<td>Helps integrate testing into modern software delivery pipelines.</td>
</tr>
<tr>
<td><strong>Continuous Learning</strong></td>
<td>Keeps your skills current as tools, technologies, and development practices evolve.</td>
</tr>
</tbody></table>
<p>We don't need to masters all of these skills overnight. The important thing is to build a strong foundation, stay curious, and keep learning as the industry evolves.</p>
<h2 id="heading-common-misconceptions-about-qa">Common Misconceptions About QA</h2>
<p>If you're new to software testing, you've probably heard some of these statements before. While they may sound reasonable, they don't reflect how modern QA teams actually work.</p>
<h3 id="heading-myth-qa-engineers-only-find-bugs">Myth: QA engineers Only Find Bugs</h3>
<p><strong>Reality:</strong> Finding bugs is only one part of the job. QA engineers also review requirements, identify risks, design test strategies, automate repetitive testing, validate APIs and databases, and help teams prevent defects before software reaches users.</p>
<h3 id="heading-myth-automation-will-replace-qa-engineers">Myth: Automation Will Replace QA Engineers.</h3>
<p><strong>Reality:</strong> Automation is a tool, not a replacement for human thinking. Automated tests can execute repetitive tasks, but they can't decide what should be tested, identify unclear requirements, or evaluate whether a feature delivers a good user experience.</p>
<h3 id="heading-myth-qa-is-responsible-for-quality">Myth: QA is Responsible for Quality.</h3>
<p><strong>Reality:</strong> Quality is a shared responsibility. Developers write reliable code, product owners define clear requirements, designers focus on usability, DevOps engineers build dependable deployment pipelines, and QA engineers help ensure everything works together as expected.</p>
<h3 id="heading-myth-manual-testing-is-no-longer-useful">Myth: Manual Testing is No Longer Useful.</h3>
<p><strong>Reality:</strong> Automation is excellent for repetitive regression testing, but manual testing remains valuable for exploratory testing, usability checks, and investigating unexpected behavior. The two approaches complement each other rather than compete.</p>
<h3 id="heading-myth-qa-is-easier-than-software-development">Myth: QA is Easier than Software Development.</h3>
<p><strong>Reality:</strong> Modern QA requires strong analytical thinking, technical knowledge, communication skills, and a solid understanding of how software systems work. While the responsibilities differ from software development, both roles play an equally important part in delivering high-quality software.</p>
<h2 id="heading-if-youre-considering-a-career-in-qa">If You're Considering a Career in QA</h2>
<p>If you're thinking about becoming a QA engineer, don't worry if you don't know every testing tool or automation framework yet. Every experienced QA professional started by learning the fundamentals.</p>
<p>Begin by understanding how software works. Learn how web applications communicate with APIs, how data is stored in databases, and how different components interact to deliver a feature. These concepts will help you understand <em>why</em> you're testing something, not just <em>how</em> to test it.</p>
<p>At the same time, practice thinking like a user. Ask questions, explore different scenarios, and look beyond the "happy path." Some of the most valuable defects are discovered simply because someone asked, <em>"What happens if this doesn't go as expected?"</em></p>
<p>As you grow, gradually build technical skills such as SQL, API testing, automation, version control, and CI/CD. Focus on continuous learning and improving one skill at a time.</p>
<p>Most importantly, remember that software quality isn't created by one person or one team. The best QA engineers work collaboratively, communicate effectively, and help everyone build better software together.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Software testing has come a long way from being viewed as the final step before a release.</p>
<p>Today's QA engineers contribute throughout the software development lifecycle. They ask thoughtful questions, clarify requirements, automate repetitive testing, validate APIs and databases, investigate production issues, and help teams deliver reliable software with confidence.</p>
<p>Finding bugs will always be an important part of the role, but it's no longer what defines a successful QA engineer.</p>
<p>What truly makes a difference is the ability to prevent problems before they happen, think critically about how software is used, and collaborate with the entire team to deliver a better product.</p>
<p>If you're just starting your journey in software quality, don't measure your progress by how many testing tools you've learned. Focus on building strong fundamentals, staying curious, and continuously improving your problem-solving skills.</p>
<p>Technology will continue to evolve, but those qualities will always be valuable.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Unit Testing in Go - A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ If you're learning Go and you’re already familiar with the idea of unit testing, the main challenge is usually not why to test, but how to test in Go. Go takes a deliberately minimal approach to testing. There are no built-in assertions, no annotatio... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/unit-testing-in-go-a-beginners-guide/</link>
                <guid isPermaLink="false">696535ad7a48c374647910f2</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unit testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gabor Koos ]]>
                </dc:creator>
                <pubDate>Mon, 12 Jan 2026 17:55:57 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768240528981/73c9c9f6-4942-4c39-9e62-87f540fd2233.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're learning Go and you’re already familiar with the idea of <strong>unit testing</strong>, the main challenge is usually not <em>why</em> to test, but <em>how</em> to test in Go.</p>
<p>Go takes a deliberately minimal approach to testing. There are no built-in assertions, no annotations, and no special syntax. Instead, tests are written as regular Go code using a small standard library package, and run with a single command. This can feel unusual at first if you're coming from ecosystems with richer testing frameworks, but it quickly becomes predictable and easy to reason about.</p>
<p>In this article, we'll look at how unit testing works in Go in practice. We'll write a few small tests, run them from the command line, and cover the most common patterns you'll see in real Go codebases, such as table-driven tests and testing functions that return errors. We'll focus on the essentials and won't cover more advanced topics like mocks or external frameworks.</p>
<p>The goal is to show how familiar testing concepts translate into idiomatic Go. By the end, you should feel comfortable reading and writing basic unit tests and integrating them into your regular Go workflow.</p>
<h2 id="heading-what-well-cover">What We'll Cover:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-writing-your-first-test">Writing Your First Test</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-running-your-test">Running Your Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-divide-by-zero">Divide by Zero</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-terrorf-vs-tfatalf">t.Errorf vs t.Fatalf</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-table-driven-tests">Table-Driven Tests</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-table-driven-add-test">Table-Driven Add Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-table-driven-divide-test">Table-Driven Divide Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-exercise">Exercise</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-testing-functions-that-return-errors">Testing Functions That Return Errors</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-safe-divide-function">Safe Divide Function</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-writing-tests-for-safedivide">Writing Tests for SafeDivide()</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-exercise-1">Exercise</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices-and-tips">Best Practices and Tips</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-name-tests-clearly">Name Tests Clearly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-keep-tests-small-and-focused">Keep Tests Small and Focused</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-table-driven-tests-for-repetitive-cases">Use Table-Driven Tests for Repetitive Cases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-check-errors-explicitly">Check Errors Explicitly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-avoid-panics-when-possible">Avoid Panics When Possible</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-tests-frequently">Run Tests Frequently</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-keep-tests-in-the-same-package">Keep Tests in the Same Package</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-tfatalf-vs-terrorf-appropriately">Use t.Fatalf vs t.Errorf Appropriately</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-solutions-to-exercises">Solutions to Exercises</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-subtract-function-and-tests">Subtract Function and Tests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-safesubtract-function-and-tests">SafeSubtract Function and Tests</a></p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you start, you should be comfortable with:</p>
<ul>
<li><p>Writing and running basic Go programs</p>
</li>
<li><p>Defining and calling functions in Go</p>
</li>
<li><p>Understanding basic Go types (int, string, bool, and so on)</p>
</li>
<li><p>Using the Go command-line tool (go run, go build)</p>
</li>
<li><p>Basic understanding of unit tests: what a test is and why it's useful</p>
</li>
<li><p>Familiarity with Test-Driven Development concepts like testing before or alongside writing code</p>
</li>
<li><p>Awareness of common testing ideas such as assertions, test coverage, and checking error conditions</p>
</li>
</ul>
<p>You don't need prior experience with Go's <code>testing</code> package or Go-specific test patterns, as this guide will cover all of that.</p>
<h2 id="heading-writing-your-first-test">Writing Your First Test</h2>
<p>Let's start with a simple function to test. Imagine you have a small <code>calc</code> package with an <code>Add</code> function:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-comment">// Add returns the sum of two integers</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Add</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a + b
}
</code></pre>
<p>To test this function, create a new file named <code>calc_test.go</code> in the same package. In Go, test files must end with <code>_test.go</code> to be recognized by the testing tool.</p>
<p>Inside <code>calc_test.go</code>, you write a test function:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestAdd</span><span class="hljs-params">(t *testing.T)</span></span> {
    got := Add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>)
    want := <span class="hljs-number">5</span>
    <span class="hljs-keyword">if</span> got != want {
        t.Errorf(<span class="hljs-string">"Add(2, 3) = %d; want %d"</span>, got, want)
    }
}
</code></pre>
<p>Here's what's happening:</p>
<ul>
<li><p>The function name starts with <code>Test</code> and takes a single <code>*testing.T</code> parameter. Go automatically discovers and runs any function that follows this convention.</p>
</li>
<li><p>The <code>t.Errorf</code> call reports a test failure. Unlike some frameworks, Go doesn't provide special assertions – you simply check a condition and call <code>t.Errorf</code> or <code>t.Fatalf</code> if it fails.</p>
</li>
<li><p>Each test is a standalone function. You can write as many as you like, and Go will run them all.</p>
</li>
</ul>
<h3 id="heading-running-your-test">Running Your Test</h3>
<p>Once the file is saved, you can run your test with:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span>
</code></pre>
<p>This runs tests for the current package (files ending with <code>_test.go</code>). If you want to run tests recursively in all subdirectories of your project, use:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> ./...
</code></pre>
<p>The <code>./...</code> pattern is shorthand for "run tests in this directory and all subdirectories". This is especially useful in larger projects where your code is spread across multiple packages.</p>
<p>If everything is working, you should see output indicating that the test passed:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
PASS
ok      _/C_/projects/Articles/Go_Testing       0.334s
</code></pre>
<p>You can add the <code>-v</code> flag for verbose output:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> -v
</code></pre>
<p>This will show you the names of the tests as they run:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.356s
</code></pre>
<p>Not much difference for a single test, but it becomes useful as you add more tests.</p>
<p>Now let's see what happens if the test fails. Change the expected value in <code>calc_test.go</code> to an incorrect one:</p>
<pre><code class="lang-go">  ...
    want := <span class="hljs-number">6</span> <span class="hljs-comment">// Incorrect expected value</span>
  ...
</code></pre>
<p>Run the tests again:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
--- FAIL: TestAdd (0.00s)
    calc_test.go:9: Add(2, 3) = 5; want 6
FAIL
<span class="hljs-built_in">exit</span> status 1
FAIL    _/C_/projects/Articles/Go_Testing       0.340s
</code></pre>
<p>or with verbose output:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAdd
    calc_test.go:9: Add(2, 3) = 5; want 6
--- FAIL: TestAdd (0.00s)
FAIL
<span class="hljs-built_in">exit</span> status 1
FAIL    _/C_/projects/Articles/Go_Testing       0.337s
</code></pre>
<p>Of course, your tests should always check for the correct expected values! A failing (but correct) test is a sign that your code needs to be fixed.</p>
<p>We only created one test file and one test function with one assertion here, but Go's testing tool can handle many files and functions at once. Behind the scenes, Go will automatically:</p>
<ul>
<li><p>Find <strong>all</strong> <code>_test.go</code> files in the specified packages (for example, current directory for <code>go test</code>, or recursively in all subdirectories with <code>go test ./...</code>).</p>
</li>
<li><p>Identify functions that start with <code>Test</code> and have the correct signature.</p>
</li>
<li><p>Compile them together with your package into a temporary test binary.</p>
</li>
<li><p>Execute each test function and report the results.</p>
</li>
</ul>
<p>To prove this, let's quickly add a <code>Divide</code> function to our package:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
...
<span class="hljs-comment">// Divide returns the result of dividing a by b</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Divide</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a / b
}
</code></pre>
<p>(Note that this is an <strong>integer division</strong>, so fractional parts are discarded. <code>Divide(5, 2)</code> would return <code>2</code>.)</p>
<p>And another test file with a corresponding test:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_2_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestDivide</span><span class="hljs-params">(t *testing.T)</span></span> {
    got := Divide(<span class="hljs-number">10</span>, <span class="hljs-number">2</span>)
    want := <span class="hljs-number">5</span>    
    <span class="hljs-keyword">if</span> got != want {
        t.Errorf(<span class="hljs-string">"Divide(10, 2) = %d; want %d"</span>, got, want)
    }    
}
</code></pre>
<p>Now when you run <code>go test</code>, both <code>TestAdd</code> and <code>TestDivide</code> will be executed:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
PASS
ok      _/C_/projects/Articles/Go_Testing       0.325s
</code></pre>
<p>Or:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestDivide
--- PASS: TestDivide (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.323s
</code></pre>
<h3 id="heading-divide-by-zero">Divide by Zero</h3>
<p>What happens if we try to <code>Divide</code> by zero? Let's add another test case for that:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
...
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestDivideByZero</span><span class="hljs-params">(t *testing.T)</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">if</span> r := <span class="hljs-built_in">recover</span>(); r == <span class="hljs-literal">nil</span> { <span class="hljs-comment">// Check if a panic occurred</span>
            t.Errorf(<span class="hljs-string">"Divide did not panic on division by zero"</span>)
        }
    }()
    Divide(<span class="hljs-number">10</span>, <span class="hljs-number">0</span>) <span class="hljs-comment">// This should cause a panic</span>
}
</code></pre>
<p>This test checks that the <code>Divide</code> function panics when dividing by zero. When you run the tests again, you'll see that this new test also passes:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestDivide
--- PASS: TestDivide (0.00s)
=== RUN   TestDivideByZero
--- PASS: TestDivideByZero (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.312s
</code></pre>
<p>(Note that in real-world Go code, it's better to return <code>(int, error)</code> for unsafe operations instead of panicking.)</p>
<p>Feel free to experiment by adding more test cases, changing expected values, and exploring how Go's testing framework handles different scenarios.</p>
<h3 id="heading-terrorf-vs-tfatalf"><code>t.Errorf</code> vs <code>t.Fatalf</code></h3>
<p>In the examples above, we used <code>t.Errorf</code> to report test failures. This function logs the error but allows the test to continue running. This is useful when you want to check multiple conditions in a single test function.</p>
<p>In contrast, <code>t.Fatalf</code> logs the error and immediately stops the execution of the current test. Use <code>t.Fatalf</code> when continuing the test after a failure doesn't make sense or could cause misleading results.</p>
<p>For example, in the <code>TestDivideByZero</code> test, if the <code>Divide</code> function does not panic, we use <code>t.Errorf</code> to report the failure but continue to the end of the test. But if we had additional checks after the division, we might want to use <code>t.Fatalf</code> to stop execution immediately upon failure.</p>
<p>While <code>t.Errorf</code> and <code>t.Fatalf</code> use <code>fmt</code>-style formatting, for simple messages without formatting, you can also use <code>t.Error</code> and <code>t.Fatal</code>, respectively.</p>
<p>In the next section, we'll look at <em>table-driven tests</em>, a common Go pattern for testing multiple cases efficiently.</p>
<h2 id="heading-table-driven-tests">Table-Driven Tests</h2>
<p>In Go, it's common to want to run the same test logic for multiple inputs and expected outputs. Rather than writing a separate test function for each case, Go developers often use <strong>table-driven tests</strong>. This pattern keeps your tests concise, readable, and easy to extend.</p>
<h3 id="heading-table-driven-add-test">Table-Driven <code>Add</code> Test</h3>
<p>Let's rewrite our Add test using a table-driven approach (and delete <code>calc_2_test.go</code> for clarity):</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestAddTableDriven</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {<span class="hljs-comment">// Define a struct for each test case and create a slice of them</span>
        name <span class="hljs-keyword">string</span>
        a, b <span class="hljs-keyword">int</span>
        want <span class="hljs-keyword">int</span>
    }{
        {<span class="hljs-string">"both positive"</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>},
        {<span class="hljs-string">"positive + zero"</span>, <span class="hljs-number">5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>},
        {<span class="hljs-string">"negative + positive"</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>},
        {<span class="hljs-string">"both negative"</span>, <span class="hljs-number">-2</span>, <span class="hljs-number">-3</span>, <span class="hljs-number">-5</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {<span class="hljs-comment">// Loop over each test case</span>
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {<span class="hljs-comment">// Run each case as a subtest</span>
            got := Add(tt.a, tt.b)
            <span class="hljs-keyword">if</span> got != tt.want {<span class="hljs-comment">// Check the result</span>
                t.Errorf(<span class="hljs-string">"Add(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want) <span class="hljs-comment">// Report failure if it doesn't match</span>
            }
        })
    }
}
</code></pre>
<p>Here's how it works:</p>
<ul>
<li><p>We define a <strong>slice of structs</strong>, each representing a test case.</p>
</li>
<li><p>Each struct contains the test name, input values, and the expected result.</p>
</li>
<li><p>We loop over the slice and call <code>t.Run(tt.name, func(t *testing.T) { ... })</code> to run each test as a <strong>subtest</strong>.</p>
</li>
<li><p>If a subtest fails, you can see which one by its name in the output.</p>
</li>
</ul>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span>
PASS
ok      _/C_/projects/Articles/Go_Testing       0.452s
</code></pre>
<p>Or to see detailed output:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAddTableDriven
=== RUN   TestAddTableDriven/both_positive
=== RUN   TestAddTableDriven/positive_+_zero
=== RUN   TestAddTableDriven/negative_+_positive
=== RUN   TestAddTableDriven/both_negative
--- PASS: TestAddTableDriven (0.00s)
    --- PASS: TestAddTableDriven/both_positive (0.00s)
    --- PASS: TestAddTableDriven/positive_+_zero (0.00s)
    --- PASS: TestAddTableDriven/negative_+_positive (0.00s)
    --- PASS: TestAddTableDriven/both_negative (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.385s
</code></pre>
<h3 id="heading-table-driven-divide-test">Table-Driven Divide Test</h3>
<p>We can apply the same pattern to <code>Divide</code>, including checking for divide-by-zero:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
...
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestDivideTableDriven</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> { <span class="hljs-comment">// Define test cases</span>
        name     <span class="hljs-keyword">string</span>
        a, b     <span class="hljs-keyword">int</span>
        want     <span class="hljs-keyword">int</span>
        wantPanic <span class="hljs-keyword">bool</span>
    }{
        {<span class="hljs-string">"normal division"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-literal">false</span>},
        {<span class="hljs-string">"division by zero"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">true</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests { <span class="hljs-comment">// Loop over</span>
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> { <span class="hljs-comment">// Run subtest</span>
            <span class="hljs-keyword">if</span> tt.wantPanic { <span class="hljs-comment">// Check for expected panic</span>
                <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> { <span class="hljs-comment">// Recover from panic</span>
                    <span class="hljs-keyword">if</span> r := <span class="hljs-built_in">recover</span>(); r == <span class="hljs-literal">nil</span> {
                        t.Errorf(<span class="hljs-string">"Divide(%d, %d) did not panic"</span>, tt.a, tt.b)
                    }
                }()
            }
            got := Divide(tt.a, tt.b) <span class="hljs-comment">// Tests that do not panic</span>
            <span class="hljs-keyword">if</span> !tt.wantPanic &amp;&amp; got != tt.want {
                t.Errorf(<span class="hljs-string">"Divide(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want)
            }
        })
    }
}
</code></pre>
<p>This example shows how to handle both normal and panic cases in a single table-driven test:</p>
<ul>
<li><p>The <code>wantPanic</code> field tells the test whether we expect a panic.</p>
</li>
<li><p>We use <code>defer</code> and <code>recover</code> to check for a panic when needed.</p>
</li>
<li><p>Normal test cases still check the result as usual.</p>
</li>
</ul>
<p>Run all tests as before:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
=== RUN   TestAddTableDriven
=== RUN   TestAddTableDriven/both_positive
=== RUN   TestAddTableDriven/positive_+_zero
=== RUN   TestAddTableDriven/negative_+_positive
=== RUN   TestAddTableDriven/both_negative
--- PASS: TestAddTableDriven (0.00s)
    --- PASS: TestAddTableDriven/both_positive (0.00s)
    --- PASS: TestAddTableDriven/positive_+_zero (0.00s)
    --- PASS: TestAddTableDriven/negative_+_positive (0.00s)
    --- PASS: TestAddTableDriven/both_negative (0.00s)
=== RUN   TestDivideTableDriven
=== RUN   TestDivideTableDriven/normal_division
=== RUN   TestDivideTableDriven/division_by_zero
--- PASS: TestDivideTableDriven (0.00s)
    --- PASS: TestDivideTableDriven/normal_division (0.00s)
    --- PASS: TestDivideTableDriven/division_by_zero (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.321s
</code></pre>
<p>Subtest names make it easy to see which case passed or failed.</p>
<h3 id="heading-exercise">Exercise</h3>
<p>Try creating your own table-driven test for a new function, <code>Subtract(a, b int) int</code>. Include at least four test cases:</p>
<ul>
<li><p>Both positive numbers</p>
</li>
<li><p>Positive minus zero</p>
</li>
<li><p>Negative minus positive</p>
</li>
<li><p>Both negative</p>
</li>
</ul>
<p>Then run your tests and verify the output.</p>
<h2 id="heading-testing-functions-that-return-errors">Testing Functions That Return Errors</h2>
<p>Many Go functions return an error as the last return value. Writing tests for these functions is slightly different from testing pure functions like our <code>Add</code> or <code>Divide</code>, because you need to check both the result and whether an error occurred.</p>
<h3 id="heading-safe-divide-function">Safe Divide Function</h3>
<p>Let's add a <code>SafeDivide</code> function to return an error instead of panicking:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
...
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>
...
<span class="hljs-comment">// SafeDivide returns the result of dividing a by b.</span>
<span class="hljs-comment">// It returns an error if b is zero.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">SafeDivide</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-keyword">if</span> b == <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, fmt.Errorf(<span class="hljs-string">"cannot divide by zero"</span>)
    }
    <span class="hljs-keyword">return</span> a / b, <span class="hljs-literal">nil</span>
}
</code></pre>
<h3 id="heading-writing-tests-for-safedivide">Writing Tests for <code>SafeDivide()</code></h3>
<p>We can use a table-driven test again:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestSafeDivide</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {
        name      <span class="hljs-keyword">string</span>
        a, b      <span class="hljs-keyword">int</span>
        want      <span class="hljs-keyword">int</span>
        wantError <span class="hljs-keyword">bool</span>
    }{
        {<span class="hljs-string">"normal division"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-literal">false</span>},
        {<span class="hljs-string">"division by zero"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">true</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
            got, err := SafeDivide(tt.a, tt.b)
            <span class="hljs-keyword">if</span> tt.wantError {
                <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
                    t.Errorf(<span class="hljs-string">"SafeDivide(%d, %d) expected error, got nil"</span>, tt.a, tt.b)
                }
                <span class="hljs-keyword">return</span> <span class="hljs-comment">// stop here, no need to check `got`</span>
            }
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                t.Errorf(<span class="hljs-string">"SafeDivide(%d, %d) unexpected error: %v"</span>, tt.a, tt.b, err)
            }
            <span class="hljs-keyword">if</span> got != tt.want {
                t.Errorf(<span class="hljs-string">"SafeDivide(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want)
            }
        })
    }
}
</code></pre>
<p>What's happening here:</p>
<ul>
<li><p>We added a <code>wantError</code> field to indicate whether the test expects an error.</p>
</li>
<li><p>If an error is expected, we check that <code>err != nil</code>. If not (that is, <code>err == nil</code>), we fail the test.</p>
</li>
<li><p>If no error is expected, we check both the returned value (<code>got</code>) and that <code>err == nil</code>.</p>
</li>
<li><p>Using <code>t.Run</code> subtests keeps everything organized and readable.</p>
</li>
</ul>
<p>Running the tests again:</p>
<pre><code class="lang-bash">$ go <span class="hljs-built_in">test</span> -v
...
=== RUN   TestSafeDivide
=== RUN   TestSafeDivide/normal_division
=== RUN   TestSafeDivide/division_by_zero
--- PASS: TestSafeDivide (0.00s)
    --- PASS: TestSafeDivide/normal_division (0.00s)
    --- PASS: TestSafeDivide/division_by_zero (0.00s)
PASS
ok      _/C_/projects/Articles/Go_Testing       0.323s
</code></pre>
<p>Showing that both normal and error cases are handled correctly.</p>
<h3 id="heading-exercise-1">Exercise</h3>
<p>Update your <code>Subtract(a, b int) int</code> function to a <code>SafeSubtract(a, b int) (int, error)</code> variant that returns an error if the result would be negative. Then write a table-driven test that covers:</p>
<ul>
<li><p>A positive result</p>
</li>
<li><p>Zero result</p>
</li>
<li><p>A negative result (should return an error)</p>
</li>
</ul>
<h2 id="heading-best-practices-and-tips">Best Practices and Tips</h2>
<p>Writing tests in Go is straightforward, but there are a few conventions and tips that make your tests more readable, maintainable, and idiomatic:</p>
<h3 id="heading-name-tests-clearly">Name Tests Clearly</h3>
<p>First, make sure you use descriptive names for test functions and subtests. A good name explains what you're testing and under what conditions.</p>
<p>Here’s an example:</p>
<pre><code class="lang-go">t.Run(<span class="hljs-string">"Divide positive numbers"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> { ... })
t.Run(<span class="hljs-string">"Divide by zero returns error"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> { ... })
</code></pre>
<h3 id="heading-keep-tests-small-and-focused">Keep Tests Small and Focused</h3>
<p>Each subtest should verify one thing, and each test function should cover a single function or method.</p>
<p>Try to avoid combining multiple unrelated checks in the same test function, and use table-driven tests help keep multiple similar checks concise without losing clarity.</p>
<h3 id="heading-use-table-driven-tests-for-repetitive-cases">Use Table-Driven Tests for Repetitive Cases</h3>
<p>If you find yourself writing multiple similar test functions, switch to a table-driven pattern. It makes it easier to add new cases, reduces duplicated code, and keeps output organized with <code>t.Run</code>.</p>
<h3 id="heading-check-errors-explicitly">Check Errors Explicitly</h3>
<p>In Go, functions often return <code>error</code>. So make sure you always check for errors in tests, even if you expect <code>nil</code>.</p>
<p>You can use the <code>wantError</code> pattern in table-driven tests for clarity.</p>
<pre><code class="lang-go"><span class="hljs-keyword">if</span> tt.wantError {
    <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
        t.Errorf(<span class="hljs-string">"expected error, got nil"</span>)
    }
}
</code></pre>
<h3 id="heading-avoid-panics-when-possible">Avoid Panics When Possible</h3>
<p>Panics are fine for some internal checks, but in production code, prefer returning an error.</p>
<p>Your tests can check for panics using <code>defer</code> and <code>recover</code>, but this should be the exception rather than the norm.</p>
<h3 id="heading-run-tests-frequently">Run Tests Frequently</h3>
<p>Try to make running tests a habit: <code>go test -v ./...</code>. Frequent testing helps catch mistakes early and reinforces TDD practices.</p>
<h3 id="heading-keep-tests-in-the-same-package">Keep Tests in the Same Package</h3>
<p>By convention, tests live in the same package as the code they test. You can create <code>_test.go</code> files for testing, and Go automatically recognizes them.</p>
<p>Only use a separate <code>package calc_test</code> if you want to test your code from the outside, like a consumer. External test packages (just like every other external package) cannot access unexported identifiers.</p>
<h3 id="heading-use-tfatalf-vs-terrorf-appropriately">Use t.Fatalf vs t.Errorf Appropriately</h3>
<ul>
<li><p><code>t.Errorf</code> reports a failure but continues running the test.</p>
</li>
<li><p><code>t.Fatalf</code> stops the test immediately, which is useful if subsequent code depends on successful setup.</p>
</li>
</ul>
<p>These tips will help you write clean, maintainable, and idiomatic Go tests that are easy to read and extend. Following these practices early in your Go journey will make testing less intimidating and more effective.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Unit testing in Go may feel different at first, especially if you're coming from ecosystems with heavy frameworks and assertions. But the simplicity of Go's testing tools is one of its strengths: once you understand the conventions, writing, running, and organizing tests becomes predictable and intuitive.</p>
<p>In this guide, you've seen how to:</p>
<ul>
<li><p>Write basic test functions with the testing package</p>
</li>
<li><p>Run tests from the command line and interpret the results</p>
</li>
<li><p>Use table-driven tests to cover multiple cases efficiently</p>
</li>
<li><p>Handle functions that return errors and check for expected failures</p>
</li>
</ul>
<p>Beyond these fundamentals, testing is not just about verifying correctness, it's also about confidence. Well-tested code allows you to refactor, experiment, and add new features with less fear of breaking existing functionality.</p>
<p>As you continue writing Go code, try to integrate testing early, follow the idiomatic patterns you've learned, and explore more advanced topics such as:</p>
<ul>
<li><p>Using <em>mocks</em> or <em>interfaces</em> to isolate dependencies</p>
</li>
<li><p>Benchmark tests with <code>testing.B</code></p>
</li>
<li><p>Coverage analysis with <code>go test -cover</code></p>
</li>
</ul>
<p>The key takeaway is that testing in Go is accessible, flexible, and powerful, even without fancy frameworks. By building these habits now, you'll write code that's more reliable, maintainable, and enjoyable to work with.</p>
<h2 id="heading-solutions-to-exercises">Solutions to Exercises</h2>
<h3 id="heading-subtract-function-and-tests">Subtract Function and Tests</h3>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Subtract</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> a - b
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestSubtractTableDriven</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {
        name <span class="hljs-keyword">string</span>
        a, b <span class="hljs-keyword">int</span>
        want <span class="hljs-keyword">int</span>
    }{
        {<span class="hljs-string">"both positive"</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>},
        {<span class="hljs-string">"positive minus zero"</span>, <span class="hljs-number">5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>},
        {<span class="hljs-string">"negative minus positive"</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-5</span>},
        {<span class="hljs-string">"both negative"</span>, <span class="hljs-number">-3</span>, <span class="hljs-number">-2</span>, <span class="hljs-number">-1</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
            got := Subtract(tt.a, tt.b)
            <span class="hljs-keyword">if</span> got != tt.want {
                t.Errorf(<span class="hljs-string">"Subtract(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want)
            }
        })
    }
}
</code></pre>
<h3 id="heading-safesubtract-function-and-tests">SafeSubtract Function and Tests</h3>
<pre><code class="lang-go"><span class="hljs-comment">// calc.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">SafeSubtract</span><span class="hljs-params">(a, b <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    result := a - b
    <span class="hljs-keyword">if</span> result &lt; <span class="hljs-number">0</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, fmt.Errorf(<span class="hljs-string">"result would be negative"</span>)
    }
    <span class="hljs-keyword">return</span> result, <span class="hljs-literal">nil</span>
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-comment">// calc_test.go</span>
<span class="hljs-keyword">package</span> calc

<span class="hljs-keyword">import</span> <span class="hljs-string">"testing"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">TestSafeSubtract</span><span class="hljs-params">(t *testing.T)</span></span> {
    tests := []<span class="hljs-keyword">struct</span> {
        name      <span class="hljs-keyword">string</span>
        a, b      <span class="hljs-keyword">int</span>
        want      <span class="hljs-keyword">int</span>
        wantError <span class="hljs-keyword">bool</span>
    }{
        {<span class="hljs-string">"positive result"</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-literal">false</span>},
        {<span class="hljs-string">"zero result"</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">false</span>},
        {<span class="hljs-string">"negative result"</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">true</span>},
    }

    <span class="hljs-keyword">for</span> _, tt := <span class="hljs-keyword">range</span> tests {
        t.Run(tt.name, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(t *testing.T)</span></span> {
            got, err := SafeSubtract(tt.a, tt.b)
            <span class="hljs-keyword">if</span> tt.wantError {
                <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
                    t.Errorf(<span class="hljs-string">"SafeSubtract(%d, %d) expected error, got nil"</span>, tt.a, tt.b)
                }
                <span class="hljs-keyword">return</span>
            }
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                t.Errorf(<span class="hljs-string">"SafeSubtract(%d, %d) unexpected error: %v"</span>, tt.a, tt.b, err)
            }
            <span class="hljs-keyword">if</span> got != tt.want {
                t.Errorf(<span class="hljs-string">"SafeSubtract(%d, %d) = %d; want %d"</span>, tt.a, tt.b, got, tt.want)
            }
        })
    }
}
</code></pre>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Scale TestOps for Global Software Development Teams ]]>
                </title>
                <description>
                    <![CDATA[ Imagine that your software team is spread across the globe—developers in the US, testers in Asia, and managers in Europe. Exciting, right? But managing this setup is no walk in the park. Coordinating testing across time zones, tools, and workflows ca... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/scale-testops-for-global-software-development-teams/</link>
                <guid isPermaLink="false">6801211371255f2b14ea901b</guid>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ testops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nazneen Ahmad ]]>
                </dc:creator>
                <pubDate>Thu, 17 Apr 2025 15:41:07 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744904445449/18f469d0-b066-4709-a463-4f378802615d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Imagine that your software team is spread across the globe—developers in the US, testers in Asia, and managers in Europe. Exciting, right? But managing this setup is no walk in the park. Coordinating testing across time zones, tools, and workflows can be challenging.</p>
<p>That is where TestOps comes in. It blends testing with operational efficiency, creating a streamlined approach to quality assurance. Scaling TestOps for global teams means setting up processes that work smoothly across continents, delivering speed and consistency without compromising on quality.</p>
<p>The challenges are real: communication gaps, tool compatibility issues, and cultural differences. But the payoff is worth it. A well-structured TestOps framework helps teams collaborate easily, automate testing, and produce software that meets global expectations.</p>
<p>This guide will walk you through overcoming these challenges, adopting practical strategies, and turning your global TestOps into a hub for innovation and quality.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-understanding-testops">Understanding TestOps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-limitations-of-scaling-testops">Limitations of Scaling TestOps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-strategies-for-scaling-testops">Strategies for Scaling TestOps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-integrate-testops-into-global-devops-pipelines">How to Integrate TestOps into Global DevOps Pipelines</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-ai-and-analytics-in-testops">How to Use AI and Analytics in TestOps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-future-of-testops-in-global-development">Future of TestOps in Global Development</a></p>
</li>
</ol>
<h2 id="heading-understanding-testops">Understanding TestOps</h2>
<p>TestOps is all about using automation to make software testing smoother and more efficient. It brings together scattered teams and processes into a unified system, helping you deliver better software faster and with fewer bugs. But what does it actually do?</p>
<p>TestOps makes testing easier to manage, run, and review. It keeps the testing process organized, consistent, and team-friendly. By using automation and central tools, TestOps helps you avoid mistakes, save time, and deliver better-quality software.</p>
<p>Here are the four central components of TestOps:</p>
<ul>
<li><p><strong>Planning</strong>: This step focuses on deciding what needs to be tested, how it will be tested (including the test environment), when testing will happen, and who will handle it.</p>
</li>
<li><p><strong>Management</strong>: This ensures testing is efficient and scalable by using tools that improve teamwork and visibility.</p>
</li>
<li><p><strong>Execution</strong>: This is the actual process of running tests on the software.</p>
</li>
<li><p><strong>Analysis</strong>: This step involves reviewing test performance, diagnosing issues, and finding ways to improve the overall testing process.</p>
</li>
</ul>
<p>At scale, TestOps focuses on:</p>
<ul>
<li><p><strong>Standardization</strong>: Setting up consistent testing methods and tools that everyone can use across teams and projects.</p>
</li>
<li><p><strong>Automation</strong>: Increasing the use of automated tests to handle more tasks quickly and accurately.</p>
</li>
<li><p><strong>Collaboration</strong>: Improving how teams work together, even if they are spread out in different locations.</p>
</li>
<li><p><strong>Scalability</strong>: Making sure testing systems and processes can grow as needs increase.</p>
</li>
<li><p><strong>Insights</strong>: Using data from large-scale testing to make better decisions and improve how things work.</p>
</li>
</ul>
<h2 id="heading-limitations-of-scaling-testops">Limitations of Scaling TestOps</h2>
<p>Scaling TestOps for global software teams comes with its fair share of challenges. While the advantages of smooth, integrated testing are clear, getting there requires careful planning.</p>
<p>Here are some key obstacles:</p>
<ul>
<li><p><strong>Communication barriers</strong>: With teams spread across different time zones, keeping communication clear and timely can be tough. Delays or misunderstandings can slow progress and affect the quality of testing.</p>
</li>
<li><p><strong>Tool compatibility</strong>: Teams may use different testing tools, leading to inefficiencies and fragmentation. It's important to make sure all tools can work together and are compatible across different environments.</p>
</li>
<li><p><strong>Cultural and organizational differences</strong>: Teams from various regions may have different work cultures, priorities, and expectations. Finding common ground without creating friction is essential for smooth collaboration.</p>
</li>
<li><p><strong>Time zone management</strong>: Coordinating meetings or ensuring real-time review of test results becomes difficult with global teams in different time zones.</p>
</li>
<li><p><strong>Quality consistency</strong>: Ensuring consistent testing standards across multiple locations can be tricky. Without centralized control, practices can vary, which may lead to missed defects and unreliable releases.</p>
</li>
</ul>
<p>Overcoming these challenges requires a well-thought-out strategy, effective communication, and the right tools to align teams and processes across the globe.</p>
<h2 id="heading-strategies-for-scaling-testops">Strategies for Scaling TestOps</h2>
<p>Scaling TestOps for global teams requires smart strategies to address communication issues, tool mismatches, and operational challenges. Here are some key approaches to make scaling work:</p>
<h3 id="heading-standardize-testing-processes"><strong>Standardize Testing Processes</strong></h3>
<p>Set up clear, consistent testing protocols and tools across all teams to ensure everyone is on the same page.</p>
<p>For example, you can standardize testing using frameworks like <strong>Jest</strong> to ensure consistency across teams.</p>
<pre><code class="lang-bash">bashCopy codenpm install --save-dev jest
</code></pre>
<p>In your <strong>package.json</strong>:</p>
<pre><code class="lang-json">jsonCopy code{
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"jest"</span>
  }
}
</code></pre>
<h3 id="heading-use-cloud-based-tools"><strong>Use Cloud-Based Tools</strong></h3>
<p>Choose cloud tools that allow teams to collaborate smoothly, provide real-time feedback, and access testing environments from anywhere.</p>
<p>For example, cloud tools like <strong>LambdaTest</strong> enable remote testing across browsers and devices.</p>
<pre><code class="lang-javascript">javascriptCopy codeconst { remote } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webdriverio'</span>);

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">runTest</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> remote({
    <span class="hljs-attr">capabilities</span>: {
      <span class="hljs-attr">browserName</span>: <span class="hljs-string">'chrome'</span>,
      <span class="hljs-attr">platform</span>: <span class="hljs-string">'Windows 10'</span>,
      <span class="hljs-attr">version</span>: <span class="hljs-string">'latest'</span>,
      <span class="hljs-string">'build'</span>: <span class="hljs-string">'TestOps Scaling Build'</span>,
      <span class="hljs-string">'name'</span>: <span class="hljs-string">'Test Parallel Execution'</span>,
    },
    <span class="hljs-attr">host</span>: <span class="hljs-string">'hub.lambdatest.com'</span>,
    <span class="hljs-attr">port</span>: <span class="hljs-number">80</span>,
    <span class="hljs-attr">user</span>: <span class="hljs-string">'your_username'</span>,
    <span class="hljs-attr">key</span>: <span class="hljs-string">'your_access_key'</span>
  });

  <span class="hljs-keyword">await</span> browser.url(<span class="hljs-string">'https://www.yoursite.com'</span>);
  <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">await</span> browser.getTitle());
  <span class="hljs-keyword">await</span> browser.deleteSession();
}

runTest();
</code></pre>
<h3 id="heading-automate-testing"><strong>Automate Testing</strong></h3>
<p>Integrate automated tests into CI/CD pipelines to reduce manual work, speed up feedback, and improve test coverage.</p>
<p>For example, you can use <strong>GitHub Actions</strong> for CI/CD test automation.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">yamlCopy codename:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Tests</span>

<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">tests</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>
</code></pre>
<h3 id="heading-use-centralized-reporting-tools"><strong>Use Centralized Reporting Tools</strong></h3>
<p>Use dashboards to give everyone real-time updates on testing progress, keeping all teams and stakeholders in the loop.</p>
<p>Here’s an example of integrating with <strong>TestRail</strong> for centralized reporting.</p>
<pre><code class="lang-javascript">javascriptCopy codeconst axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>);

<span class="hljs-keyword">const</span> result = {
  <span class="hljs-string">"status"</span>: <span class="hljs-string">"passed"</span>, 
  <span class="hljs-string">"test_case_id"</span>: <span class="hljs-number">123</span>,
  <span class="hljs-string">"run_id"</span>: <span class="hljs-number">456</span>
};

axios.post(<span class="hljs-string">'https://your-testrail-instance/api/v2/add_result_for_case/1/123'</span>, result, {
  <span class="hljs-attr">auth</span>: { <span class="hljs-attr">username</span>: <span class="hljs-string">'your_email'</span>, <span class="hljs-attr">password</span>: <span class="hljs-string">'your_password'</span> }
})
.then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Test result posted successfully'</span>))
.catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error));
</code></pre>
<h3 id="heading-encourage-cross-regional-collaboration"><strong>Encourage Cross-Regional Collaboration</strong></h3>
<p>Use collaboration tools and hold regular meetings to bridge time zone and cultural differences between teams.</p>
<p>You can use <strong>Slack</strong> or similar tools for real-time communication and alerts.</p>
<pre><code class="lang-javascript">javascriptCopy codeconst slackMessage = { <span class="hljs-attr">text</span>: <span class="hljs-string">"Test Execution Completed: All tests have passed successfully!"</span> };

axios.post(<span class="hljs-string">'https://hooks.slack.com/services/your-webhook-url'</span>, slackMessage)
  .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Slack message sent'</span>))
  .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error));
</code></pre>
<h3 id="heading-create-a-continuous-feedback-loop"><strong>Create a Continuous Feedback Loop</strong></h3>
<p>Set up systems that provide immediate feedback and allow for quick action, ensuring quality isn’t delayed.</p>
<p>For example, you can trigger feedback loops with <strong>Slack</strong> for an immediate response.</p>
<pre><code class="lang-javascript">javascriptCopy codeconst slackMessage = { <span class="hljs-attr">text</span>: <span class="hljs-string">"Alert: Test failure detected!"</span> };

axios.post(<span class="hljs-string">'https://hooks.slack.com/services/your-webhook-url'</span>, slackMessage)
  .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Alert sent to Slack'</span>))
  .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error));
</code></pre>
<h3 id="heading-upskill-teams"><strong>Upskill Teams</strong></h3>
<p>Offer training so all team members know how to use TestOps tools effectively.</p>
<p>Try providing training through GitHub repositories with testing best practices.</p>
<pre><code class="lang-plaintext">markdownCopy code# Automated Testing Guide

## Steps:
1. Clone repo
2. Install dependencies: `npm install`
3. Run tests: `npm test`
4. Review TestRail dashboard
</code></pre>
<h3 id="heading-adapt-to-time-zones"><strong>Adapt to Time Zones</strong></h3>
<p>Organize workflows and shifts that allow for continuous testing, helping teams overcome time zone challenges.</p>
<p>You can schedule tests using <strong>Jenkins</strong>, for example, to accommodate global teams.</p>
<pre><code class="lang-plaintext">groovyCopy codepipeline {
    agent any
    triggers {
        cron('H 0 * * *')
    }
    stages {
        stage('Run Tests') {
            steps {
                sh 'npm test'
            }
        }
    }
}
</code></pre>
<h2 id="heading-how-to-integrate-testops-into-global-devops-pipelines">How to Integrate TestOps into Global DevOps Pipelines</h2>
<p>Integrating TestOps into global DevOps pipelines is crucial for maintaining software quality across distributed teams. This integration makes testing a seamless and automated part of the software delivery process, helping improve and release software quickly.</p>
<p>Tools like containerization and orchestration platforms play a big role in scaling TestOps across global pipelines. Here’s how to do it effectively:</p>
<h3 id="heading-test-early-and-continuously"><strong>Test Early and Continuously</strong></h3>
<p>When you start testing early in the development cycle, you catch issues before they reach production. This early approach allows developers to fix bugs while the changes are still fresh. It also prevents those issues from becoming bigger later.</p>
<p>Continuous testing means tests run automatically whenever code changes. These are usually triggered during the Continuous Integration (CI) process. Since the tests run right after a change is made, feedback is quick.</p>
<p>This quick feedback helps reduce debugging time. It also supports teams working from different regions, since they can move ahead without waiting on others. And because tests fail fast, blockers are identified early and resolved quickly.</p>
<p><strong>Example:</strong><br>A global logistics company uses GitHub Actions to run unit and integration tests whenever a developer submits a pull request. The setup alerts developers immediately if any test fails. Since the teams are based in India, the US, and Germany, this system helps them work independently. It also avoids delays that often happen due to time zone differences.</p>
<h3 id="heading-automate-test-execution"><strong>Automate Test Execution</strong></h3>
<p>Using test automation frameworks lets you run tests automatically across different stages of development. Tools like TestNG, Playwright, and Cypress can help you do this easily. These tools are great for saving time and reducing human error.</p>
<p>By automating the test process, you avoid the need for manual execution. This also makes regression testing more manageable, especially in large applications. It gives your team more confidence to release code frequently.</p>
<p>As tests run on every code change, any new issues are quickly caught. And because automation supports repeatability, it keeps testing consistent across teams.</p>
<p><strong>Example:</strong><br>A healthcare software company uses Cypress for automating UI tests. These tests are connected with GitLab CI and run whenever someone updates a feature branch. The tests run in parallel containers, which helps speed up the process. This setup ensures key features are always verified before merging code. Even when several features are being developed at once, their system keeps everything on track.</p>
<p><strong>Sample Cypress Test:</strong></p>
<pre><code class="lang-javascript">javascriptCopy codedescribe(<span class="hljs-string">'Login Functionality'</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">'should log in with valid credentials'</span>, <span class="hljs-function">() =&gt;</span> {
    cy.visit(<span class="hljs-string">'https://app.healthcare-demo.com/login'</span>)
    cy.get(<span class="hljs-string">'input[name=email]'</span>).type(<span class="hljs-string">'testuser@demo.com'</span>)
    cy.get(<span class="hljs-string">'input[name=password]'</span>).type(<span class="hljs-string">'securePassword123'</span>)
    cy.get(<span class="hljs-string">'button[type=submit]'</span>).click()
    cy.url().should(<span class="hljs-string">'include'</span>, <span class="hljs-string">'/dashboard'</span>)
    cy.contains(<span class="hljs-string">'Welcome back'</span>)
  })
})
</code></pre>
<p><strong>GitLab CI Configuration (gitlab-ci.yml):</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">yamlCopy codestages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">test</span>

<span class="hljs-attr">cypress_tests:</span>
  <span class="hljs-attr">stage:</span> <span class="hljs-string">test</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">cypress/browsers:node-18.12.0-chrome-106</span>
  <span class="hljs-attr">script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">npx</span> <span class="hljs-string">cypress</span> <span class="hljs-string">run</span>
  <span class="hljs-attr">artifacts:</span>
    <span class="hljs-attr">when:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">cypress/videos/</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">cypress/screenshots/</span>
  <span class="hljs-attr">only:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">merge_requests</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">branches</span>
</code></pre>
<p>This code demonstrates how Cypress runs UI tests, and how GitLab CI automatically triggers those tests when a new branch is pushed or a merge request is created. It reflects the kind of scalable, repeatable test execution process that supports global software teams.</p>
<h3 id="heading-use-containerization-for-environment-consistency"><strong>Use Containerization for Environment Consistency</strong></h3>
<p>When you use Docker, you create containers that include your application and everything it needs to run. These containers can be shared and used anywhere. That means every developer and tester uses the same environment.</p>
<p>This removes the “it works on my machine” issue. It also helps create identical setups across development, staging, QA, and production. Since everyone works with the same tools and settings, there are fewer environment-related bugs.</p>
<p>With containerization, it becomes easier to test on different systems without needing to reconfigure anything. And it helps teams in different locations stay in sync.</p>
<p><strong>Example:</strong><br>A fintech startup packages its API and testing framework using Docker. These containers are then used inside Azure DevOps pipelines. The same tests run across staging, QA, and production environments. Since the containers do not change, the results are always reliable and consistent.</p>
<p><strong>Sample Dockerfile for API Testing:</strong></p>
<pre><code class="lang-dockerfile">DockerfileCopy code<span class="hljs-comment"># Use official Node.js image</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>

<span class="hljs-comment"># Set working directory</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /usr/src/app</span>

<span class="hljs-comment"># Copy package files</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./</span>

<span class="hljs-comment"># Install dependencies</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm install</span>

<span class="hljs-comment"># Copy test files and configuration</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-comment"># Run tests (can be overridden in CI/CD)</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"npm"</span>, <span class="hljs-string">"test"</span>]</span>
</code></pre>
<p><strong>Example docker-compose.yml:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-attr">yamlCopy codeversion:</span> <span class="hljs-string">'3.8'</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">api:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">fintech-api:latest</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">./api</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"3000:3000"</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">NODE_ENV=test</span>

  <span class="hljs-attr">tests:</span>
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">./tests</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">api</span>
    <span class="hljs-attr">command:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">test</span>
</code></pre>
<p>This setup builds two containers – one for the API and one for the test runner. It mirrors how the startup runs its tests inside Azure DevOps pipelines. Developers, QA, and staging environments all use the same containers, reducing variability and giving more predictable test results.</p>
<h3 id="heading-enable-scalable-test-orchestration"><strong>Enable Scalable Test Orchestration</strong></h3>
<p>When tests are run in parallel across different environments, they finish faster. Tools like Selenium Grid, and LambdaTest help you do this. These platforms allow you to test across various browsers, operating systems, and devices.</p>
<p>By running tests this way, you save time and cover more scenarios at once. This is especially useful when your product needs to work globally. It ensures users across different regions have the same experience.</p>
<p>Parallel testing also helps teams working in different time zones. While one team sleeps, another team can pick up where they left off.</p>
<p><strong>Example:</strong><br>A retail company uses LambdaTest to run regression tests every night. These tests cover Chrome, Firefox, and Safari, both on desktop and mobile. Since the tests run in parallel, the UK team finishes validation before the US team starts their workday. This keeps their pipeline flowing without delays.</p>
<p><strong>Sample Setup for Parallel Execution with Selenium Grid (Docker-based):</strong></p>
<pre><code class="lang-yaml"><span class="hljs-string">yamlCopy</span> <span class="hljs-string">code#</span> <span class="hljs-string">docker-compose.yml</span>
<span class="hljs-attr">version:</span> <span class="hljs-string">"3"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">selenium-hub:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">selenium/hub:4.18.1</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"4442:4442"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"4443:4443"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"4444:4444"</span>

  <span class="hljs-attr">chrome:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">selenium/node-chrome:4.18.1</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">selenium-hub</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">SE_EVENT_BUS_HOST=selenium-hub</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">SE_EVENT_BUS_PUBLISH_PORT=4442</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">SE_EVENT_BUS_SUBSCRIBE_PORT=4443</span>

  <span class="hljs-attr">firefox:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">selenium/node-firefox:4.18.1</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">selenium-hub</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">SE_EVENT_BUS_HOST=selenium-hub</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">SE_EVENT_BUS_PUBLISH_PORT=4442</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">SE_EVENT_BUS_SUBSCRIBE_PORT=4443</span>
</code></pre>
<p>This creates a Selenium Grid with Chrome and Firefox nodes that connect to the central hub. It allows you to distribute your tests in parallel across browsers.</p>
<p><strong>Sample Java Test (TestNG) for Running in Parallel:</strong></p>
<pre><code class="lang-java">javaCopy codepublic <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ParallelTest</span> </span>{
    WebDriver driver;

    <span class="hljs-meta">@Parameters("browser")</span>
    <span class="hljs-meta">@BeforeMethod</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">(String browser)</span> <span class="hljs-keyword">throws</span> MalformedURLException </span>{
        DesiredCapabilities capabilities = <span class="hljs-keyword">new</span> DesiredCapabilities();

        <span class="hljs-keyword">if</span> (browser.equalsIgnoreCase(<span class="hljs-string">"chrome"</span>)) {
            capabilities.setBrowserName(<span class="hljs-string">"chrome"</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (browser.equalsIgnoreCase(<span class="hljs-string">"firefox"</span>)) {
            capabilities.setBrowserName(<span class="hljs-string">"firefox"</span>);
        }

        driver = <span class="hljs-keyword">new</span> RemoteWebDriver(<span class="hljs-keyword">new</span> URL(<span class="hljs-string">"http://localhost:4444/wd/hub"</span>), capabilities);
    }

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">runTest</span><span class="hljs-params">()</span> </span>{
        driver.get(<span class="hljs-string">"https://retail-demo.com"</span>);
        Assert.assertTrue(driver.getTitle().contains(<span class="hljs-string">"Retail"</span>));
    }

    <span class="hljs-meta">@AfterMethod</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">tearDown</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">if</span> (driver != <span class="hljs-keyword">null</span>) {
            driver.quit();
        }
    }
}
</code></pre>
<p><strong>TestNG Parallel Configuration (testng.xml):</strong></p>
<pre><code class="lang-xml">xmlCopy code<span class="hljs-tag">&lt;<span class="hljs-name">suite</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"ParallelTests"</span> <span class="hljs-attr">parallel</span>=<span class="hljs-string">"tests"</span> <span class="hljs-attr">thread-count</span>=<span class="hljs-string">"2"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">test</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"ChromeTest"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">parameter</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"browser"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"chrome"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">classes</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">class</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"ParallelTest"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">classes</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">test</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">test</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"FirefoxTest"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">parameter</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"browser"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"firefox"</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">classes</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">class</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"ParallelTest"</span>/&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">classes</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">test</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">suite</span>&gt;</span>
</code></pre>
<p>This setup allows tests to run in parallel on both Chrome and Firefox, using Selenium Grid hosted locally with Docker. It mirrors how global teams can scale their test execution and speed up feedback loops.</p>
<h3 id="heading-role-of-containerization-and-orchestration-tools"><strong>Role of Containerization and Orchestration Tools</strong></h3>
<p>Tools like Docker and Kubernetes are essential for integrating TestOps into global DevOps pipelines.</p>
<ul>
<li><p><strong>Docker</strong> creates lightweight, repeatable test environments that can be quickly set up or shut down. By packaging apps and their dependencies into containers, Docker ensures tests run consistently, whether on local machines or in the cloud.</p>
</li>
<li><p><strong>Kubernetes</strong> manages the deployment and scaling of containerized apps. In TestOps, Kubernetes automates test execution across multiple containers, speeding up testing in global pipelines. It helps scale testing to handle large volumes of tests in different environments and locations.</p>
</li>
</ul>
<p>Containerization with Docker and orchestration with Kubernetes can streamline the testing process, especially for global DevOps pipelines.</p>
<h4 id="heading-step-1-docker-for-repeatable-test-environments">Step 1: Docker for Repeatable Test Environments</h4>
<p>You can use Docker to package your application along with its dependencies into a container, making it easy to run tests consistently across various environments.</p>
<h5 id="heading-example-dockerfile-for-test-environment"><strong>Example Dockerfile for Test Environment:</strong></h5>
<pre><code class="lang-dockerfile">dockerfileCopy codeFROM node:<span class="hljs-number">14</span>

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-comment"># Install dependencies</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm install</span>

<span class="hljs-comment"># Copy application code</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>

<span class="hljs-comment"># Run tests</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"npm"</span>, <span class="hljs-string">"test"</span>]</span>
</code></pre>
<p>This Dockerfile sets up a container to install dependencies and run the tests using <code>npm test</code>.</p>
<h4 id="heading-step-2-kubernetes-for-orchestration">Step 2: Kubernetes for Orchestration</h4>
<p>You can use Kubernetes to scale the test execution across multiple containers. Kubernetes can manage the deployment of containers and automatically distribute them across nodes, enabling parallel testing.</p>
<h5 id="heading-example-kubernetes-deployment-yaml"><strong>Example Kubernetes Deployment YAML:</strong></h5>
<pre><code class="lang-yaml"><span class="hljs-attr">yamlCopy codeapiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">test-deployment</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>  <span class="hljs-comment"># Number of containers to run in parallel</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">test-app</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">test-app</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test-container</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">my-app-test-env:latest</span>  <span class="hljs-comment"># Docker image built earlier</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
</code></pre>
<p>This Kubernetes deployment configuration specifies that 3 replicas (containers) of the test environment will be created and run in parallel. This helps speed up the testing process by distributing the workload.</p>
<h4 id="heading-step-3-running-tests-in-kubernetes">Step 3: Running Tests in Kubernetes</h4>
<p>Once you have the containers and Kubernetes deployment configured, you can integrate this setup into your CI/CD pipeline. Kubernetes can handle scaling the test execution, making it ideal for global pipelines where tests need to run across different environments.</p>
<h5 id="heading-example-kubernetes-command-to-deploy"><strong>Example Kubernetes Command to Deploy:</strong></h5>
<pre><code class="lang-bash">bashCopy codekubectl apply -f test-deployment.yaml
</code></pre>
<p>This command deploys the test containers to the Kubernetes cluster, ensuring that your tests run across multiple containers, in parallel, at scale.</p>
<h3 id="heading-continuous-monitoring-and-feedback"><strong>Continuous Monitoring and Feedback</strong></h3>
<p>TestOps relies on continuous monitoring to provide real-time insights into test results, performance, and system health.</p>
<p>Kubernetes helps manage testing resources and spot problems quickly. Real-time feedback from automated tests lets developers fix issues immediately, improving software quality.</p>
<h3 id="heading-cross-tool-integration"><strong>Cross-Tool Integration</strong></h3>
<p>TestOps works well with different DevOps tools, creating a smooth feedback loop. It connects test management platforms (like TestRail) with CI/CD tools (like Jenkins or GitLab CI) and uses containerized environments to run tests consistently. Kubernetes ensures testing resources scale automatically to meet the needs of global teams.</p>
<h3 id="heading-shift-left-testing"><strong>Shift-Left Testing</strong></h3>
<p>TestOps follows a <a target="_blank" href="https://www.freecodecamp.org/news/what-is-shift-left-in-software/">shift-left approach</a>, which means integrating testing earlier in the pipeline to catch issues right away. Running tests in containerized environments speeds up testing and allows teams to find problems earlier in the development process, reducing risks and improving quality.</p>
<h4 id="heading-shift-left-testing-with-docker-and-ci">Shift-Left Testing with Docker and CI</h4>
<p>Shift-left testing integrates tests early in the development pipeline to catch issues sooner. Using Docker in a CI pipeline automates test execution in a consistent environment.</p>
<h4 id="heading-dockerfile-example"><strong>Dockerfile Example:</strong></h4>
<pre><code class="lang-dockerfile">dockerfileCopy codeFROM node:<span class="hljs-number">14</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm install</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"npm"</span>, <span class="hljs-string">"test"</span>]</span>
</code></pre>
<h4 id="heading-jenkinsfile-example"><strong>Jenkinsfile Example:</strong></h4>
<pre><code class="lang-plaintext">groovyCopy codepipeline {
    agent any
    environment {
        DOCKER_IMAGE = 'my-app-test-env'
    }
    stages {
        stage('Checkout') {
            steps {
                git 'https://github.com/your-repository/my-app.git'
            }
        }
        stage('Build Docker Image') {
            steps {
                script {
                    sh 'docker build -t $DOCKER_IMAGE .'
                }
            }
        }
        stage('Run Tests') {
            steps {
                script {
                    sh 'docker run --rm $DOCKER_IMAGE'
                }
            }
        }
    }
    post {
        always {
            sh 'docker rmi $DOCKER_IMAGE'
        }
    }
}
</code></pre>
<h3 id="heading-scalability-and-flexibility"><strong>Scalability and Flexibility</strong></h3>
<p>Global teams need to manage large test environments. Containerization and Kubernetes provide the scalability needed to run thousands of tests across different regions at once. Containers package tests into small, isolated environments, while Kubernetes automates their scaling and management, keeping testing efficient as the pipeline grows.</p>
<h2 id="heading-how-to-use-ai-and-analytics-in-testops">How to Use AI and Analytics in TestOps</h2>
<p>When you bring AI and analytics into TestOps, it helps simplify complex testing tasks. It reduces manual work, improves accuracy, and supports better decision-making. Since DevOps teams often work across regions, this becomes even more important.</p>
<p>AI helps reduce repetitive testing tasks, while analytics turns test data into clear insights. Together, they create smarter and faster testing pipelines. And because these pipelines are shared globally, consistency is key.</p>
<h3 id="heading-what-tools-can-you-use">What Tools Can You Use?</h3>
<p>There are different tools that support AI and analytics in TestOps. Some focus on automation with intelligence, while others give you clear visibility into your test data.</p>
<p>AI-powered testing tools like Mabl, Testim, and Functionize use machine learning. These tools help create, run, and even repair test cases when the app changes. Since applications change frequently, these tools help keep your tests up to date.</p>
<p>They also save time on maintenance, since the tests adjust themselves when needed. And because the tools learn from patterns, they help teams catch issues faster.</p>
<p>Analytics and observability platforms such as TestRail Analytics, Xray, Grafana, and Kibana focus on trends. They turn raw test results into visual dashboards and alerts.</p>
<p>These platforms connect with CI/CD tools, so you get real-time updates on test quality. This makes it easier for teams to stay on top of what matters, even when they are spread across locations.</p>
<h4 id="heading-example-running-a-working-test-on-lambdatest">Example – Running a Working Test on LambdaTest</h4>
<p>LambdaTest lets you execute real browser tests in the cloud, making it easy to scale your testing across browsers and OS combinations. Here is a working example using Python and Selenium, which opens a page, checks the title, and closes the browser:</p>
<pre><code class="lang-python">pythonCopy codefrom selenium <span class="hljs-keyword">import</span> webdriver
<span class="hljs-keyword">from</span> selenium.webdriver.common.by <span class="hljs-keyword">import</span> By

<span class="hljs-comment"># Define LambdaTest capabilities</span>
capabilities = {
  <span class="hljs-string">"browserName"</span>: <span class="hljs-string">"Chrome"</span>,
  <span class="hljs-string">"browserVersion"</span>: <span class="hljs-string">"latest"</span>,
  <span class="hljs-string">"LT:Options"</span>: {
    <span class="hljs-string">"platformName"</span>: <span class="hljs-string">"Windows 11"</span>,
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"TestOps Working Demo"</span>,
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"Title Verification Test"</span>,
    <span class="hljs-string">"selenium_version"</span>: <span class="hljs-string">"4.8.0"</span>,
    <span class="hljs-string">"w3c"</span>: <span class="hljs-literal">True</span>
  }
}

<span class="hljs-comment"># Replace with your LambdaTest username and access key</span>
USERNAME = <span class="hljs-string">"your_username"</span>
ACCESS_KEY = <span class="hljs-string">"your_access_key"</span>

<span class="hljs-comment"># Connect to LambdaTest cloud grid</span>
driver = webdriver.Remote(
    command_executor=<span class="hljs-string">f"https://<span class="hljs-subst">{USERNAME}</span>:<span class="hljs-subst">{ACCESS_KEY}</span>@hub.lambdatest.com/wd/hub"</span>,
    desired_capabilities=capabilities
)

<span class="hljs-keyword">try</span>:
    <span class="hljs-comment"># Step 1: Navigate to the app under test</span>
    driver.get(<span class="hljs-string">"https://www.lambdatest.com/selenium-playground/"</span>)

    <span class="hljs-comment"># Step 2: Interact with the page (click a link)</span>
    driver.find_element(By.LINK_TEXT, <span class="hljs-string">"Simple Form Demo"</span>).click()

    <span class="hljs-comment"># Step 3: Enter message and verify output</span>
    message_box = driver.find_element(By.ID, <span class="hljs-string">"user-message"</span>)
    message_box.send_keys(<span class="hljs-string">"TestOps in action!"</span>)
    driver.find_element(By.ID, <span class="hljs-string">"showInput"</span>).click()

    output = driver.find_element(By.ID, <span class="hljs-string">"message"</span>).text
    <span class="hljs-keyword">assert</span> output == <span class="hljs-string">"TestOps in action!"</span>, <span class="hljs-string">"Message output did not match input."</span>

    print(<span class="hljs-string">"✅ Test Passed: Message displayed correctly."</span>)
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">"❌ Test Failed:"</span>, e)
<span class="hljs-keyword">finally</span>:
    driver.quit()
</code></pre>
<p>What this test does:</p>
<ul>
<li><p>Launches a browser on LambdaTest cloud</p>
</li>
<li><p>Navigates to their Selenium Playground</p>
</li>
<li><p>Fills out a form and clicks a button</p>
</li>
<li><p>Verifies that the output matches the input</p>
</li>
<li><p>Logs the result and closes the session</p>
</li>
</ul>
<p>Once the test finishes, you can view detailed logs, screenshots, and video recordings on the LambdaTest Automation Dashboard, which also includes AI-based debugging info.</p>
<h3 id="heading-what-types-of-problems-can-ml-help-solve-and-how">What Types of Problems Can ML Help Solve — and How?</h3>
<p>Machine learning can solve several pain points in testing. It looks at patterns in your data and helps identify things that manual checks might miss.</p>
<h4 id="heading-flaky-tests"><strong>Flaky Tests:</strong></h4>
<p>ML helps detect tests that pass and fail randomly across different builds. It finds patterns in those failures and flags the ones that are unstable. And by doing this early, it prevents teams from wasting time chasing false bugs.</p>
<h4 id="heading-test-prioritization"><strong>Test Prioritization:</strong></h4>
<p>ML studies your past test results and recent code changes. It then ranks your tests based on risk and importance. So, the most critical ones run first. This way, your pipeline moves faster without skipping key checks.</p>
<h4 id="heading-failure-prediction"><strong>Failure Prediction:</strong></h4>
<p>ML uses logs, crash reports, and previous outcomes to predict where failures may happen. If it finds something risky, it warns the team in advance. This gives them time to fix problems before they grow bigger.</p>
<h4 id="heading-root-cause-clustering"><strong>Root Cause Clustering:</strong></h4>
<p>When many tests fail at once, ML groups them by shared failure reasons. It helps you understand whether the issue is with one module or across several. That means your team can solve the actual root problem quicker.</p>
<h4 id="heading-anomaly-detection"><strong>Anomaly Detection:</strong></h4>
<p>ML tracks things like test duration and system behavior. If something suddenly changes, like a test taking too long or using too much memory, it flags it. These alerts help teams spot performance dips early.</p>
<h3 id="heading-what-types-of-analytics-tools-can-you-use">What Types of Analytics Tools Can You Use?</h3>
<p>Analytics tools help you turn your test results into useful information. They highlight patterns, gaps, and areas that need your attention. And because these insights are visual, they are easier to act on.</p>
<p>These tools can show how your pass/fail rates have changed over time. They also help you check which parts of your app are not covered by tests. If some tests are being skipped or are too flaky, the tools will highlight that too.</p>
<p>They also measure how long tests take to run and where your pipeline slows down. This helps teams reduce bottlenecks and improve efficiency.</p>
<p>Some platforms include dashboards that link test quality to deployment status. This gives you a clear picture of whether your product is ready for release.</p>
<p>They also track failures by environment—like which browsers or regions face more issues. This helps teams debug faster and improve global reliability.</p>
<p>All of these insights help QA and DevOps teams improve their strategies. They let you remove unnecessary tests, fix flaky ones, and focus where testing matters the most.</p>
<h3 id="heading-example-using-grafana-and-kibana-for-test-analytics"><strong>Example – Using Grafana and Kibana for Test Analytics</strong></h3>
<p>Analytics tools help your teams understand test trends, flakiness, coverage gaps, and slowdowns in the CI/CD pipeline. Here is how you can set them up to actually <em>work</em> with your test data.</p>
<h4 id="heading-example-1-visualizing-test-results-in-grafana-using-influxdb">Example 1: Visualizing Test Results in Grafana using InfluxDB</h4>
<p>Grafana is commonly paired with InfluxDB to display metrics such as pass/fail rates, test durations, and failure frequencies. Here’s how you can push your test results into InfluxDB and visualize them in Grafana.</p>
<h5 id="heading-step-by-step-setup"><strong>Step-by-step setup:</strong></h5>
<ol>
<li><p>Push test results to InfluxDB after each test run. This can be done from Jenkins, GitHub Actions, or any test automation framework that generates test results.</p>
</li>
<li><p>Query and visualize data in Grafana using InfluxDB as the data source.</p>
</li>
</ol>
<h5 id="heading-python-script-to-send-test-metrics-to-influxdb"><strong>Python script to send test metrics to InfluxDB:</strong></h5>
<pre><code class="lang-python">pythonCopy codefrom influxdb <span class="hljs-keyword">import</span> InfluxDBClient
<span class="hljs-keyword">import</span> time

<span class="hljs-comment"># Create an InfluxDB client to send data</span>
client = InfluxDBClient(host=<span class="hljs-string">'localhost'</span>, port=<span class="hljs-number">8086</span>)
client.switch_database(<span class="hljs-string">'test_metrics'</span>)  <span class="hljs-comment"># Switch to your specific database</span>

<span class="hljs-comment"># Example test result data</span>
json_body = [
    {
        <span class="hljs-string">"measurement"</span>: <span class="hljs-string">"test_results"</span>,
        <span class="hljs-string">"tags"</span>: {
            <span class="hljs-string">"test_suite"</span>: <span class="hljs-string">"login_tests"</span>,  <span class="hljs-comment"># Name of the test suite</span>
            <span class="hljs-string">"environment"</span>: <span class="hljs-string">"staging"</span>  <span class="hljs-comment"># Environment like 'production', 'staging', etc.</span>
        },
        <span class="hljs-string">"time"</span>: time.strftime(<span class="hljs-string">'%Y-%m-%dT%H:%M:%SZ'</span>),  <span class="hljs-comment"># Timestamp for the test execution</span>
        <span class="hljs-string">"fields"</span>: {
            <span class="hljs-string">"pass"</span>: <span class="hljs-number">10</span>,        <span class="hljs-comment"># Number of tests that passed</span>
            <span class="hljs-string">"fail"</span>: <span class="hljs-number">2</span>,         <span class="hljs-comment"># Number of tests that failed</span>
            <span class="hljs-string">"skipped"</span>: <span class="hljs-number">1</span>,      <span class="hljs-comment"># Number of tests that were skipped</span>
            <span class="hljs-string">"duration"</span>: <span class="hljs-number">12.5</span>   <span class="hljs-comment"># Duration of the test run in seconds</span>
        }
    }
]

<span class="hljs-comment"># Write the data points to InfluxDB</span>
client.write_points(json_body)
</code></pre>
<h5 id="heading-grafana-setup">Grafana setup:</h5>
<ul>
<li><p><strong>Data Source</strong>: In Grafana, connect to your InfluxDB instance.</p>
</li>
<li><p><strong>Dashboard</strong>: Create a dashboard that queries the <code>test_results</code> measurement and display:</p>
<ul>
<li><p>Line charts for pass/fail trends over time.</p>
</li>
<li><p>Pie charts for distribution of test results.</p>
</li>
<li><p>Table showing tests and their durations.</p>
</li>
</ul>
</li>
</ul>
<p>This approach helps you track key metrics and trends for each test suite and environment.</p>
<h4 id="heading-example-2-debugging-failures-by-environment-with-kibana-and-elasticsearch">Example 2: Debugging Failures by Environment with Kibana and Elasticsearch</h4>
<p>If your testing framework logs results into Elasticsearch, you can use Kibana to analyze and visualize those logs. For example, you can track which browsers or regions are facing more issues and display the results in Kibana.</p>
<h5 id="heading-elasticsearch-data-model"><strong>Elasticsearch Data Model:</strong></h5>
<p>First, let’s assume that test results are logged in Elasticsearch with the following format:</p>
<pre><code class="lang-json">jsonCopy code{
  <span class="hljs-attr">"timestamp"</span>: <span class="hljs-string">"2025-04-16T14:00:00Z"</span>,
  <span class="hljs-attr">"test_name"</span>: <span class="hljs-string">"checkout_flow_mobile"</span>,  # Name of the test
  <span class="hljs-attr">"status"</span>: <span class="hljs-string">"fail"</span>,  # Pass or fail status
  <span class="hljs-attr">"browser"</span>: <span class="hljs-string">"Safari"</span>,  # Browser used for the test
  <span class="hljs-attr">"region"</span>: <span class="hljs-string">"EU-West"</span>,  # Region where the test was run
  <span class="hljs-attr">"error"</span>: <span class="hljs-string">"Element not visible"</span>,  # Error message in case of failure
  <span class="hljs-attr">"duration"</span>: <span class="hljs-number">9.8</span>,  # Duration of the test in seconds
  <span class="hljs-attr">"env"</span>: <span class="hljs-string">"QA"</span>  # Environment where the test ran
}
</code></pre>
<h5 id="heading-kibana-setup"><strong>Kibana setup:</strong></h5>
<ol>
<li><p><strong>Data Ingestion</strong>: Your CI pipeline or test scripts push results to Elasticsearch after each run.</p>
</li>
<li><p><strong>Create Visualizations</strong>: In Kibana, create visualizations like:</p>
<ul>
<li><p><strong>Pie Chart</strong>: Show failure rates by browser type (e.g., Chrome, Firefox, Safari).</p>
</li>
<li><p><strong>Line Chart</strong>: Track test durations over time for a specific test suite.</p>
</li>
<li><p><strong>Table</strong>: Display flaky tests that fail repeatedly by region or environment.</p>
</li>
</ul>
</li>
</ol>
<p>Example of a Kibana query that you might use to filter failures by browser:</p>
<pre><code class="lang-json">jsonCopy code{
  <span class="hljs-attr">"query"</span>: {
    <span class="hljs-attr">"bool"</span>: {
      <span class="hljs-attr">"must"</span>: [
        { <span class="hljs-attr">"match"</span>: { <span class="hljs-attr">"status"</span>: <span class="hljs-string">"fail"</span> }},
        { <span class="hljs-attr">"match"</span>: { <span class="hljs-attr">"browser"</span>: <span class="hljs-string">"Safari"</span> }}
      ]
    }
  }
}
</code></pre>
<p>This will show all test failures in Safari, helping you identify browser-specific issues.</p>
<h3 id="heading-why-this-matters"><strong>Why This Matters</strong></h3>
<p>Using Grafana and Kibana with your test results helps your team gain valuable insights:</p>
<ul>
<li><p>Identify flaky tests and prioritize them for maintenance.</p>
</li>
<li><p>Track performance trends, including test duration and failure rates.</p>
</li>
<li><p>Debug faster by identifying failure patterns tied to specific browsers, environments, or regions.</p>
</li>
</ul>
<p>With these analytics in place, teams can make data-driven decisions to improve test coverage, reduce bottlenecks, and ensure better product quality.</p>
<h2 id="heading-future-of-testops-in-global-development">Future of TestOps in Global Development</h2>
<p>As global software delivery keeps growing more complex, TestOps is changing fast. It is no longer just a supporting function. Instead, it is becoming a central part of DevOps strategies. And as development moves faster, this shift is only expected to continue.</p>
<p>Here are some of the key changes that are shaping the future of TestOps. You will need to watch for them and prepare ahead.</p>
<h3 id="heading-ai-driven-decision-making-in-testops">AI-Driven Decision-Making in TestOps</h3>
<p>AI in TestOps is moving beyond just automating test cases or running scripts. It is starting to take on decision-making roles within the testing process. For instance, orchestration tools powered by AI will soon go further.</p>
<p>They will decide which tests should run, based on recent code changes and how those changes affect the business. They will also predict which parts of the system are more likely to break in the next release.</p>
<p>And as these tools learn from real-time usage patterns, they will suggest ways to improve your test strategy. That means TestOps professionals will not only need to know how to use these tools. They will also need to understand how to read the insights and make smart choices with them.</p>
<h3 id="heading-cloud-native-and-edge-testing">Cloud-Native and Edge Testing</h3>
<p>While teams are already embracing cloud-based testing, the next step is even more distributed. This is where edge-aware TestOps comes in. It focuses on testing software where it is used, not just where it is built.</p>
<p>That means tests will run closer to the user, in specific networks or regional setups. It will also mean checking how systems perform in places with different latency or unreliable connectivity.</p>
<p>And as data laws vary across regions, teams will need to manage test data carefully and securely. Because TestOps now stretches across countries and cloud platforms, it must adapt to decentralized architectures.</p>
<h3 id="heading-testops-as-a-unifier-of-observability-and-automation">TestOps as a Unifier of Observability and Automation</h3>
<p>TestOps will not only be about running tests anymore. It will play a larger role in bringing together testing, monitoring, and automation across the pipeline. This will create a more complete view of system health and product quality.</p>
<p>Tools under TestOps will start using production monitoring data to improve test design. If something fails in production, it can guide what should be tested next.</p>
<p>Real-time behavior from users can even trigger specific regression tests. This helps teams fix issues faster and smarter. As a result, TestOps will create a feedback loop between pre-release and post-release stages.</p>
<p>That means teams will not only rely on TestOps for test automation. They will also use it to see how everything connects—from development to operations.</p>
<h3 id="heading-skill-shifts-in-testops-teams">Skill Shifts in TestOps Teams</h3>
<p>As testing tools become more advanced, the skills that TestOps teams need will change too. There will be less demand for manual testing roles. But there will be more need for engineers who think strategically about quality.</p>
<p>You will see more roles focused on site reliability, automation frameworks, and test strategy. These roles will require knowledge of cloud infrastructure and continuous delivery.</p>
<p>And instead of working in silos, testers will work closely with developers and ops teams. This shift will require people who can think across functions and understand how everything fits together.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Scaling TestOps for global software development teams is essential in today’s fast-moving, distributed work environment. By using best practices like standardizing tools, automating tests, promoting collaboration, and taking advantage of cloud and AI solutions, teams can ensure smooth, high-quality software delivery across different regions and time zones.</p>
<p>As TestOps evolves with advances in automation, AI, and cloud technology, it will make the testing process even more efficient. Teams will be able to respond faster, predict problems before they happen, and maintain high-quality standards.</p>
<p>The future of TestOps looks even more promising with smarter tools, better collaboration, and more automation, driving success for global development teams and improving the entire software development process.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use AI to Automate Unit Testing with TestGen-LLM and Cover-Agent ]]>
                </title>
                <description>
                    <![CDATA[ It's important to write clear and efficient unit tests that actually work during the software development process. Unit tests separate out individual code elements and confirm that they work as intended. Effective unit tests not only catch errors but... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/automated-unit-testing-with-testgen-llm-and-cover-agent/</link>
                <guid isPermaLink="false">66d4608a246e57ac83a2c7bb</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ LLM&#39;s  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwadamisi Samuel ]]>
                </dc:creator>
                <pubDate>Mon, 03 Jun 2024 14:45:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/06/Final-X3-Cover-FFC-Cover-Agent.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>It's important to write clear and efficient unit tests that actually work during the software development process. Unit tests separate out individual code elements and confirm that they work as intended.</p>
<p>Effective unit tests not only catch errors but also help you be confident that your code can be maintained and is dependable. But it takes time and resources to manually create an extensive suite of unit tests.</p>
<p>There have been some recent developments in artificial intelligence that promise to help automate the unit test development processes. In February, researchers at Meta released a paper on <a target="_blank" href="https://arxiv.org/abs/2402.09171">Automated Unit Test Improvement using Large Language Models</a>. This introduced an innovative method for automating unit testing.</p>
<p>Their research focuses on a new tool called <code>TestGen-LLM</code>, which explores the possibilities of using LLMs to analyze already-existing unit tests and improving on them to increase code coverage.</p>
<p>Although the code for the TestGen-LLM was not released, I will introduce an open-source alternative inspired by their research in this article. You'll learn how it generates test suites, why it's better than most LLMs, and where to get your hands on this technology and start trying it out.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-metas-testgen-llm">Meta’s TestGen-LLM</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-testgen-llm-work">How does TestGen-LLM work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-open-source-implementation-cover-agent">Open-Source Implementation (Cover-Agent)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-cover-agent-work">How does Cover-Agent work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-cover-agent">How to Use Cover-Agent</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benefits-of-the-open-source-cover-agent">Benefits of the Open-Source Cover-Agent</a></p>
</li>
<li><p><a class="post-section-overview" href="#how-can-you-contribute-to-this-technology">How can you Contribute to this Technology?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-metas-testgen-llm">Meta’s TestGen-LLM</h2>
<p>Meta's TestGen-LLM tackles the time-consuming task of unit test writing by leveraging the power of Large Language Models (LLMs). General-purpose LLMs like Gemini or ChatGPT might struggle with the specific domain of unit test code, testing syntax, and generating tests that don't add value. But TestGen-LLM is specifically tailored for unit testing.</p>
<p>This specialization allows it to understand the intricacies of code structure and test logic, leading to more targeted test suites and generate tests that actually add value and increase code coverage.</p>
<p>TestGen-LLM is able to evaluate unit tests and identify areas for improvements. It achieves this through its understanding of common testing patterns which it has been trained with. But generating tests alone is insufficient for proper code coverage.</p>
<p>Meta's researchers implemented safeguards within TestGen-LLM to ensure the effectiveness of the tests it writes. These safeguards, referred to as <code>filters</code>, act as a quality control mechanism. They eliminate suggestions that:</p>
<ul>
<li><p>wouldn't compile</p>
</li>
<li><p>fail consistently, or</p>
</li>
<li><p>fail to actually improve code coverage (suggestions that are already covered by other tests).</p>
</li>
</ul>
<h2 id="heading-how-does-testgen-llm-work">How Does TestGen-LLM Work?</h2>
<p>TestGen-LLM uses an approach called "Assured LLM-based Software Engineering" (Assured LLMSE). TestGen-LLM simply augments an existing test class with additional test cases, retaining all existing test cases and thereby guaranteeing that there will be no regression.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/img-testgen-llm-paper.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Test generation workflow(</em><a target="_blank" href="https://arxiv.org/abs/2402.09171"><em>From TestGen_LLM paper</em></a><em>)</em></p>
<p>The TestGen-LLM generates a bunch of tests, then filters out the tests that don’t run and drops any that don’t pass. Finally, it discards those that don't increase the code coverage.</p>
<p>After using the TestGen-LLM to automate a test suite, Meta used a human reviewer to accept or reject tests where the generated tests had an acceptance rate of 73% in their best reported cases.</p>
<p>According to the paper, TestGen-LLM generates a single test on each run that is then added to an existing test suite which was written previously by a developer. But it doesn’t necessarily generate tests for any given test suite.</p>
<p>The effectiveness of TestGen-LLM was demonstrated at Meta's internal test-a-thons. Here, the tool was used to analyze existing test suites and suggest improvements. The results were promising:</p>
<blockquote>
<p><em>“75% of TestGen-LLM's test cases built correctly, 57% passed reliably, and 25% increased coverage. During Meta's Instagram and Facebook test-a-thons, it improved 11.5% of all classes to which it was applied, with 73% of its recommendations being accepted for production deployment by Meta software engineers”</em>.</p>
</blockquote>
<p>Also, the recommendations from TestGen-LLM were deemed useful and relevant by the developers who took part in the test-a-thons.</p>
<h2 id="heading-open-source-implementation-cover-agent">Open-Source Implementation (Cover-Agent)</h2>
<p>The TestGen-LLM research from Meta has a lot of potential to change unit testing and automated test generation. The tool will likely help improve code coverage and speed up test creation by utilizing LLMs particularly trained on code. But this technology is not available for use by just anyone, since the code for the TestGen-LLM was not released.</p>
<p>Developers who have taken an interest in this technology are probably frustrated by the lack of publicly available code. After all, Meta's TestGen-LLM study provides a glimpse into the future of what automated testing can be.</p>
<p>It's quite appealing to be able to dive into the newest technology’s internal workings, comprehend its decision-making procedures, and maybe even help shape its evolution. But while the absence of Meta's code is a hindrance, there's an open-source implementation called <code>Cover-Agent</code> which can serve as a helpful alternative.</p>
<p><code>CodiumAI's Cover-Agent</code> is the first open-source implementation of an automated testing tool based on TestGen-LLM. Inspired by Meta's research, Cover-Agent is now at the forefront of developments in open-source AI-driven unit testing as a result.</p>
<h3 id="heading-why-are-specific-testing-focused-llms-necessary">Why are specific testing-focused LLMs necessary?</h3>
<p>Since most LLMs (like ChatGPT and Gemini) are capable of generating tests, then why bother with a new technology?</p>
<p>Well, Cover-Agent and TestGen-LLM were created to be the next step in the evolution of efficient unit testing. Their aim is to avoid common pitfalls that developers run into when generating tests with LLMs such as:</p>
<ul>
<li><p>LLM Hallucination</p>
</li>
<li><p>Generating tests that do not add value</p>
</li>
<li><p>Generating tests that omit some parts of the code, resulting in low code coverage</p>
</li>
</ul>
<p>To overcome such challenges (specifically for regression unit tests) the TestGen-LLM researchers came up with the following criteria which the generated tests must meet before the test can be accepted:</p>
<ul>
<li><p>Does the generated test compile and run properly?</p>
</li>
<li><p>Does the test increase code coverage?</p>
</li>
<li><p>Does it add value?</p>
</li>
<li><p>Does it meet any additional requirements that we may have?</p>
</li>
</ul>
<p>These are fundamental questions and issues that the generated test must solve before it is deemed an upgrade on the existing technology. Cover-Agent provides tests that answer these questions to an amazingly high degree.</p>
<h2 id="heading-how-does-cover-agent-work">How Does Cover-Agent Work?</h2>
<p>Cover-Agent is part of a broader suite of utilities designed to automate the creation of unit tests for software projects. Utilizing the TestGen-LLM Generative AI model, it aims to simplify and expedite the testing process, ensuring high-quality software development.</p>
<p>The system is made up of several components:</p>
<ul>
<li><p><strong>Test Runner</strong>: Executes the command or scripts to run the test suite and generate code coverage reports.</p>
</li>
<li><p><strong>Coverage Parser:</strong> Validates that code coverage increases as tests are added, ensuring that new tests contribute to the overall test effectiveness.</p>
</li>
<li><p><strong>Prompt Builder:</strong> Gathers necessary data from the codebase and constructs the prompt to be passed to the Large Language Model (LLM).</p>
</li>
<li><p><strong>AI Caller:</strong> Interacts with the LLM to generate tests based on the prompt provided.</p>
</li>
</ul>
<p>These components work together with TestGen-LLM to generate only tests that are guaranteed to improve the existing code base.</p>
<h2 id="heading-how-to-use-cover-agent">How to Use Cover-Agent</h2>
<h3 id="heading-requirements">Requirements</h3>
<p>You need to have the following requirements before you can begin using Cover-Agent:</p>
<ul>
<li><p><code>OPENAI_API_KEY</code> set in your environment variables, which is required for calling the <code>OpenAI API</code>.</p>
</li>
<li><p>Code Coverage tool: A Cobertura XML code coverage report is required for the tool to function correctly. For example, in Python you could use <code>pytest-cov.</code> Add the <code>--cov-report=xml</code> option when running Pytest.</p>
</li>
</ul>
<h3 id="heading-installation">Installation</h3>
<p>If you're running Cover-Agent directly from the repository, you will also need:</p>
<ul>
<li><p>Python installed on your system.</p>
</li>
<li><p>Poetry installed for managing Python package dependencies. You can find installation instructions for Poetry <a target="_blank" href="https://python-poetry.org/docs/">here</a>.</p>
</li>
</ul>
<h4 id="heading-standalone-runtime">Standalone Runtime</h4>
<p>You can install Cover-Agent as a Python Pip package or run it as a standalone executable.</p>
<h4 id="heading-python-pip">Python Pip</h4>
<p>To install the Python Pip package directly via GitHub, run the following command:</p>
<pre><code class="lang-py">pip install git+https://github.com/Codium-ai/cover-agent.git
</code></pre>
<h4 id="heading-binary">Binary</h4>
<p>You can run the binary without any Python environment installed on your system (for example, within a Docker container that does not contain Python). You can download the release for your system by navigating to the project's <a target="_blank" href="https://github.com/Codium-ai/cover-agent/releases">release page</a>.</p>
<h4 id="heading-repository-setup">Repository Setup</h4>
<p>Run the following command to install all the dependencies and run the project from source:</p>
<pre><code class="lang-py">poetry install
</code></pre>
<h4 id="heading-running-the-code">Running the Code</h4>
<p>After downloading the executable or installing the Pip package, you can now run Cover-Agent to generate and validate unit tests.</p>
<p>Execute it from the command line by using the following command:</p>
<pre><code class="lang-py">cover-agent \
--source-file-path <span class="hljs-string">"path_to_source_file"</span> \
--test-file-path <span class="hljs-string">"path_to_test_file"</span> \
--code-coverage-report-path <span class="hljs-string">"path_to_coverage_report.xml"</span> \
--test-command <span class="hljs-string">"test_command_to_run"</span> \
--test-command-dir <span class="hljs-string">"directory_to_run_test_command/"</span> \
--coverage-type <span class="hljs-string">"type_of_coverage_report"</span> \
--desired-coverage <span class="hljs-string">"desired_coverage_between_0_and_100"</span> \
--max-iterations <span class="hljs-string">"max_number_of_llm_iterations"</span> \
 --included-files <span class="hljs-string">"&lt;optional_list_of_files_to_include&gt;"</span>
</code></pre>
<p>You can use the example projects within this repository to run this code as a test.</p>
<h4 id="heading-command-arguments">Command Arguments</h4>
<ul>
<li><p><strong>source-file-path:</strong> Path of the file containing the functions or block of code we want to test for.</p>
</li>
<li><p><strong>test-file-path:</strong> Path of the file where the tests will be written by the agent. It’s best to create a skeleton of this file with at least one test and the necessary import statements.</p>
</li>
<li><p><strong>code-coverage-report-path:</strong> Path where the code coverage report is saved.</p>
</li>
<li><p><strong>test-command:</strong> Command to run the tests (for example pytest).</p>
</li>
<li><p><strong>test-command-dir</strong>: Directory where the test command should run. Set this to the root or the location of your main file to avoid issues with relative imports.</p>
</li>
<li><p><strong>coverage-type:</strong> Type of coverage to use. Cobertura is a good default.</p>
</li>
<li><p><strong>desired-coverage:</strong> Coverage goal. Higher is better, though 100% is often impractical.</p>
</li>
<li><p><strong>max-iterations:</strong> Number of times the agent should retry to generate test code. More iterations may lead to higher OpenAI token usage.</p>
</li>
<li><p><strong>additional-instructions:</strong> Prompts to ensure the code is written in a specific way. For example, here we specify that the code should be formatted to work within a test class.</p>
</li>
</ul>
<p>On running the command, the agent starts writing and iterating on the tests.</p>
<h3 id="heading-how-to-use-cover-agent-1">How to Use Cover-Agent</h3>
<p>It's time test out Cover-Agent. We will use a simple calculator.py app to compare the code coverage for manual and automated testing.</p>
<h4 id="heading-manual-testing">Manual Testing</h4>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add</span>(<span class="hljs-params">a, b</span>):</span>
    <span class="hljs-keyword">return</span> a + b

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">subtract</span>(<span class="hljs-params">a, b</span>):</span>
    <span class="hljs-keyword">return</span> a - b

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">multiply</span>(<span class="hljs-params">a, b</span>):</span>
    <span class="hljs-keyword">return</span> a * b

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">divide</span>(<span class="hljs-params">a, b</span>):</span>
    <span class="hljs-keyword">if</span> b == <span class="hljs-number">0</span>:
        <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Cannot divide by zero"</span>)
    <span class="hljs-keyword">return</span> a / b
</code></pre>
<p>This is the test_calculator.py placed in the test folder.</p>
<pre><code class="lang-py"><span class="hljs-comment"># tests/test_calculator.py</span>
<span class="hljs-keyword">from</span> calculator <span class="hljs-keyword">import</span> add, subtract, multiply, divide

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestCalculator</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_add</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">assert</span> add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>) == <span class="hljs-number">5</span>
</code></pre>
<p>To see the test coverage, we need to install <code>pytest-cov</code>, a pytest extension for coverage reporting stated earlier.</p>
<pre><code class="lang-py">pip install pytest-cov
</code></pre>
<p>Run the coverage analysis with:</p>
<pre><code class="lang-py">pytest --cov=calculator
</code></pre>
<p>The output shows:</p>
<pre><code class="lang-py">Name            Stmts   Miss  Cover
-----------------------------------
calculator.py      <span class="hljs-number">10</span>      <span class="hljs-number">5</span>    <span class="hljs-number">50</span>%
-----------------------------------
TOTAL              <span class="hljs-number">10</span>      <span class="hljs-number">5</span>    <span class="hljs-number">50</span>%
</code></pre>
<p>The output above shows that 5 of the 10 statements in calculator.py are not executed, resulting in just 50% code coverage. For a larger code base, this will become a serious issue and lead to setbacks.</p>
<p>Now let's see if cover-agent can do better.</p>
<h4 id="heading-automated-testing-with-cover-agent">Automated Testing with Cover-Agent</h4>
<p>To set up Codium's Cover-Agent, follow these steps:</p>
<p>First, install Cover-Agent:</p>
<pre><code class="lang-py">pip install git+https://github.com/Codium-ai/cover-agent.git
</code></pre>
<p>Make sure that your OPENAI_API_KEY is set in your environment variables, as it is required for the OpenAI API.</p>
<p>Next, write the commands to start generating tests in the terminal:</p>
<pre><code class="lang-py">cover-agent \
--source-file-path <span class="hljs-string">"calculator.py"</span> \
--test-file-path <span class="hljs-string">"tests/test_calculator.py"</span> \
--code-coverage-report-path <span class="hljs-string">"coverage.xml"</span> \
--test-command <span class="hljs-string">"pytest --cov=. --cov-report=xml --cov-report=term"</span> \
--test-command-dir <span class="hljs-string">"./"</span> \
--coverage-type <span class="hljs-string">"cobertura"</span> \
--desired-coverage <span class="hljs-number">80</span> \
--max-iterations <span class="hljs-number">3</span> \
--openai-model <span class="hljs-string">"gpt-4o"</span> \
--additional-instructions <span class="hljs-string">"Since I am using a test class, each line of code (including the first line) needs to be prepended with 4 whitespaces. This is extremely important to ensure that every line returned contains that 4 whitespace indent; otherwise, my code will not run."</span>
</code></pre>
<p>This generates the following code:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> pytest
<span class="hljs-keyword">from</span> calculator <span class="hljs-keyword">import</span> add, subtract, multiply, divide

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestCalculator</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_add</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">assert</span>(add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>), <span class="hljs-number">5</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_subtract</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""
        Test subtracting two numbers.
        """</span>
        <span class="hljs-keyword">assert</span> subtract(<span class="hljs-number">5</span>, <span class="hljs-number">3</span>) == <span class="hljs-number">2</span>
        <span class="hljs-keyword">assert</span> subtract(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>) == <span class="hljs-number">-2</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_multiply</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""
        Test multiplying two numbers.
        """</span>
        <span class="hljs-keyword">assert</span> multiply(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>) == <span class="hljs-number">6</span>
        <span class="hljs-keyword">assert</span> multiply(<span class="hljs-number">-2</span>, <span class="hljs-number">3</span>) == <span class="hljs-number">-6</span>
        <span class="hljs-keyword">assert</span> multiply(<span class="hljs-number">2</span>, <span class="hljs-number">-3</span>) == <span class="hljs-number">-6</span>
        <span class="hljs-keyword">assert</span> multiply(<span class="hljs-number">-2</span>, <span class="hljs-number">-3</span>) == <span class="hljs-number">6</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_divide</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""
        Test dividing two numbers.
        """</span>
        <span class="hljs-keyword">assert</span> divide(<span class="hljs-number">6</span>, <span class="hljs-number">3</span>) == <span class="hljs-number">2</span>
        <span class="hljs-keyword">assert</span> divide(<span class="hljs-number">-6</span>, <span class="hljs-number">3</span>) == <span class="hljs-number">-2</span>
        <span class="hljs-keyword">assert</span> divide(<span class="hljs-number">6</span>, <span class="hljs-number">-3</span>) == <span class="hljs-number">-2</span>
        <span class="hljs-keyword">assert</span> divide(<span class="hljs-number">-6</span>, <span class="hljs-number">-3</span>) == <span class="hljs-number">2</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_divide_by_zero</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""
        Test dividing by zero, should raise ValueError.
        """</span>
        <span class="hljs-keyword">with</span> pytest.raises(ValueError, match=<span class="hljs-string">"Cannot divide by zero"</span>):
            divide(<span class="hljs-number">5</span>, <span class="hljs-number">0</span>)
</code></pre>
<p>You can see that the agent also wrote tests that check for errors for any edge cases.</p>
<p>Now it's time to test the coverage again:</p>
<pre><code class="lang-py">pytest --cov=calculator
</code></pre>
<p>Output:</p>
<pre><code class="lang-py">Name            Stmts   Miss  Cover
-----------------------------------
calculator.py      <span class="hljs-number">10</span>      <span class="hljs-number">0</span>   <span class="hljs-number">100</span>%
-----------------------------------
TOTAL              <span class="hljs-number">10</span>      <span class="hljs-number">0</span>   <span class="hljs-number">100</span>%
</code></pre>
<p>In this example we reached 100% code coverage. For larger code bases, the procedure is relatively the same. You can read through <a target="_blank" href="https://dev.to/oluwadamisisamuel1/how-to-automate-test-generation-with-ai-using-codiumai-cover-agent-1kep">this guide</a> for a walkthrough on a larger code base.</p>
<p>While Cover-Agent represents a significant step forward, it's important to note that this technology is still in its early stages. Continued research and development are crucial for further refinement and broader adoption and codiumAI invites you to make your contributions to this open source tool.</p>
<h2 id="heading-benefits-of-the-open-source-cover-agent">Benefits of the Open Source Cover-Agent</h2>
<p>The open source nature of Cover-Agent offers several advantages that should help propel the technology forward. Among them are:</p>
<ul>
<li><p><strong>Accessibility:</strong> Its open source nature enables LLM-based testing experimentation and it is accessible to developers with varying backgroundsThis will increase the number of users and lead to the development of a better technology and more applications..</p>
</li>
<li><p><strong>Cooperation:</strong> Developers are able to make contributions, suggest enhancements, propose new features and report issues. Cover-Agent will grow and develop quickly into a project perfect for developers..</p>
</li>
<li><p><strong>Transparency:</strong> Information about the internal operations are available and this promotes trust and will ultimately increase the potential for the technology.</p>
</li>
</ul>
<p>In addition to it’s open-source advantages, Cover-Agent provides developers with a strong set of benefits of its own:</p>
<ul>
<li><p><strong>Simple Access:</strong> Developers can easily install and experiment with LLM-based testing. This allows for firsthand and immediate exploration of the technology's capabilities and with little to no disruption in their workflow.</p>
</li>
<li><p><strong>Customization for Specific Needs:</strong> Cover-Agent's open-source nature allows developers to adapt the tool to their specific project requirements. This could involve modifying the LLM model used, adjusting training data to better reflect their codebase, or integrating Cover-Agent with existing testing frameworks. This level of customization empowers developers to leverage the power of LLM-based testing in a way that aligns with their project needs.</p>
</li>
<li><p><strong>Easy Integration:</strong> It is easily integrated with VSCode (a popular code editor) which makes integration with existing workflows a breeze. You can also easily integrate it with existing human-written tests.</p>
</li>
</ul>
<h2 id="heading-how-can-you-contribute-to-cover-agent">How Can You Contribute to Cover-Agent?</h2>
<p>Cover-Agent source code is publicly available through <a target="_blank" href="https://github.com/Codium-ai/cover-agent">this GitHub repo</a>. They encourage developers of all backgrounds to test their product and make contributions to further improve and grow this new technology.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>LLM-based test improvement tools hold immense potential for revolutionizing the way developers approach unit testing. By leveraging the power of large language models specifically trained on code, these tools can streamline test creation, improve code coverage, and ultimately enhance software quality.</p>
<p>While Meta's research with TestGen-LLM offers valuable insights, the lack of publicly available code hinders wider adoption and ongoing development. Fortunately, Cover-Agent has provided a readily accessible and customizable solution. It empowers developers to experiment with LLM-based testing and contribute to its evolution.</p>
<p>The potential for the TestGen-LLM and Cover-Agent is immense, and further development through contributions by developers will lead to a revolutionary tool that will transform automated test generation forever.</p>
<ul>
<li><p><a target="_blank" href="https://arxiv.org/abs/2402.09171">Meta’s Research Paper</a></p>
</li>
<li><p><a target="_blank" href="https://www.codium.ai/blog/we-created-the-first-open-source-implementation-of-metas-testgen-llm/">Cover-Agent Blog for further study</a></p>
</li>
<li><p><a target="_blank" href="https://dev.to/oluwadamisisamuel1/how-to-automate-test-generation-with-ai-using-codiumai-cover-agent-1kep">Article for installation process and use cases</a></p>
</li>
</ul>
<p>Connect with me on <a target="_blank" href="http://www.linkedin.com/in/samuel-oluwadamisi-01b3a4236">LinkedIn</a> and <a target="_blank" href="https://twitter.com/Data_Steve_">Twitter</a> if you found this helpful.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Cypress for End-to-End Testing Your React Apps ]]>
                </title>
                <description>
                    <![CDATA[ React is a popular framework for building web applications. It's is one of the best options for frontend engineering because of its declarative approach to user interface design and component-based architecture. But it can be difficult to make sure y... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/cypress-for-end-to-end-testing-react-apps/</link>
                <guid isPermaLink="false">66d4608affe6b1f641b5fa4f</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Okosa Leonard ]]>
                </dc:creator>
                <pubDate>Wed, 01 Nov 2023 22:45:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/1_PfsGOHgjLPh3EFOkxCRmjw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>React is a popular framework for building web applications. It's is one of the best options for frontend engineering because of its declarative approach to user interface design and component-based architecture.</p>
<p>But it can be difficult to make sure your React application functions as intended in various different scenarios.</p>
<p>Cypress is an end-to-end testing framework that you can use to test your React apps. And in this tutorial, I'll explain how to create efficient end-to-end tests for your web application using Cypress and React.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-cypress">What is Cypress?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-environment">How to Set Up Your Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-your-first-cypress-test">How to Write Your First Cypress Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-interact-with-react-components">How to Interact with React Components</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cypress-queries">Cypress Queries</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cypress-assertions">Cypress Assertions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cypress-actions">Cypress Actions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-organize-and-run-your-cypress-tests">How to Organize and Run Your Cypress Tests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-plugins">Plugins</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-describe-blocks">Describe Blocks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-custom-commands">Custom Commands</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-cypress-dashboard">Cypress Dashboard</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-debug-your-code-with-cypress">How to Debug Your Code with Cypress</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-continuous-integration">Continuous Integration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-cypress">What is Cypress?</h2>
<p>Cypress is a straightforward open-source end-to-end testing framework designed for modern web development. It's based on JavaScript.</p>
<p>The tool operates within the browser, which sets it apart from other testing tools like Selenium. The concise, uncomplicated API that Cypress provides for interacting with your application makes it easy to create and manage tests.</p>
<p>You can create and manage tests that mimic user interactions with Cypress. You can also examine your web application for expected behavior and troubleshoot any concerns that arise.</p>
<h2 id="heading-how-to-set-up-your-environment">How to Set Up Your Environment</h2>
<p>Before we begin writing tests, you'll want to set up your environment. You must have Node.js and npm (Node Package Manager) installed in order to use Cypress with your React application.</p>
<p>You can visit <a target="_blank" href="https://nodejs.org/en/">https://nodejs.org/en/</a> to download to install Node.js on your computer if it isn't already installed. To get started using Cyprus after installing Node, you can follow these steps:</p>
<h3 id="heading-1-create-a-react-application">1) Create a React Application</h3>
<p>We'll utilize Vite to develop our React application, as it helps us quickly and efficiently create web apps.</p>
<p>To get started, create a React app on Vite by entering the following code in your console:</p>
<pre><code class="lang-js">npm create vite@latest
</code></pre>
<p>Vite will ask you to give your project a name. Choose whatever name you like.</p>
<p>Following that, Vite will give you the choice of framework. Just make sure you select React for this lesson.</p>
<p>It'll then ask you to select between TypeScript and JavaScript when you select React. Pick JavaScript.</p>
<p>Then go to your project directory and choose your project.</p>
<p>Finally, type <code>npm install</code> after that, and then <code>npm start dev</code> to launch your project.</p>
<p>Your project will be hosted by Vite at <code>http://localhost:5173/</code>. To view your project in a browser, click the link that's provided.</p>
<h3 id="heading-2-install-cypress">2) Install cypress</h3>
<p>You can install Cypress in your project by using this code <code>install cypress –-save-dev</code> in your terminal. This code installs Cypress in your React application.</p>
<h3 id="heading-3-initialize-cypress">3) Initialize Cypress</h3>
<p>Run <code>npx Cypress open</code> from the directory of your React project. This command should launch Cypress Test Runner, a graphical tool for creating and managing tests. You will now have two alternatives for automated testing:</p>
<ul>
<li><p>End-to-End Testing: End-to-end testing involves a browser using an automated tool like Cypress, so you can make tests consisting of functions like pressing buttons, typing in inputs, and so on. This helps make sure everything’s working perfectly.</p>
</li>
<li><p>Component Testing: This involves testing individual components instead of the entire React application.</p>
</li>
</ul>
<h4 id="heading-what-is-the-cypress-test-runner">What is the Cypress Test Runner?</h4>
<p>The test runner opens up once you pass in <code>npx cypress open</code>. It is a graphical tool for creating and managing tests, it also allows you to write and execute your test cases in Cypress.</p>
<p>After selecting what type of testing you want to do, the next section of the test runner helps you specify what script you want to execute, and gives you the option to select the browser you want to use and validate your tests as expected.</p>
<h3 id="heading-4-folder-structure">4) Folder Structure</h3>
<p>Cypress will automatically save its test files and configurations inside of your project in a subfolder named <code>cypress</code>.</p>
<h2 id="heading-how-to-write-your-first-cypress-test">How to Write Your First Cypress Test</h2>
<p>We'll use JavaScript to write our Cypress tests which are executed within the browser. Here's how to create your React application's first test.</p>
<p>Suppose we wish to check to see if the Document Object Model (DOM) displays our h2 element. To accomplish this using Cypress, take the following actions:</p>
<p><strong>Step 1</strong>: In your Cypress folder, go to the e2e file and there you’ll see a line of code similar to this:</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">'template spec'</span>, <span class="hljs-function">() =&gt;</span> {
    it(<span class="hljs-string">'passes'</span>, <span class="hljs-function">() =&gt;</span> {
        cy.visit(<span class="hljs-string">'https://example.cypress.io'</span>)
    })
    })
</code></pre>
<p><strong>Step 2</strong>: Now, in <code>cy.visit</code>, update the default http address to your React app's address (<a target="_blank" href="http://localhost:5173/">http://localhost:5173/</a>, if you recall).</p>
<p><strong>Step 3</strong>: We wish to check to see if the DOM has our h2 element. Navigate to the element you wish to test and add <code>data-testid</code> as an attribute to accomplish this. Next, assign a name to the attribute. Example: <code>&lt;h2 data-testid="cypress-h1"&gt; Leo Website &lt;/h2&gt;</code>.</p>
<p><strong>Step 4</strong>: Make a space and add this piece of code to your cypress folder to see if the attribute exists.</p>
<pre><code class="lang-js">cy.get(‘[data-testid=”cypress-header”]’).should(“exists”):
</code></pre>
<p>Since Cypress is configured to execute tests upon save, as soon as you save anything in your code editor, the test ought to instantly start in Cypress.</p>
<h2 id="heading-how-to-interact-with-react-components">How to Interact With React Components</h2>
<p>You'll want to be familiar with the vast array of commands used to imitate user interactions in order to write more insightful tests.</p>
<p>With its extensive command set for interacting with and testing online elements, Cypress is designed specifically for webpages and web applications.</p>
<p>To imitate user interactions and assert certain behaviors of the application, test scripts employ these instructions. Because Cypress maintains a promise chain on your behalf, you can chain commands together. Until the chain breaks and an error occurs, each command yields a "subject" to the following command.</p>
<p>Cypress commands can be classified into the following groups: queries, assertions, actions, and others.</p>
<h3 id="heading-cypress-queries">Cypress Queries</h3>
<p>These are Cypress commands that retrieve your application's current status. They retry as necessary to ensure that the DOM element or other data they offer is constantly current. They return a subject for additional commands to act upon or assert.</p>
<p>Here are some ways you can organize and run tests:</p>
<h4 id="heading-using-the-children-method">Using the <code>.children()</code> method</h4>
<p>To choose and work with a parent element's direct child elements in Cypress, use the <code>.children()</code> method. It enables you to access particular child elements of a given parent element by navigating the DOM tree.</p>
<p>Here are some examples of how to use <code>.children()</code> in Cypress:</p>
<p>Example 1: Assuming you have this sort HTML structure in your <code>jsx</code> file:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"parent-element"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"child"</span>&gt;</span>Child 1<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"child"</span>&gt;</span>Child 2<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"child"</span>&gt;</span>Child 3<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>To choose and work with the child elements inside the <code>.parent</code> element, use the <code>.children()</code> function like this:</p>
<pre><code class="lang-javascript">cy.get(<span class="hljs-string">'.parent'</span>).children(<span class="hljs-string">'.child'</span>).should(<span class="hljs-string">'have.length'</span>, <span class="hljs-number">3</span>);

<span class="hljs-comment">// Click on the second child element</span>
cy.get(<span class="hljs-string">'.parent'</span>).children(<span class="hljs-string">'.child'</span>).eq(<span class="hljs-number">1</span>).click();
</code></pre>
<p>Example 2: you can perform a whole range of tasks using <code>.children()</code> combined with other Cypress functions. You can, for instance, recognize certain child objects and define their properties:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Ensure that the first child element contains the text "Child 1"</span>
cy.get(<span class="hljs-string">'.parent'</span>).children(<span class="hljs-string">'.child'</span>).first().should(<span class="hljs-string">'contain'</span>, <span class="hljs-string">'Child 1'</span>);

<span class="hljs-comment">// Verify that all child elements have a specific class</span>
cy.get(<span class="hljs-string">'.parent'</span>).children(<span class="hljs-string">'.child'</span>).each(<span class="hljs-function">(<span class="hljs-params">$child</span>) =&gt;</span> {
  cy.wrap($child).should(<span class="hljs-string">'have.class'</span>, <span class="hljs-string">'child'</span>);
});
</code></pre>
<p>Example 3: Many Cypress instructions may be chained together to perform more complex interactions. Using <code>.children()</code>, you can select the parent element, select its child elements, and then operate with the child elements as demonstrated by this example:</p>
<pre><code class="lang-javascript">cy.get(<span class="hljs-string">'.parent'</span>)
  .children(<span class="hljs-string">'.child'</span>)
  .first()
  .click()
  .next()
  .type(<span class="hljs-string">'I'</span>m Leo<span class="hljs-string">');</span>
</code></pre>
<p>To interact with the next child element in this example, we first pick the <code>.parent</code> element, then use <code>.children('.child')</code> to select its child elements, click on the first child element, and then chain more instructions to type "I'm Leo."</p>
<p>These examples show you how to pick and work with child items inside of a parent element in Cypress using the <code>.children()</code> function. Depending on your unique HTML structure, you can modify the CSS selector inside <code>.children()</code> to match the child elements you wish to target.</p>
<p>Now you should have an have an idea of how <code>.children()</code> works and how you can use queries as a type of Cypress command. There are more query commands but you can always refer to the documentation on <a target="_blank" href="https://www.freecodecamp.org/news/p/ee697448-b73d-43dd-85de-d31f1e1005c6/cypress.io">cypress.io</a> to learn more on them.</p>
<h4 id="heading-using-the-cyget-command">Using the cy.get() command</h4>
<p><code>cy.get()</code> is a fundamental command in the Cypress framework. You use it to choose and perform actions on DOM elements inside a web application, including those created using React.</p>
<p>To choose one or more DOM elements from your React application for testing, use the <code>cy.get()</code> command. It lets you pick elements with CSS selectors or other Cypress-compatible methods.</p>
<p>The fundamental syntax of <code>cy.get()</code> is <code>cy.get(selector)</code>.</p>
<p><code>selector</code> is an instance of a CSS selector. There are alternative ways for choosing components based on attributes like ID, class name, or data.</p>
<p>Suppose you have a React component like this:</p>
<pre><code class="lang-javascript">&lt;button data-testid=<span class="hljs-string">"Leo-button"</span>&gt;Click&lt;/button&gt;
</code></pre>
<p>You can use <code>cy.get()</code> to select the button element:</p>
<pre><code class="lang-javascript">cy.get(‘[data-testid=”Leo-button”]’);
</code></pre>
<p><code>cy.get('#Leo-button')</code> in this case chooses the button element with ID "Leo-button."</p>
<h3 id="heading-cypress-assertions">Cypress Assertions</h3>
<p>These are Cypress commands that make assertions about the state of your application. They stop your test when they reach the time limit or the specified condition is fulfilled.</p>
<p>There are only two types of assertion commands: <code>.and()</code> and <code>.should()</code>. I'll discuss <code>.should()</code> here.</p>
<p>In Cypress, statements concerning the states of particular elements are made using the <code>.should()</code> command. It enables you to specify requirements that components must fulfill. In the event that the requirements are not satisfied, the test will fail.</p>
<h4 id="heading-fundamental-use-of-should">Fundamental Use of <code>.should()</code></h4>
<p>The basic syntax of <code>.should()</code> is as follows:</p>
<pre><code class="lang-javascript">cy.get(<span class="hljs-string">'selector'</span>).should(<span class="hljs-string">'condition'</span>, expectedValue);
</code></pre>
<ul>
<li><p>The DOM element(s) that you wish to make assertions against are chosen by <code>cy.get('selector')</code>.</p>
</li>
<li><p><code>.should('condition', expectedValue)</code> indicates what conditions and values the chosen element(s) must satisfy.</p>
</li>
</ul>
<p>You can also chain <code>.should()</code> assertions. Chained <code>.should()</code> statements are used to verify different conditions on the same element:</p>
<pre><code class="lang-javascript">cy.get(<span class="hljs-string">'input#username'</span>)
  .should(<span class="hljs-string">'be.visible'</span>)
  .should(<span class="hljs-string">'have.attr'</span>, <span class="hljs-string">'placeholder'</span>, <span class="hljs-string">'Enter your username'</span>)
</code></pre>
<p>In this example, we verify that the username input element is there and that the placeholder content is appropriate.</p>
<p><code>.should()</code> also allows you to use custom assertions by passing a callback function as the expected value:</p>
<pre><code class="lang-javascript">cy.get(<span class="hljs-string">'.my-element'</span>).should(<span class="hljs-function">(<span class="hljs-params">$element</span>) =&gt;</span> {
  expect($element).to.have.class(<span class="hljs-string">'active'</span>);
  expect($element).to.have.css(<span class="hljs-string">'color'</span>, <span class="hljs-string">'rgb(255, 0, 0)'</span>);
});
</code></pre>
<p>In this code, we're using custom assertions within <code>.should()</code> to check if the element has the class "active" and if its text color is red.</p>
<h4 id="heading-negating-assertions-with-should">Negating assertions with .should()</h4>
<p><code>.should('not.condition', expectedValue)</code> can be used to refute the following claims:</p>
<pre><code class="lang-javascript">cy.get(<span class="hljs-string">'#error-message'</span>).should(<span class="hljs-string">'not.exist'</span>);
</code></pre>
<p>This code verifies that there isn't an element with the ID "error-message".</p>
<h4 id="heading-combining-should-with-other-commands">Combining .should() with Other Commands</h4>
<p>To create testing scenarios with greater complexity, you can combine <code>.should()</code> with other Cypress commands. For instance, you can use it to assert the element's final state following a <code>.click()</code> or <code>.type()</code> command.</p>
<pre><code class="lang-javascript">cy.get(<span class="hljs-string">'button'</span>).click().should(<span class="hljs-string">'be.disabled'</span>);
cy.get(‘[data-testid=”search-input”]’).type(<span class="hljs-string">'Leonard'</span>).should(<span class="hljs-string">'have.value'</span>, <span class="hljs-string">'Leonard'</span>);
</code></pre>
<p>In these instances, we interact with the elements first (by clicking a button or entering text into an input box), and then we use the resultant states of the elements' states as determined by using <code>.should()</code>.</p>
<p>When constructing relevant assertions to confirm the behavior and state of items in your web application during end-to-end testing, Cypress's <code>.should()</code> command is a potent tool.</p>
<h3 id="heading-cypress-actions">Cypress Actions</h3>
<p>Cypress commands that interact with your program like a user would are called actions. They do not interact with the page until components or elements are actionable. There are numerous examples, but for now I'll use the <code>.click()</code> command.</p>
<p>To mimic a click on a DOM element in Cypress, use the <code>.click()</code> command. It's among the most typical and basic things you'll do in your end-to-end tests.</p>
<p>Let's look at an example of some code to demonstrate how to use <code>.click()</code>:</p>
<h4 id="heading-basic-click-usage">Basic <code>.Click()</code> Usage</h4>
<p>The following is the syntax of <code>.click()</code>:</p>
<pre><code class="lang-javascript">cy.get(<span class="hljs-string">'selector'</span>).click();
</code></pre>
<p>To choose the DOM element you want to click, use <code>cy.get('selector')</code>. <code>.click()</code> replicates a click event on the element that is chosen.</p>
<h4 id="heading-managing-clicks-during-user-interaction">Managing Clicks During User Interaction</h4>
<p>Sometimes, you may need to interact with things before clicking. For instance, you may want to complete a form field and press the submit button afterward. You can use <code>.type()</code> to communicate with input fields before continuing on to the submit button, <code>.click()</code>:</p>
<pre><code class="lang-javascript">cy.get(‘[data-testid=”username”]’).type(<span class="hljs-string">'okosaleo'</span>);
cy.get(‘[data-testid=”password”]’).type(<span class="hljs-string">'password'</span>);
cy.get(‘[data-testid=”Leo-button”]’).click();
</code></pre>
<p>First, in this case we employ <code>.type()</code> to input values in the password and username fields, and then click <code>.click()</code> to click the login button and submit the form.</p>
<h4 id="heading-managing-dynamic-elements">Managing Dynamic Elements</h4>
<p>You can utilize <code>.click()</code> while working with dynamic elements, or elements that emerge following an action. In order to make sure the element is there and prepared for interaction, you can use <code>.click()</code> in conjunction with other commands.</p>
<p>For instance, clicking a dynamic element. Assume you have a list of items and that you wish to click on a certain item that pops up following a certain amount of user interaction:</p>
<pre><code class="lang-javascript">cy.get(‘[data-testid=”Leo-button”]’).click();
cy.get(<span class="hljs-string">'.dynamic-item'</span>).should(<span class="hljs-string">'have.length.gt'</span>, <span class="hljs-number">5</span>);
cy.get(<span class="hljs-string">'.dynamic-item'</span>).eq(<span class="hljs-number">4</span>).click();
</code></pre>
<p>In this example, to load new things dynamically, we first click the <code>"Leo-button"</code> button. After making sure that at least six things are shown using <code>.should('have.length.gt', 5)</code>, we click the fifth item using <code>.eq(4)</code>.</p>
<h3 id="heading-other-commands">Other commands</h3>
<p>Cypress has many other commands which we haven't discussed here but that you can use to write other tests. You can check them out in the <a target="_blank" href="https://docs.cypress.io/api/table-of-contents">Cypress docs here</a>.</p>
<h2 id="heading-how-to-organize-and-run-your-cypress-tests">How to Organize and Run Your Cypress Tests</h2>
<p>Your application may accumulate a number of tests over time. Cypress offers several methods for setting them up and managing them effectively.</p>
<h3 id="heading-plugins">Plugins</h3>
<p>To assist with tasks such as creating code coverage reports and simulating APIs, Cypress offers a number of extensions and plugins.</p>
<p>Plugins are specially designed JavaScript modules that let you modify and add new features to the Cypress testing framework. With the use of plugins, you can increase Cypress's capabilities and customize it to suit your unique requirements.</p>
<p>Cypress plugins include:</p>
<ol>
<li><p>Integrations: Cypress may be integrated with other programs, services, and systems by the creation of plugins. Examples of these include deployment pipelines, version control systems, and platforms for continuous integration (CI).</p>
</li>
<li><p>Custom Commands: To encapsulate and reuse routine test procedures or user interactions with your application, you may define custom commands using plugins. This improves the organization and maintainability of your test code.</p>
</li>
</ol>
<p>There are many more plugin features that Cypress offers. Plugins are a flexible way to customize your testing framework to the specific needs of your project, increase the power of your testing, and organize and maintain your test code better.</p>
<h3 id="heading-describe-blocks">Describe Blocks</h3>
<p>The Mocha testing framework, which is integrated with Cypress to help you organize and structure your test suites, includes describe blocks. These describe blocks provide your tests a clear structure and help you group related test cases.</p>
<p>You can use describe blocks to categorize and explain the functionality and behavior of the program you are evaluating.</p>
<h4 id="heading-purpose-of-describe-blocks-in-cypress">Purpose of Describe Blocks in Cypress</h4>
<ul>
<li><p>Organize Test Cases: You can organize relevant test cases together by using the describe blocks. A description block can be made for a particular application feature, part, or functionality.</p>
</li>
<li><p>Enhance Test framework: They give your tests a hierarchical framework that makes the test suite easier to explore and comprehend. Developers and testers can find certain tests more quickly with the aid of this structure.</p>
</li>
<li><p>Clarity and Documentation: You can give titles for your tests that are both human-readable and descriptive by using describe blocks. This functions as a type of test documentation.</p>
</li>
</ul>
<p>A <code>describe</code> block is structured like this:</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">'Description of the Test Suite or Feature'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Your test cases (it blocks) go here</span>
});
</code></pre>
<ul>
<li><p><code>"Description of the Test Suite or Feature"</code>: This is a string that contains a description of the test suite or feature. It acts as the group of test cases title.</p>
</li>
<li><p><code>function () { /*... */ }</code>: This function holds the various test cases that are related to this <code>describe</code> block and are written within <code>it</code> blocks.</p>
</li>
</ul>
<h4 id="heading-how-to-use-describe-blocks-custom-commands">How to Use Describe Blocks Custom Commands</h4>
<p>Let's say you are evaluating a web application's login functionality. The following is how you could structure your tests using describe blocks:</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">'Login Feature'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// This is the outermost describe block for the Login Feature</span>

  it(<span class="hljs-string">'should display the login form'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Test case logic to check if the login form is displayed</span>
  });

  it(<span class="hljs-string">'should show an error message on invalid login'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Test case logic to check error message display</span>
  });

  it(<span class="hljs-string">'should log in successfully with valid credentials'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Test case logic to check successful login</span>
  });

  describe(<span class="hljs-string">'Password Reset'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Nested describe block for Password Reset functionality</span>

    it(<span class="hljs-string">'should allow users to request a password reset'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-comment">// Test case logic for password reset request</span>
    });

    it(<span class="hljs-string">'should reset the password with a valid reset token'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-comment">// Test case logic for password reset with a valid token</span>
    });
  });
});
</code></pre>
<p>In this instance:</p>
<ol>
<li><p>All of the test cases for the login feature are grouped together in the outermost describe block.</p>
</li>
<li><p>Tests pertaining to the password reset feature are grouped together in a describe block nestled within it.</p>
</li>
</ol>
<p>You can easily identify related tests thanks to this hierarchical structure, which also aids in clarifying the goals of each set of test cases.</p>
<p>Effective use of <code>describe</code> blocks in Cypress allows you to write well-structured and easily accessible test suites, which will facilitate test management and maintenance as your project expands.</p>
<h3 id="heading-custom-commands">Custom Commands</h3>
<p>To simplify repetitive tasks and improve the readability of your tests, you can create custom Cypress commands.</p>
<p>To encapsulate and reuse common test processes or interactions with the application being tested, you may develop and add custom commands—user-defined JavaScript functions—to your Cypress test suite. These can help augment the built-in Cypress commands and improve the readability, organization, and maintainability of your test code.</p>
<p>You need to define your custom command in one of your test files, usually the <code>commands.js</code> file. Often, you may find this file in the <code>cypress/support</code> directory.</p>
<p>On the other hand, you can arrange your customized commands in different files or folders according to the features of your program.</p>
<p>Here's an example:</p>
<pre><code class="lang-javascript">Cypress.Commands.add(<span class="hljs-string">'customCommandName'</span>, <span class="hljs-function">(<span class="hljs-params">arg0, arg1, ...</span>) =&gt;</span> {
  <span class="hljs-comment">// Define the custom command logic here</span>
});
</code></pre>
<ul>
<li><p><code>customCommandName</code>: This is the name that you'll use in your test scripts to identify your custom command.</p>
</li>
<li><p><code>(arg0, arg1,...)</code>: These are the parameters or arguments that you can give to the custom command in order to alter how the program behaves.</p>
</li>
<li><p><code>// Custom command logic }</code>: Here you provide the precise operations you wish to be carried out by your custom command.</p>
</li>
</ul>
<h4 id="heading-how-to-use-custom-commands">How to Use Custom Commands</h4>
<p>Once your custom command is defined, you can use it in your test scripts in the same way as any other Cypress command that is built-in.</p>
<p>Using the following syntax, you can invoke a custom command:</p>
<pre><code class="lang-javascript">cy.customCommandName(arg0, arg1, ...);
</code></pre>
<h4 id="heading-advantages-of-custom-commands">Advantages of Custom Commands</h4>
<p>Cypress custom commands have a number of advantages:</p>
<ol>
<li><p>Reusability: You can encapsulate intricate or commonly used interactions with your application using custom commands. This lessens redundancy in your test scripts and encourages the reuse of code.</p>
</li>
<li><p>Readability: Your test scripts become more accessible and understandable when typical tasks are abstracted into custom commands. Your test code will be more self-explanatory with custom commands.</p>
</li>
<li><p>Maintenance: Rather than having to look for and update each instance of an action in several test scripts, you just need to change the custom command once when you need to update a common action or interaction.</p>
</li>
<li><p>Consistency: Using custom commands guarantees that your test suite's common interactions are carried out in a consistent manner. This keeps the testing process consistent.</p>
</li>
</ol>
<p>This is a basic illustration of a custom command that allows a user to log in by filling out a login form with their username and password:</p>
<pre><code class="lang-javascript">Cypress.Commands.add(<span class="hljs-string">'login'</span>, <span class="hljs-function">(<span class="hljs-params">username, password</span>) =&gt;</span> {
  cy.get(‘[username-input”]’).type(username);
  cy.get(‘[data-testid=”password-input”]’).type(password);
  cy.get(‘[data-testid=”Leo-button”]’).click();
});
</code></pre>
<h3 id="heading-cypress-dashboard">Cypress Dashboard</h3>
<p>This tool lets you monitor films of text executors, organize test runs, and discuss outcomes with your team. You can find the Cypress dashboard in the test runner window.</p>
<p>To see the history of your test runs, click the "Runs" tab. The Runs tab will include a "View Dashboard" link at the top. When you click the "View Dashboard" link, your web browser will launch the Cypress Dashboard.</p>
<p>Cypress offers the dashboard as a web-based service. The goal of the Cypress Dashboard is to improve and streamline the web application testing and test management process. It provides a single platform to monitor, analyze, and manage your test runs using Cypress.</p>
<p>The Cypress Dashboard's main attributes and capabilities are as follows:</p>
<ol>
<li><p>Real-Time Test Execution: You can watch the test execution process in real time when you use the Cypress Dashboard service to execute Cypress tests. It offers a real-time view of your testing, including with videos and live browser screenshots. This tool is useful for tracking test progress and troubleshooting problems.</p>
</li>
<li><p>Collaboration and Sharing: The development and QA teams can work together thanks to the dashboard. Test results may be accessed and reviewed by developers, testers, and other stakeholders with ease when test runs are shared with team members.</p>
</li>
<li><p>Centralized Test Management: You can store and manage your Cypress test results centrally using the Cypress Dashboard. Test runs can be accessed and arranged from a single location.</p>
</li>
<li><p>The Cypress Dashboard facilitates the concurrent running of tests across many computers and web browsers. This can speed up the test execution process considerably and provide you the results you need more quickly.</p>
</li>
<li><p>Test Insights and Analytics: By tracking test results over time, you can learn more about your test runs. Comprehensive analytics, including execution times, pass/fail statuses, and more, are available in the Cypress Dashboard. You can use this data to find trends and patterns in your test results.</p>
</li>
<li><p>Test Automation and Scheduling: You can automate the testing process by scheduling tests to run at predetermined times or intervals. Running regression tests and maintaining ongoing test coverage can both benefit from this.</p>
</li>
</ol>
<p>These are some of the functions and attributes the Cypress dashboard offers.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/dashboard-analytics-overview.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Here's an example of what the cypress dashboard looks like</em></p>
<h2 id="heading-how-to-debug-your-code-with-cypress">How to Debug Your Code With Cypress</h2>
<p>It's simple to debug your tests with Cypress. To halt test execution and view an application's status in the browser, add <code>cy.debug()</code>. Let's look at an example of how to use <code>cy.debug</code> in Cypress:</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">'Cypress Debug Example'</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">'performs a login and checks the user profile'</span>, <span class="hljs-function">() =&gt;</span> {
    cy.visit(<span class="hljs-string">'/login'</span>);
    cy.get(<span class="hljs-string">'#username-input'</span>).type(<span class="hljs-string">'testuser'</span>);

    <span class="hljs-comment">// Pause the test execution to inspect the application state</span>
    cy.pause();

    cy.get(<span class="hljs-string">'#password-input'</span>).type(<span class="hljs-string">'password123'</span>);
    cy.get(<span class="hljs-string">'#login-button'</span>).click();

    <span class="hljs-comment">// Continue with assertions</span>
    cy.url().should(<span class="hljs-string">'include'</span>, <span class="hljs-string">'/profile'</span>);
    cy.get(<span class="hljs-string">'.user-info'</span>).should(<span class="hljs-string">'contain'</span>, <span class="hljs-string">'Welcome, testuser'</span>);
  });
});
</code></pre>
<p>In this example, the test will halt when it reaches the <code>cy.pause()</code> command, and the Cypress Test Runner will prompt you to view the DevTools console.</p>
<p>After that, you may interact with the program, examine the DOM, verify variables, and execute more Cypress commands interactively for debugging reasons via the console.</p>
<h4 id="heading-time-travel-debugging">Time Travel Debugging</h4>
<p>Cypress also has a powerful capability known as "Time Travel Debugging." This feature allows you to see the current state of your application at any time while it is being tested. This greatly simplifies the diagnosis process.</p>
<p>Using this feature, you can record and replay test execution stages to troubleshoot your Cypress tests. By giving you a visual depiction of the test execution process step-by-step, it assists you in identifying and resolving problems with your tests and application. Using Test Driven Development (TTD), you may see your application's state interactively at various stages of the test run.</p>
<p>This is how Cypress's Time Travel Debugging works:</p>
<p>You must first record a test run in order to use TTD. You may do this using the Cypress Dashboard service or by running your tests locally with the <code>--record</code> parameter.</p>
<p>This is an example of how to conduct testing while recording:</p>
<pre><code class="lang-bash">npx cypress run --record
</code></pre>
<p>The test is launched in the Electron browser by default.</p>
<p>Then you can use the Cypress Dashboard web interface once the test run is finished and the results are submitted to the Cypress Dashboard.</p>
<p>Next, find the test run you wish to debug in the Cypress Dashboard and click on it. This will direct you to the page with test specifics.</p>
<p>You may see a timeline representation of your application's state at each Cypress command execution on the test details page.</p>
<p>To see the state of the application at a certain moment in time, click on a snapshot on the timeline. You may browse console logs, work with the DOM, and even run Cypress commands in the context of that snapshot.</p>
<p>To examine the condition of the application at various test phases, you can either go to particular points in the timeline or walk through each command in the timeline one at a time.</p>
<p>This is an illustration of how to apply TTD in a Cypress test:</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">'Time Travel Debugging Example'</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">'performs a login and checks the user profile'</span>, <span class="hljs-function">() =&gt;</span> {
    cy.visit(<span class="hljs-string">'/login'</span>);
    cy.get(‘[username-input”]’).type(<span class="hljs-string">'testuser'</span>);
    cy.get(‘[password-input”]’).type(<span class="hljs-string">'password'</span>);
    cy.get(‘[login-input”]’).click();

    <span class="hljs-comment">// At this point, we may want to perform TTD to inspect the application's state</span>
    <span class="hljs-comment">// and ensure the user is returned to the profile page.</span>

    cy.url().should(<span class="hljs-string">'include'</span>, <span class="hljs-string">'/profile'</span>);
    cy.get(<span class="hljs-string">'.user-info'</span>).should(<span class="hljs-string">'contain'</span>, <span class="hljs-string">'Welcome, testuser'</span>);
  });
});
</code></pre>
<p>In this scenario, you may run into a problem that you wish to diagnose after entering your login information and hitting the login button.</p>
<p>To make sure the user is on the profile page and that their name is shown appropriately, you may use TTD to halt at the <code>cy.url()</code> assertion and verify the status of the application.</p>
<p>With TTD, you can examine the status of the application at different times to interactively debug and troubleshoot your Cypress tests, which will help you find and address problems more efficiently.</p>
<h2 id="heading-continuous-integration">Continuous Integration</h2>
<p>Cypress tests can be integrated into your continuous integration (CI) workflow using solutions like as Travis CI, GitHub actions, Jenkins, or CircleCI. Let's talk about continuous integration using Github actions:</p>
<h3 id="heading-cypress-continuous-integration-using-github-actions">Cypress Continuous Integration Using GitHub Actions</h3>
<p>You can automate the testing of your web apps with Cypress as part of your development workflow with Continuous Integration (CI) using GitHub Actions.</p>
<p>You can create workflows using GitHub Actions that automate a variety of processes, such as Cypress test runs, whenever changes are made to your source. Let's walk through how to get it set up.</p>
<p><strong>Prerequisites:</strong></p>
<ol>
<li><p>GitHub Repository: The Cypress tests and application code should be located in a GitHub repository.</p>
</li>
<li><p>Cypress Setup: Verify that Cypress is installed in your project and that your Cypress test configurations are accurate. Cypress Open may be used to set up and execute your tests locally.</p>
</li>
</ol>
<h4 id="heading-step-1-establish-a-workflow-configuration">Step 1: Establish a Workflow Configuration:</h4>
<p>Create a file in your GitHub repository called workflow configuration. The CI workflow is defined in this file, which is usually named <code>.github/workflows/cypress.yml</code>. It also provides instructions on when and how to execute Cypress tests.</p>
<p>This is an example of a simple configuration file:</p>
<pre><code class="lang-javascript">name: Cypress Tests

<span class="hljs-attr">on</span>:
  push:
    branches:
      - main # Align to your repository<span class="hljs-string">'s default branch

jobs:
  cypress:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Install dependencies
      run: npm install

    - name: Run Cypress tests
      run: npm run cypress:run</span>
</code></pre>
<p>Every time a push is made to the <code>main</code> branch, a process to execute Cypress tests is established by this configuration file. This may be altered to meet your needs.</p>
<h4 id="heading-step-2-describe-the-npm-scripts">Step 2: Describe the <code>npm</code> Scripts:</h4>
<p>Define <code>npm</code> scripts in your <code>package.json</code> file so that Cypress tests may be executed. For instance:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"scripts"</span>: {
  <span class="hljs-string">"cypress:run"</span>: <span class="hljs-string">"cypress run"</span>,
  <span class="hljs-string">"cypress:open"</span>: <span class="hljs-string">"cypress open"</span>
}
</code></pre>
<p>While <code>cypress:open</code> is used to run tests interactively during development, <code>cypress:run</code> is used to execute Cypress tests in headless mode.</p>
<h4 id="heading-step-3-add-to-github-repo">Step 3: Add to GitHub Repo</h4>
<p>Add your application code and the GitHub Actions configuration file (cypress.yml) to your GitHub repository. Send the modifications to GitHub.</p>
<h4 id="heading-step-4-switch-on-github-actions">Step 4: Switch on GitHub Actions:</h4>
<p>If you haven't already, go to your GitHub repository, choose the "Actions" tab, and activate GitHub Actions for your repository.</p>
<h4 id="heading-step-5-manage-your-process">Step 5: Manage Your Process:</h4>
<p>GitHub Actions will launch the specified process, which includes your Cypress tests, automatically each time you post changes to your repository.</p>
<h4 id="heading-step-6-examine-the-results">Step 6: Examine the Results:</h4>
<p>The "Actions" page on your GitHub repository is where you can view the status and outcomes of your Cypress tests. Test results and logs will be displayed to you, allowing you to diagnose and look for any issues.</p>
<h3 id="heading-headless-option">Headless Option</h3>
<p>Cypress offers a headless option for continuous integration (CI) testing. Without the Cypress test runner, you can still run Cypress headlessly. By using the command <code>npx cypress run</code> this simply means there is no graphical user interface involved so the test runner isn't being deployed, here we mostly write scripts to use Cypress.</p>
<p>Web browsers that function without a graphical user interface, or headless browsers, are appropriate for automated testing in server contexts such as continuous integration/continuous delivery systems.</p>
<p>When you run Cypress tests in headless mode, no visible browser window is displayed during test execution. This is very helpful when testing without a user interface in an automated continuous integration and delivery pipeline.</p>
<p>To use this put the command <code>npx cypress run</code> in your terminal.</p>
<ol>
<li><p>Cypress will launch your test in an electron browser when you execute this command.</p>
</li>
<li><p>Upon completion, Cypress will automatically provide a video clip of the test run and screenshots in the event that a test fails, facilitating easy debugging.</p>
</li>
<li><p>The user can adjust screenshots and videos. The folder labeled "screenshots and videos" contains both screenshots and videos cypress produces.</p>
</li>
</ol>
<p>Another way of running tests is through adding scripts to your <code>package.json</code> file. You can add scripts which consist of the <code>key</code> and <code>value</code> pair that work as objects. with <code>key</code> being the name of the command and <code>value</code> being the command that's being run.</p>
<p>Let's run a test headlessly using a key value pair:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"script"</span>: {
    <span class="hljs-string">"cypress:run"</span>: <span class="hljs-string">"cypress run"</span>,
    <span class="hljs-string">"cypress:open"</span>: <span class="hljs-string">"cypress open"</span>
},
</code></pre>
<p>In this code <code>"cypress:run"</code> and <code>"cypress:open"</code> are the <code>keys</code> and <code>"cypress open"</code> and <code>"cypress open"</code> are the commands. To run the command we need the <code>npm run</code> followed by the key used example: <code>npm cypress:run</code>.</p>
<p>Hopefully you now understand how to run tests headlessly in Cypress.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Cypress is an effective tool you can use to test your web apps. You can use it to write end-to-end tests that make sure your React projects function as intended and identify any issues.</p>
<p>Cypress is a useful addition to your testing toolkit because of its interactive Test Runner, easy-to-use API, and robust debugging features. Now go ahead and use Cypress to test your React applications to make sure the user experience is seamless and error-free.</p>
<p>If you want to learn about Cypress in more detail, you can check out their documentation at <a target="_blank" href="https://www.freecodecamp.org/news/p/ee697448-b73d-43dd-85de-d31f1e1005c6/CYPRESS.IO">cypress.io</a>. Cheers to your testing!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Test User Interactions Using the React Testing Library ]]>
                </title>
                <description>
                    <![CDATA[ When you're testing different components in React, you’ll need to simulate user interactions with various parts of each component. In this tutorial, I am going to show you some methods to simulate user interactions with different interactive elements... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-test-user-interactions-in-react/</link>
                <guid isPermaLink="false">66d8515139c4dccc43d4d4bf</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kunal Nalawade ]]>
                </dc:creator>
                <pubDate>Mon, 20 Feb 2023 19:12:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/0_cba5BsaMWpgBXzGK.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you're testing different components in React, you’ll need to simulate user interactions with various parts of each component.</p>
<p>In this tutorial, I am going to show you some methods to simulate user interactions with different interactive elements.</p>
<p>Before getting started, make sure you know how to structure a test in React and how to write simple tests. You can brush up on that knowledge by reading my <a target="_blank" href="https://www.freecodecamp.org/news/how-to-write-unit-tests-in-react/">previous post</a> about how to write unit tests in React.</p>
<p>I also have a challenge for you, so make sure you read the complete article.</p>
<h2 id="heading-how-to-set-up-the-project">How to Set Up the Project</h2>
<p>Create your React app using the command <code>create-react-app testing-user-interactions</code>. Next, install the library <code>user-event</code>.</p>
<pre><code class="lang-python">npm i user-event
</code></pre>
<p>I am assuming you know how to use this library. If not, I explained how it works in my previous <a target="_blank" href="https://www.freecodecamp.org/news/how-to-write-unit-tests-in-react/">tutorial</a>.</p>
<p>Basically, this library has a few functions you can use to simulate a user. We’ll go through all these functions with examples.</p>
<p>Before writing your tests, you'll need to make the following imports at the start of your test suite (the file in which you write all your tests).</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {render, screen} <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>
<span class="hljs-keyword">import</span> userEvent <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/user-event'</span>
</code></pre>
<p>You'll also need to import the component you’ll be testing.</p>
<p>For every test, we'll be simulating user interactions and test how our component behaves.</p>
<h2 id="heading-how-to-test-select-element-interactions">How to Test <code>select</code> Element Interactions</h2>
<p>In your application, you may have <code>select</code> elements that allow a user to select something from a list of options. Let’s take an example:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'selectCity'</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">option</span>&gt;</span> Mumbai<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">option</span>&gt;</span> Bangalore<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">option</span>&gt;</span> Chennai<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
</code></pre>
<p>Normally, when a user sees a <code>select</code> element, they would click it and select an option they want. We are going to simulate the exact same behaviour and test if our component reacts accordingly. For instance, if a user selects <code>Chennai</code>, the value shown must be the same.</p>
<p>First, let’s see how to query the element. Let's use <code>getByRole</code> to query the element. You can use the role <code>combobox</code> for the above element.</p>
<pre><code class="lang-javascript">screen.getByRole(<span class="hljs-string">'combobox'</span>)
</code></pre>
<p>What is a role? How do you know which role to use for an element? In React, for testing purposes, there's an attribute called <code>role</code> defined for every HTML element. This makes it easier for you to query an element while testing.</p>
<p>Now, in order to know the value of the role attribute for each element, you can refer to the list of roles <a target="_blank" href="https://www.w3.org/TR/html-aria/#docconformance">here</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot-2023-02-18-at-12.37.44-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>A Snapshot of the List of Roles</em></p>
<p>If you want to query an anchor element containing an <code>href</code> attribute, you can use the role <code>link</code>. Same is true for other elements in the list.</p>
<p>However, do not define an explicit role attribute for your HTML element, as it's already defined for you.</p>
<p>Let's see another method of querying the element. If there are is more than one <code>select</code> element, you can use <code>getByLabelText()</code>. For this, add a label, <code>&lt;label htmlFor=”selectCity”&gt;Select City&lt;/label&gt;</code> for the element and do <code>screen.getByLabelText(/select city/i)</code>. You’ll get the element.</p>
<p>Now, use the <code>[selectOptions()](https://testing-library.com/docs/ecosystem-user-event/#selectoptionselement-values-options)</code> method of the library to simulate a user selecting an option. It takes three arguments: the element, the value, and any options.</p>
<pre><code class="lang-javascript">userEvent.selectOptions(    
    screen.getByRole(<span class="hljs-string">'combobox'</span>),    
    <span class="hljs-string">'Chennai'</span>
)
</code></pre>
<p>Then, add an assertion at the end of your test to check if the element has the same value that the user selected.</p>
<pre><code class="lang-javascript">expect(screen.getByRole(<span class="hljs-string">'combobox'</span>)).toHaveValue(<span class="hljs-string">'Chennai'</span>)
</code></pre>
<p>The user selected <code>Chennai</code> and our <code>select</code> element has the same value, so the test passes.</p>
<h2 id="heading-how-to-test-state-change">How to Test State Change</h2>
<p>Now, I’ll make a state update on changing the <code>select</code> element’s value. First, I’ll have a state called <code>cityName</code> to display the option selected.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [cityName, setCityName] = useState(<span class="hljs-string">'Mumbai'</span>);
</code></pre>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span> City Name:  {cityName} <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
</code></pre>
<p>Then, I’ll add an <code>onChange</code> attribute with a method that updates the state.</p>
<pre><code class="lang-javascript">onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> { setCityName(e.target.value)}}
</code></pre>
<p>To write the test for this, follow the same process:</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"City Name header rendering"</span>, <span class="hljs-function">() =&gt;</span> {    
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SelectElement</span>/&gt;</span></span>)    
    userEvent.selectOptions(        
        screen.getByRole(<span class="hljs-string">'combobox'</span>),        
        <span class="hljs-string">'Bangalore'</span>    )    
    expect(screen.getByRole(<span class="hljs-string">'heading'</span>,               
                     { <span class="hljs-attr">name</span>: <span class="hljs-regexp">/bangalore/i</span>})).toBeInTheDocument();
})
</code></pre>
<p>To query the <code>h2</code> element, use <code>[getByRole()](https://testing-library.com/docs/queries/byrole/)</code> with the <code>name</code> option. The <code>name</code> option adds another filter to the query – in case there are multiple <code>h2</code> elements.</p>
<h2 id="heading-how-to-test-a-hover-event">How to Test a Hover Event</h2>
<p>Your application may have functionalities where a user can hover over an element to see some information as a tooltip. So, to learn how to test this, let’s implement a basic tooltip functionality.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tooltip"</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> Hover over me<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tooltiptext"</span>&gt;</span>Tooltip text<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>There’s also some CSS styles for the classes defined above, but I won’t show them here. Basically, when a user hovers over the text <em>Hover over me,</em> the tooltip text is visible.</p>
<p>To simulate a user hovering over the text element, use the <code>hover()</code> method. Add an assertion at the end to test if the tooltip is visible.</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Tooltip visible"</span>, <span class="hljs-function">() =&gt;</span> {    
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">HoverEvent</span>/&gt;</span></span>)
    userEvent.hover(screen.getByText(<span class="hljs-regexp">/hover over me/i</span>))          
    expect(screen.getByText(<span class="hljs-regexp">/tooltip text/i</span>)).toBeInTheDocument()
})
</code></pre>
<p>The test will pass if the tooltip is visible on hover. If it doesn't, you'll need to refactor your code.</p>
<h2 id="heading-how-to-test-a-file-upload-event">How to Test a File Upload Event</h2>
<p>Another important event is a user uploading files to your website. This is a pretty common functionality in any UI application.</p>
<p>Let’s have an <code>input</code> of type <code>file</code>.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"singleFile"</span>&gt;</span>Upload File<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'file'</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'singleFile'</span> /&gt;</span>
</code></pre>
<p>In the test, you’ll need to simulate the user uploading a file. Use the <code>upload()</code> method to do that:</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Test single file upload"</span>, <span class="hljs-function">() =&gt;</span> {    
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FileUpload</span>/&gt;</span></span>)    
    <span class="hljs-keyword">const</span> file = <span class="hljs-keyword">new</span> File([<span class="hljs-string">'hello'</span>], <span class="hljs-string">'hello.png'</span>, {<span class="hljs-attr">type</span>: <span class="hljs-string">'image/png'</span>})   
    userEvent.upload(screen.getByLabelText(<span class="hljs-regexp">/upload file/i</span>), file)  
    expect(screen.getByLabelText(<span class="hljs-regexp">/upload file/i</span>).files[<span class="hljs-number">0</span>]).toEqual(file)
})
</code></pre>
<p>First, create a mock file and upload the same. Then, make an assertion that the file present in the input matches our uploaded file. It does, so the test passes.</p>
<p>Now, let’s take an example of multiple file uploads.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'multipleFiles'</span>&gt;</span>Upload multiple files<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'file'</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'multipleFiles'</span> <span class="hljs-attr">multiple</span> /&gt;</span>
</code></pre>
<p>In our test, upload multiple files.</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Test multiple file uploads"</span>, <span class="hljs-function">() =&gt;</span> {    
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FileUpload</span>/&gt;</span></span>)    
    <span class="hljs-keyword">const</span> files = [        
           <span class="hljs-keyword">new</span> File([<span class="hljs-string">'file1'</span>], <span class="hljs-string">'file1.png'</span>, {<span class="hljs-attr">type</span>: <span class="hljs-string">'image/png'</span>}),       
           <span class="hljs-keyword">new</span> File([<span class="hljs-string">'file2'</span>], <span class="hljs-string">'file2.png'</span>, {<span class="hljs-attr">type</span>: <span class="hljs-string">'application/pdf'</span>})   
    ]    
    userEvent.upload(screen.getByLabelText(<span class="hljs-regexp">/upload multiple files/i</span>), 
                        files)    
    <span class="hljs-keyword">const</span> fileInput = screen.getByLabelText(<span class="hljs-regexp">/upload multiple files/i</span>)  

    expect(fileInput.files.length).toBe(<span class="hljs-number">2</span>);   
    expect(fileInput.files[<span class="hljs-number">0</span>]).toEqual(files[<span class="hljs-number">0</span>]);   
    expect(fileInput.files[<span class="hljs-number">1</span>]).toEqual(files[<span class="hljs-number">1</span>]);
})
</code></pre>
<p>In the assertions, check the number of files and whether both the files match the uploaded ones. Thus, all our tests pass with 100% coverage.</p>
<p>Now, time for the challenge. Go through my posts on <a target="_blank" href="https://medium.com/gitconnected/how-to-implement-multiple-file-uploads-in-react-4cdcaadd0f6e">Muliple File Uploads</a> and <a target="_blank" href="https://medium.com/gitconnected/4-different-examples-of-the-usestate-hook-in-react-5504ce011a20">Different Examples of the useState Hook</a> and write unit tests for all the functionalities. Upload your code on GitHub and share it with me.</p>
<h2 id="heading-how-to-test-form-behaviour">How to Test Form Behaviour</h2>
<p>An important feature of most web apps is form handling. When writing tests for a React app, it’s highly likely you will need to test your component’s behaviour with forms.</p>
<p>In our example, let’s have a form with two <code>input</code> fields, one <code>select</code> field and a submit button.</p>
<p>First, let’s define the states for the respective fields and an additional state for indicating whether the form is submitted.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [name, setName] = useState(<span class="hljs-string">''</span>)
<span class="hljs-keyword">const</span> [password, setPassword] = useState(<span class="hljs-string">''</span>)
<span class="hljs-keyword">const</span> [country, setCountry] = useState(<span class="hljs-string">''</span>)
<span class="hljs-keyword">const</span> [formSubmitted, setFormSubmitted] = useState(<span class="hljs-literal">false</span>)
</code></pre>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {setName(e.target.value)}}             
           placeholder='Enter name'/&gt;    
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {setPassword(e.target.value)}}                          placeholder='Enter password'/&gt;    
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'selectCountry'</span>&gt;</span> Select Country <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'selectCountry'</span>             
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> {setCountry(e.target.value)}}&gt;       
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span>&gt;</span>India<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>        <span class="hljs-tag">&lt;<span class="hljs-name">option</span>&gt;</span>Japan<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>       
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span>&gt;</span>China<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>        <span class="hljs-tag">&lt;<span class="hljs-name">option</span>&gt;</span>USA<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>       
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span>&gt;</span>England<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>    
    <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>    
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleSubmit}</span>&gt;</span> Submit <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>In the <code>onChange</code> attribute, make the state updates. In the end, display a text on successful submission of the form.</p>
<pre><code class="lang-html">{formSubmitted &amp;&amp; 'Form Submitted'}
</code></pre>
<p>Also, let’s add some validation in the <code>handleSubmit</code> method. When a validation fails, a list of errors will be shown. Create another state to hold errors.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [errors, setErrors] = useState([])
</code></pre>
<p>Now, implement the <code>handleSubmit</code> method.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {    
    event.preventDefault();    
    <span class="hljs-keyword">const</span> errorList=[];    
    <span class="hljs-keyword">if</span>(name == <span class="hljs-string">''</span> || password == <span class="hljs-string">''</span> || country == <span class="hljs-string">''</span>) {        
        errorList.push(<span class="hljs-string">'Please fill all the details'</span>)    
    }        
    <span class="hljs-keyword">const</span> regex = <span class="hljs-regexp">/^[a-z]*$/i</span>;    
    <span class="hljs-keyword">if</span>(!name.match(regex)) {        
        errorList.push(<span class="hljs-string">'Please enter a valid name'</span>)    
    }    
    <span class="hljs-keyword">if</span>(errorList.length != <span class="hljs-number">0</span>) {        
        setErrors(errorList);        
        setFormSubmitted(<span class="hljs-literal">false</span>);        
        <span class="hljs-keyword">return</span>;      
    }     
    setErrors([]);    
    setFormSubmitted(<span class="hljs-literal">true</span>)
}
</code></pre>
<p>First, we’ll do <code>event.preventDefault()</code> which cancels out any events that prevent submitting the form. Then, we’ll add few error checks and add any errors to the state <code>errors</code>. In the end, if there are no errors then set <code>errors</code> to an empty array and set <code>formSubmitted</code> as <code>true</code>.</p>
<p>We’ll also display the list of all the errors which will be visible on clicking the submit button.</p>
<pre><code class="lang-javascript">errors.map(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> (    
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>        
        {err}   
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
))
</code></pre>
<p>Let’s write the tests for this example. There are a few different things you should test. First, write a test simulating a user interacting with the form and submitting it with correct values.</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Form getting submitted with correct input values"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FormBehaviour</span>/&gt;</span></span>)

    userEvent.type(screen.getByPlaceholderText(<span class="hljs-regexp">/enter name/i</span>), <span class="hljs-string">'kunal'</span>)
    userEvent.type(screen.getByPlaceholderText(<span class="hljs-regexp">/enter password/i</span>),                                   <span class="hljs-string">'pass'</span>)
    userEvent.selectOptions(screen.getByLabelText(<span class="hljs-regexp">/select country/i</span>),                               <span class="hljs-string">'India'</span>)
    userEvent.click(screen.getByText(<span class="hljs-regexp">/submit/i</span>))

    expect(screen.getByText(<span class="hljs-regexp">/form submitted/i</span>)).toBeInTheDocument();
})
</code></pre>
<p>Use the <code>type</code> and <code>click</code> methods of the <code>userEvent</code> library to simulate the respective actions. In the end, add an assertion to see if the text matching the regex <code>/form submitted/i</code> is visible in the document since our logic displays the text as soon as the form is submitted.</p>
<p><img src="https://miro.medium.com/max/1400/1*Tb0jZPtQRAJwlrlWpEhUiw.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Test Coverage for the Form Behaviour Component</em></p>
<p>Our test passes, but there are still some uncovered lines. We have tested regular form behaviour. However, we also have error handling mechanisms in place. So, you also need to write tests that cover different error scenarios.</p>
<p>First, simulate a user clicking the submit button without entering anything.</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Empty fields error shown"</span>, <span class="hljs-function">() =&gt;</span> {    
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FormBehaviour</span>/&gt;</span></span>)    
    userEvent.click(screen.getByText(<span class="hljs-regexp">/submit/i</span>))    
    expect(screen.getByText(<span class="hljs-regexp">/please fill all the details/i</span>))                         .toBeInTheDocument()
})
</code></pre>
<p>On clicking the submit button, an error message should be visible prompting the user to fill all the details. Make an assertion for the same.</p>
<p>Not all lines are covered yet. We still have one more error scenario. What if the user enters an invalid name?</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Invalid name error shown"</span>, <span class="hljs-function">() =&gt;</span> {    
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FormBehaviour</span>/&gt;</span></span>)    
    userEvent.type(screen.getByPlaceholderText(<span class="hljs-regexp">/enter name/i</span>), <span class="hljs-string">'@###'</span>)       userEvent.type(screen.getByPlaceholderText(<span class="hljs-regexp">/enter password/i</span>),                                    <span class="hljs-string">'pass'</span>)    
    userEvent.selectOptions(screen.getByLabelText(<span class="hljs-regexp">/select country/i</span>),                                  <span class="hljs-string">'India'</span>)    
    userEvent.click(screen.getByText(<span class="hljs-regexp">/submit/i</span>))    
    expect(screen.getByText(<span class="hljs-regexp">/Please enter a valid name/i</span>))
          .toBeInTheDocument();
})
</code></pre>
<p>This test is similar to the first test, except here, the user enters an invalid name. So, assert whether the corresponding error is shown.</p>
<p><img src="https://miro.medium.com/max/1400/1*QEBA48iEtB15EmuQrs-DTQ.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>100% Coverage for the Form Behaviour Component</em></p>
<p>Now, all our tests pass with 100% coverage.</p>
<p>You can find the complete code on <a target="_blank" href="https://github.com/KunalN25/testing-user-interactions">GitHub</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Testing user interactions helps you make sure that users can interact efficiently and effectively with your application. You need to cover all possible scenarios a user may encounter. In this article, I have shown you how you can simulate user interactions with different elements.</p>
<p>I have also demonstrated testing form behaviour covering many scenarios. I hope you now know how to use the <code>userEvent</code> library to test your component’s behaviour with user events.</p>
<p>If you are unable to understand the content or find the explanation unsatisfactory, get in touch with me and let me know. New ideas are always appreciated! Feel free to connect with me on Twitter. Till then, Goodbye!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Write Unit Tests in React ]]>
                </title>
                <description>
                    <![CDATA[ When you're building a website and coding all the cool features you had planned, you'll want to test if everything works as expected. Now, you can’t do that just by going through the website yourself – you need to check if each unit of your code is w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-write-unit-tests-in-react/</link>
                <guid isPermaLink="false">66d8515392b237d9f6a7c528</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unit testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kunal Nalawade ]]>
                </dc:creator>
                <pubDate>Mon, 23 Jan 2023 15:35:25 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/unit-testing-in-react-image.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you're building a website and coding all the cool features you had planned, you'll want to test if everything works as expected.</p>
<p>Now, you can’t do that just by going through the website yourself – you need to check if each unit of your code is working as you want. For that, you need to write unit tests. And they can be quite a hassle when you actually get down to writing them.</p>
<p>So, in this post, I am going to show you how you can get started with writing unit tests in React. I will explain the process through several examples to help you understand better.</p>
<p>For this post, I assume you have a basic knowledge of React. If not, then head over to the <a target="_blank" href="https://reactjs.org/docs/getting-started.html">docs</a> to get started.</p>
<h2 id="heading-how-is-a-test-structured">How Is a Test Structured?</h2>
<p>Testing involves checking if your code is functioning as it's supposed to by comparing the expected output with the actual output.</p>
<h3 id="heading-what-to-test">What to Test</h3>
<p>In general, your tests should cover the following aspects of your code:</p>
<ol>
<li><p>If a component renders with or without props</p>
</li>
<li><p>How a component renders with state changes</p>
</li>
<li><p>How a component reacts to user interactions</p>
</li>
</ol>
<h3 id="heading-what-not-to-test">What Not to Test</h3>
<p>Testing most of your code is important, but here are some things you do not need to test:</p>
<ol>
<li><p><strong>Actual Implementation:</strong> You do not need to test the actual implementation of a functionality. Just test if the component is behaving correctly.<br> Let’s say you want to sort an array on the click of a button. There’s no need to test the actual sorting logic. You only test if the function was called and if the state changes are rendering correctly.</p>
</li>
<li><p><strong>Third Party libraries:</strong> If you are using any third party libraries like Material UI, no need to test those – they should already be tried and tested.</p>
</li>
</ol>
<p>This might seem a little complicated at the moment, but you should understand better through examples.</p>
<p>Any test in React, no matter how complicated, follows this structure:</p>
<ol>
<li><p>Render the component</p>
</li>
<li><p>Get an element from the component and simulate any user interactions</p>
</li>
<li><p>Write an assertion.</p>
</li>
</ol>
<h2 id="heading-how-to-set-up-our-project">How to Set Up Our Project</h2>
<p>First, create the app with <code>create-react-app</code>. We’ll be using <a target="_blank" href="https://jestjs.io/docs/getting-started">Jest</a> and <a target="_blank" href="https://testing-library.com/docs/react-testing-library/intro/">React Testing Library</a>. Both of them come pre - installed with <code>create-react-app</code>. You can see what it looks like here:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"@testing-library/jest-dom"</span>: <span class="hljs-string">"^5.16.5"</span>,
    <span class="hljs-string">"@testing-library/react"</span>: <span class="hljs-string">"^13.4.0"</span>,
    <span class="hljs-string">"react"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-string">"react-dom"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-string">"react-scripts"</span>: <span class="hljs-string">"5.0.1"</span>,
    <span class="hljs-string">"web-vitals"</span>: <span class="hljs-string">"^2.1.4"</span>
},
</code></pre>
<p>As you can see, Jest is not visible here. But, if you go to the <code>node_modules</code> folder, you will see Jest there. So, no need to install it separately.</p>
<p>Also, make sure you have the following lines in your <code>setupTests.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">'@testing-library/jest-dom'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'@testing-library/jest-dom/extend-expect'</span>;
</code></pre>
<p>Also, in your <code>package.json</code> file, modify your scripts like this:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"react-scripts start"</span>,
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"react-scripts build"</span>,
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"react-scripts test --watchAll --coverage"</span>,
    <span class="hljs-string">"eject"</span>: <span class="hljs-string">"react-scripts eject"</span>
},
</code></pre>
<p>This will run your tests in watch mode and also show you the coverage (that is, the portion of the code covered by your tests). You can also define the coverage threshold by adding another property <code>jest</code>. For now, keep the threshold at 80%.</p>
<pre><code class="lang-javascript"><span class="hljs-string">"jest"</span>: {
    <span class="hljs-string">"coverageThreshold"</span>: {
        <span class="hljs-string">"global"</span>: {
            <span class="hljs-string">"lines"</span>: <span class="hljs-number">80</span>
        }
    }
}
</code></pre>
<p>That’s it, now you are ready to start testing. Let’s first start with a basic test.</p>
<h2 id="heading-how-to-write-your-first-test-for-a-react-app">How to Write Your First Test for a React App</h2>
<p>Let’s write a test for the following component:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> FirstTest = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span> First test <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>Here, we just need to test if the <code>h2</code> element renders. Now, where should we write the tests? We can write them inside a <code>__tests__</code> folder anywhere in the <code>src</code> folder. The test file just needs to have a <code>.test.js/jsx</code> extension and the test runner will pick it up.</p>
<p>This is how the test looks in our <code>FirstTest.test.jsx</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>
<span class="hljs-keyword">import</span> FirstTest <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/FirstTest'</span>;

test(<span class="hljs-string">"Example 1 renders successfully"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FirstTest</span>/&gt;</span></span>);

    <span class="hljs-keyword">const</span> element = screen.getByText(<span class="hljs-regexp">/first test/i</span>);

    expect(element).toBeInTheDocument();
})
</code></pre>
<p>First, import the required methods. The <code>test()</code> method contains an individual test. It takes the name of the test and a callback function as the two arguments.</p>
<p>Now, following the structure mentioned above, render the component you're testing using the <a target="_blank" href="https://testing-library.com/docs/react-testing-library/api/#render">render</a> method. Then, use the <code>screen</code> object to make a query for an element. In this case, it’s the <code>h2</code> element. Our query gets an element containing text that matches the regex <code>/first test/i</code> (<em>i</em> means ignore case).</p>
<p>Lastly, make the assertion using the <code>expect</code> method. We expect the element to be in the document and it is, so the test passes.</p>
<p>There are lots of other assertions you can make in your tests. You can read more about them <a target="_blank" href="https://jestjs.io/docs/using-matchers">here</a>. Also, you can find a list of ways to query an element <a target="_blank" href="https://testing-library.com/docs/queries/about">here</a>. We’ll use some of them in our further examples.</p>
<h2 id="heading-how-to-test-with-mock-data-in-react">How to Test With Mock Data in React</h2>
<p>Here, we have a component with a prop <code>data</code> that displays a list of items. Let’s assume this data comes from the backend and your component is displaying this data.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">const</span> TestWithMockData = <span class="hljs-function">(<span class="hljs-params">{data}</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
            {data.map(item =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{item.id}</span>&gt;</span>
                    {item.id}
                    {item.first_name},
                    {item.last_name},
                    {item.email}

                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            ))}
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TestWithMockData
</code></pre>
<p>While writing tests for a component with props, you need to pass some mock data while rendering this component that pertains to your functionality. Here, one object in our data contains four fields, so we pass some mock data here.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mockData = [
    {
        <span class="hljs-string">"id"</span>: <span class="hljs-number">1</span>,
        <span class="hljs-string">"first_name"</span>: <span class="hljs-string">"Fletcher"</span>,
        <span class="hljs-string">"last_name"</span>: <span class="hljs-string">"McVanamy"</span>,
        <span class="hljs-string">"email"</span>: <span class="hljs-string">"mmcvanamy0@e-recht24.de"</span>,
        <span class="hljs-string">"age"</span>: <span class="hljs-number">30</span>
      }, {
        <span class="hljs-string">"id"</span>: <span class="hljs-number">2</span>,
        <span class="hljs-string">"first_name"</span>: <span class="hljs-string">"Clarice"</span>,
        <span class="hljs-string">"last_name"</span>: <span class="hljs-string">"Harrild"</span>,
        <span class="hljs-string">"email"</span>: <span class="hljs-string">"charrild1@dion.ne.jp"</span>,
        <span class="hljs-string">"age"</span>: <span class="hljs-number">35</span>
      }, 
]
</code></pre>
<p>Note that the actual data could contain thousands of records, but the mock data only needs to be relevant to what you want to test. Now, let’s write the test and check if the list renders.</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"List renders successfully"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TestWithMockData</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{mockData}</span> /&gt;</span></span>)
    expect(screen.getByText(<span class="hljs-regexp">/fletcher/i</span>)).toBeInTheDocument();
})
</code></pre>
<h2 id="heading-how-to-test-with-mock-data-covering-all-branches-in-react">How to Test With Mock Data Covering All Branches in React</h2>
<p>Let’s introduce some branching in the above component. We’ll have another prop, <code>displayUnorderedList</code>, that determines whether to display an ordered or unordered list. We’ll also render <code>Senior</code> for <code>age &gt; 50</code> and <code>Not Senior</code> otherwise.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">const</span> TestWithMockData = <span class="hljs-function">(<span class="hljs-params">{data, displayUnorderedList, handleClick}</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        {displayUnorderedList ? 
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
                {data.map(item =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{item.id}</span>&gt;</span>
                        {item.id}
                        {item.first_name},
                        {item.last_name},
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
                            console.log("email link clicked")
                            handleClick()
                        }}&gt;{item.email}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>

                        {item.age &gt; 50 ? 'Senior' : 'Not senior'}

                    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
        : 
            <span class="hljs-tag">&lt;<span class="hljs-name">ol</span>&gt;</span>
                {data.map(item =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{item.id}</span>&gt;</span>
                        Last name: {item.last_name}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">ol</span>&gt;</span>
        }
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TestWithMockData
</code></pre>
<p>Here's the coverage report so far:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-141.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Failed Test: Unable to find the element</em></p>
<p>You can see that Line 9 (that is, the unordered list part) is not covered by our existing tests. And our previous test has also failed since it is unable to find <code>fletcher</code> in the component.</p>
<p>Why is this so? In our previous test, we have not passed the <code>displayUnorderedList</code> prop to the component so it is <code>null</code>. So, the component renders the ordered list and it does not contain the <code>first_name</code>.</p>
<p>So, let’s write another test covering the ordered list part.</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Ordered list renders"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TestWithMockData</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{mockData}</span> <span class="hljs-attr">displayUnorderedList</span>=<span class="hljs-string">{false}</span> /&gt;</span></span>)

    expect(screen.getByText(<span class="hljs-regexp">/McVanamy/i</span>)).toBeInTheDocument()
})
</code></pre>
<p>Here, pass the prop’s value as <code>false</code> to render the ordered list. Also, add the <code>displayUnorderedList</code> prop to the failing test and pass the value <code>true</code>.</p>
<p>Now, all our tests pass with 100% coverage.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-142.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Uncovered Branch</em></p>
<p>One line is still not covered by the test cases, which is the branching logic for <code>age</code>. So, add another record to the mock data that has an age greater than 50.</p>
<pre><code class="lang-javascript">{
    <span class="hljs-string">"id"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-string">"first_name"</span>: <span class="hljs-string">"Amby"</span>,
    <span class="hljs-string">"last_name"</span>: <span class="hljs-string">"Emmer"</span>,
    <span class="hljs-string">"email"</span>: <span class="hljs-string">"aemmer2@buzzfeed.com"</span>,
    <span class="hljs-string">"age"</span>: <span class="hljs-number">67</span>
}
</code></pre>
<p>Now, all our tests should pass with 100% coverage.</p>
<h2 id="heading-how-to-test-user-interactions-in-react">How to Test User Interactions in React</h2>
<p>The most important part of testing any UI app is testing its behaviour with various user interactions. Almost every functionality in a UI app involves user interactions.</p>
<p>You can use the <code>user-event</code> library to simulate user interactions. It has methods for simulating various user events such as <em>click</em>, <em>type</em>, <em>hover</em>, and so on.</p>
<p>First, we need to install the library:</p>
<pre><code class="lang-python">npm install --save-dev @testing-library/user-event
</code></pre>
<p>We can use this library to simulate user events. In our examples, we’ll interact with different elements, mostly <code>input</code> and <code>button</code> elements.</p>
<h3 id="heading-how-to-test-a-function-call-on-click-of-an-element">How to Test a Function Call on Click of an Element</h3>
<p>In our above component, we need to<code>TestWithMockData</code>, make the <code>email</code> field clickable and call a function <code>handleClick</code> on it. This will be passed as props to the component. There, replace <code>{item.email}</code> with:</p>
<pre><code class="lang-javascript">&lt;a onClick={<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"email link clicked"</span>)
    handleClick()
}}&gt;{item.email}&lt;/a&gt;
</code></pre>
<p>Now, our test coverage takes a hit. To cover this scenario, write the following test:</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Email link click handler called"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> mockHandleClick = jest.fn();
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TestWithMockData</span> 
                <span class="hljs-attr">data</span>=<span class="hljs-string">{mockData}</span> 
                <span class="hljs-attr">displayUnorderedList</span>=<span class="hljs-string">{true}</span>
                <span class="hljs-attr">handleClick</span> = <span class="hljs-string">{mockHandleClick}</span>
          /&gt;</span></span>)
    <span class="hljs-keyword">await</span> userEvent.click(screen.getByText(<span class="hljs-regexp">/mmcvanamy0@e-recht24.de/i</span>));
    expect(mockHandleClick).toHaveBeenCalled();
})
</code></pre>
<p>First, create the mock of the <code>handleClick</code> function using <code>jest.fn()</code>. We do not need the actual implementation of the method, as we only want to test the behaviour of the component. So, we create an empty mock and pass the same as props. Read more about mocking functions <a target="_blank" href="https://jestjs.io/docs/mock-functions">here</a>.</p>
<p>Now, query the <code>&lt;a&gt;</code> element by text (any <code>email</code> from the mock data). Use the <code>click()</code> method to simulate a click event. Use <code>await</code> as simulating a user event is an asynchronous operation.</p>
<p>Write an assertion in the end to check if the method was called. The method was called, so our test passes with 100% coverage.</p>
<h3 id="heading-how-to-query-input-fields-and-buttons">How to Query Input Fields and Buttons</h3>
<p>So far, we have used only one method of querying elements – <code>getByText()</code>. Now, let’s see how you can query input fields and buttons.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Enter name'</span>/&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span> Submit <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>To query these elements, you can do the following:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> inputElement = screen.getByRole(<span class="hljs-string">'textbox'</span>)
</code></pre>
<p><code>getByRole</code> finds an element by the given role. In this case, the <code>textbox</code> role means the <code>input</code> element.</p>
<p>How is role determined? Each element has a defined role, so you do not need to specify an explicit role attribute. You can see a list of roles for different elements <a target="_blank" href="https://www.w3.org/TR/html-aria/#docconformance">here</a>. Whatever element you want, just do <code>getByRole</code> and refer to the list.</p>
<p>For <code>button</code>, the default role is ‘button’ as you can see here:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> button = screen.getByRole(<span class="hljs-string">'button'</span>)
</code></pre>
<p>What if we add another <code>input</code> element, <code>&lt;input placeholder=’Enter description’/&gt;</code>? The test runner will now throw an error saying that there are two elements with the same role. What should we do in such a scenario? Use another query <code>getByPlaceholderText()</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> nameInput = screen.getByPlaceholderText(<span class="hljs-regexp">/enter name/i</span>);
<span class="hljs-keyword">const</span> descrInput = screen.getByPlaceholderText(<span class="hljs-regexp">/enter description/i</span>);
</code></pre>
<p>You can also use <code>getByLabelText()</code> if your <code>input</code> has a <code>label</code>.</p>
<pre><code class="lang-javascript">&lt;label htmlFor=<span class="hljs-string">'password'</span>&gt; Enter password&lt;/label&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'password'</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'password'</span>/&gt;</span></span>
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> passwordInput = screen.getByLabelText(<span class="hljs-regexp">/enter password/i</span>);
</code></pre>
<p>For querying buttons, we can do <code>screen.getByRole('button')</code>.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span> Submit <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span> Apply<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Since we have two buttons here, just doing <code>getByRole</code> will throw an error. So, we use a <code>name</code> option.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> submitButton = screen.getByRole(<span class="hljs-string">'button'</span>, { <span class="hljs-attr">name</span>: <span class="hljs-regexp">/submit/i</span> });
<span class="hljs-keyword">const</span> applyButton = screen.getByRole(<span class="hljs-string">'button'</span>, { <span class="hljs-attr">name</span>: <span class="hljs-regexp">/apply/i</span> });
</code></pre>
<p>The <code>name</code> option can contain the label of a form element, text of a button, or the value of the <code>aria-label</code> attribute of any element. We can also do <code>getByText()</code> for a button.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> submitButton = screen.getByText(<span class="hljs-regexp">/submit/i</span>);
</code></pre>
<h2 id="heading-how-to-test-state-updates-in-react">How to Test State Updates in React</h2>
<p>We have seen how to query form elements like <code>input</code> and <code>button</code>. Now, let’s simulate some user interactions and test state updates. What do I mean by testing state updates?</p>
<p>State updates cause a component to re-render. So, when your functionality performs a state update, you should test how the component is behaving due to the state change.</p>
<p>First, let’s take a simple example where we set the state as soon as the component is loaded – that is, in the <code>useEffect</code> block.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> TestingStateChange = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [loaded, setLoaded] = useState(<span class="hljs-literal">false</span>)
    useEffect(<span class="hljs-function">() =&gt;</span> {
        setLoaded(<span class="hljs-literal">true</span>)
    }, [])
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        {loaded &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span> Page Loaded <span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>Here, your entire component, starting from the <code>useEffect</code> statement to the end, is uncovered. When you write the following test, not only does the HTML part get covered but also the <code>useEffect</code> part where you set the state.</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Testing page load"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TestingStateChange</span>/&gt;</span></span>)
    expect(screen.getByText(<span class="hljs-regexp">/page loaded/i</span>)).toBeInTheDocument();
})
</code></pre>
<p>This is how you test state updates. You test whether the component behaves as you expect with a state change.</p>
<h3 id="heading-how-to-test-a-state-change-on-click-of-a-button">How to Test a State Change on Click of a Button</h3>
<p>Let’s have a button and a text that toggles on the click of the button.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [toggleTextVisible, setToggleTextVisible] = useState(<span class="hljs-literal">false</span>)<span class="hljs-string">`</span>
</code></pre>
<pre><code class="lang-html">{toggleTextVisible &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> Text visible <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span> }

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> { setToggleTextVisible(!toggleTextVisible) }}&gt; 
    Toggle text 
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>We’ll write the test for this:</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Toggle text visible"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TestingStateChange</span>/&gt;</span></span>);
    <span class="hljs-keyword">await</span> userEvent.click(screen.getByText(<span class="hljs-regexp">/toggle text/i</span>));
    expect(screen.getByText(<span class="hljs-regexp">/text visible/i</span>)).toBeInTheDocument();
})
</code></pre>
<p>With <code>userEvent</code>, we have simulated the click of a button and asserted if the text is visible.</p>
<p>Now, let’s test another scenario where we check if the above button is disabled on the click of another button.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [btnDisabled, setBtnDisabled] = useState(<span class="hljs-literal">false</span>);
</code></pre>
<p>Add a <code>disabled={btnDisabled}</code> attribute to the above button and create another button that controls its value.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> { setBtnDisabled(!btnDisabled) }}&gt; 
      Toggle button disabled 
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Use the <code>toBeDisabled()</code> method to test if the button is disabled.</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Button disabled on click"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TestingStateChange</span>/&gt;</span></span>)
    <span class="hljs-keyword">await</span> userEvent.click(screen.getByText(<span class="hljs-regexp">/toggle button disabled/i</span>));
    expect(screen.getByText(<span class="hljs-regexp">/toggle text/i</span>)).toBeDisabled();
})
</code></pre>
<h3 id="heading-how-to-test-if-an-element-was-added-to-a-list">How to Test if an Element Was Added to a List</h3>
<p>Let’s perform another assertion. Here, we’ll check if an element was added to a list. We’ll have some data and create a state with the data as the initial value. We'll display it while also having a button to add an element to it.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [elements, setElements] = useState(data);
</code></pre>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span> List <span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
{elements.map(item =&gt; (
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>= <span class="hljs-string">{item.id}</span>&gt;</span>
       {item.id}: { item.name }
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
))}
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
    setElements([...elements, {
        id: 4,
        name: 'abhinav'
    }])
}}&gt; Add to list <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Add an attribute <code>data-testid='record'</code> to each record to help query the element. Let’s write the test for this:</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Element added to the list"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TestingStateChange</span>/&gt;</span></span>)
    expect(screen.getAllByTestId(<span class="hljs-string">'record'</span>).length).toBe(<span class="hljs-number">3</span>);

    <span class="hljs-keyword">await</span> userEvent.click(screen.getByText(<span class="hljs-regexp">/add to list/i</span>));
    expect(screen.getAllByTestId(<span class="hljs-string">'record'</span>).length).toBe(<span class="hljs-number">4</span>);
})
</code></pre>
<p>To get multiple elements with the same query, use the <code>getAllBy…</code> method. In this case, there are multiple elements with the test-id <code>record</code>, so using the <code>getAllByTestId()</code> method gets a list of all such elements.</p>
<p>Here, we have checked the length of the list. We can add another assertion to check if the new element is visible.</p>
<pre><code class="lang-javascript">expect(screen.getByText(<span class="hljs-regexp">/abhinav/i</span>)).toBeInTheDocument();
</code></pre>
<h3 id="heading-how-to-test-if-an-element-was-removed-from-a-list">How to Test if an Element Was Removed From a List</h3>
<p>Now, let’s add another button that removes an element from the list.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
    setElements(elements.filter(item =&gt; item.id != 2))
}}&gt; Remove from list <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>The test for this:</p>
<pre><code class="lang-javascript">test(<span class="hljs-string">"Element removed from list"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TestingStateChange</span>/&gt;</span></span>)
    <span class="hljs-keyword">await</span> userEvent.click(screen.getByText(<span class="hljs-regexp">/remove from list/i</span>));
    expect(screen.getAllByTestId(<span class="hljs-string">'record'</span>).length).toBe(<span class="hljs-number">2</span>);
})
</code></pre>
<p>Here, we test whether the length of the list was reduced. All our tests pass with 100% coverage.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-143.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Coverage Report for Testing State Change: Tests Passed</em></p>
<h2 id="heading-how-to-test-api-calls-in-react">How to Test API Calls in React</h2>
<p>API calls are an important part of any UI application. Let’s understand how to test an API call.</p>
<p>In this example, we have a file <code>data.json</code> that has some data. We’ll make an API call to fetch this data.</p>
<p>For demonstration, we’ll save this file on the local server only (inside the <code>public</code> folder), but the procedure remains the same when fetching a file from a remote server.</p>
<p>We’ll use <code>fetch</code> to make the API call.</p>
<pre><code class="lang-javascript">fetch(<span class="hljs-string">"http://localhost:3000/data.json"</span>)
  .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
      <span class="hljs-keyword">return</span> res.json();
  })
  .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
      <span class="hljs-comment">// Store the data in state</span>
  })
</code></pre>
<p>Now, let’s have a state to store the data and render it.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [data, setData] = useState([])
</code></pre>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    {data.map(item =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            {item.name}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    ))}
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>How do we write the test for this? One way is to mock the <code>fetch</code> method and provide our own mock implementation. But what if there are multiple Fetch API calls in the component? You cannot have the same mock implementation for everyone.</p>
<p>So, we put the <code>fetch</code> call in a separate function and have a mock implementation of that function. We put our API call in a <code>FetchData</code> method that returns the promise returned by <code>fetch</code>.</p>
<p>We put the method in a separate <code>Services.js</code> file and export it from there.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> FetchData = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> fetch(<span class="hljs-string">"http://localhost:3000/data.json"</span>)
        .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">return</span> res.json();
        }) 
}
</code></pre>
<p>Now, when we call this method inside our component, it looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [data, setData] = useState([])

useEffect(<span class="hljs-function">() =&gt;</span> {
    FetchData().then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
        setData(data);
    })
})

<span class="hljs-keyword">return</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      {data.map(item =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
              {item.name}
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ))}
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
)
</code></pre>
<p>Now, let’s write the test for this. The main thing is to mock the FetchData method. First, import all methods from <code>Services.js</code> as a module.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> services <span class="hljs-keyword">from</span> <span class="hljs-string">'../utils/Services'</span>
</code></pre>
<p>Let’s mock our function using <code>jest.spyOn()</code>. The method takes two arguments, the object and the method name as a string.</p>
<pre><code class="lang-python">const mockFetchData = jest.spyOn(services, <span class="hljs-string">'FetchData'</span>)
        .mockImplementation(<span class="hljs-keyword">async</span> () =&gt; {
            <span class="hljs-keyword">return</span> [{
                name: <span class="hljs-string">'kunal'</span>
            }];
        })
</code></pre>
<p><code>spyOn</code> creates an empty mock of the method. Now, we provide our mock implementation where we return some mock data. This will be called when our component renders.</p>
<pre><code class="lang-python">render(&lt;TestingAPICalls/&gt;)
expect(mockFetchData).toHaveBeenCalled();
</code></pre>
<p>We use <code>toHaveBeenCalled()</code> to test if the method was called. It was – so the test passes. Now, to test the behaviour of our component, let’s test if the name was rendered.</p>
<pre><code class="lang-javascript">expect(screen.getByText(<span class="hljs-regexp">/kunal/i</span>)).toBeInTheDocument();
</code></pre>
<p>In this case, the test fails as it is unable to find the element. Why is this so?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-144.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Failed Test: Unable to find the element</em></p>
<p>Since API calls and state updates are asynchronous, the text hasn’t been rendered yet. To test asynchronous behaviour, wrap the query in a <code>waitFor()</code> block.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">await</span> waitFor(<span class="hljs-function">() =&gt;</span> {
    expect(screen.getByText(<span class="hljs-regexp">/kunal/i</span>)).toBeInTheDocument();
})
</code></pre>
<p><code>waitFor</code> does exactly what the name suggests. It waits for the asynchronous behaviour to finish before making the query. Now, our test passes with 100% coverage.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/01/image-145.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>100% Coverage for Testing API Calls</em></p>
<p>You can find the complete code on <a target="_blank" href="https://github.com/KunalN25/react-testing-basics">GitHub</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Phew! That was a long one. But it needed to be. I have explained, through basic examples, how you can start writing tests in React.</p>
<p>This article started from a basic test with a simple assertion and explained how to use mock data to serve the purpose of your test.</p>
<p>I also explained how to simulate user interactions, how to test a component’s behaviour on state updates, and lastly how to test API calls. Hopefully, all these examples help you write tests for your upcoming project.</p>
<p>If you are unable to understand the content or find the explanation unsatisfactory, please let me know. New ideas are always appreciated! Feel free to DM me on <a target="_blank" href="https://www.linkedin.com/in/kunalnk25/">LinkedIn</a> if you want to discuss anything. Until then, Goodbye!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ API Testing Best Practices – How to Test APIs for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ In this digital age, APIs have become the cornerstone of how data is shared and processed.  But many users are often unaware of the fact that they are putting their trust in an API and not a person. This is why it's important to leverage API testing ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/rules-of-api-testing-for-beginners/</link>
                <guid isPermaLink="false">66b8dbfb1ce2b93cb4142f0c</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Hillary Nyakundi ]]>
                </dc:creator>
                <pubDate>Fri, 16 Dec 2022 21:25:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/12/OOP--2-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this digital age, APIs have become the cornerstone of how data is shared and processed. </p>
<p>But many users are often unaware of the fact that they are putting their trust in an API and not a person.</p>
<p>This is why it's important to leverage API testing techniques to ensure that every aspect of your website or application works as expected.</p>
<h2 id="heading-why-api-testing-is-important">Why API Testing is Important</h2>
<p>My first interaction with APIs was while working on a personal project, a <a target="_blank" href="https://github.com/larymak/VirtualAssistant">VirtualAssistant</a>. It required me to fetch real data from third party apps. </p>
<p>As a beginner, the knowledge I acquired was mainly from tutorials I had watched which basically covered how to <code>GET</code>, <code>POST</code> and <code>DELETE</code> data. I didn't know that there were rules that I should follow while working with APIs. </p>
<p>But as time progressed I came to understand the importance of APIs and the major role they play in the daily applications of a developer's job.</p>
<p>Mastering the rules is an essential step in learning about APIs. Most applications today are made up of pieces of software, each of which needs to be tested in its own way. </p>
<p>There are always new and exciting ways to test an application – and we're not just talking about checking for bugs here. It's more about the app functionality.</p>
<p>As technology continues to advance, the trends related to how to test these applications will also change. </p>
<p>In this guide, we will be discussing the trends that apply to the testing of APIs, how they differ from other types of testing, tools we can leverage when it comes to testing APIs, and how you can stay on top of your game when it comes to API testing.</p>
<p>First, let's get an understanding of what we mean by APIs.</p>
<h2 id="heading-what-is-an-api">What is an API?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/i50d3cqyx3ijgcla2tc4.png" alt="APIs" width="600" height="400" loading="lazy"></p>
<p>APIs are designed for developers to use. They are, in essence, a coding tool that allows your application to communicate with other applications. APIs allow you to integrate third-party applications into your work or use your own data and processes in the cloud.</p>
<p>The functionality of any application is defined by the connection it has with the outside world. For this reason, connecting your application with APIs becomes very helpful. </p>
<p>In today's development process, APIs have become an essential part in web and mobile applications. They enable communication of different components of these systems and help you access data and services.</p>
<p>A very important thing to keep in mind is that when dealing with APIs, you need to be sure everything works properly and that you get the correct feedback before integrating them into your applications. That's why testing them is essential.</p>
<p>For this reason, a vital part of testing any type of software requires evaluating whether or not there's a way the app can be broken. In order to ensure a certain level of continuity when using an API in multiple ways simultaneously, you need to conduct API testing.</p>
<p>Now that you have an understanding of what an API and why testing it is important, let's clarify what exactly might we mean by this – is there more to the term testing?</p>
<h2 id="heading-what-is-api-testing">What is API Testing?</h2>
<p>API Testing is a process that checks that an API adheres to the given 
requirements. Achieving this goal is not an easy task and there are many ways in which a tester can go about doing it effectively. </p>
<p>You can conduct API testing manually or automatically, and it is often considered a part of integration testing. </p>
<ul>
<li>Manual API testing involves directly interacting with the API using tools like <a target="_blank" href="https://www.postman.com/">Postman</a>, <a target="_blank" href="https://apitester.org/">API Tester</a> or any other available online tools. </li>
<li>Automated API testing uses specialized software to send requests to the API and then compares the results to the expected behavior.</li>
</ul>
<p>API testing is important because it helps ensure that the various components of a system are working together as expected. By verifying the functionality of the API, you can be confident that the system as a whole will function as intended.</p>
<h2 id="heading-api-testing-tools">API Testing Tools</h2>
<p>You can use API testing tools to test the functionality of an API. They can help you test the performance of an API, as well as to check for security vulnerabilities. </p>
<p>There are a number of different API testing tools available, each with its own advantages and disadvantages. Some of the most popular API testing tools include SoapUI, Postman, Runscope, and API Tester. </p>
<h2 id="heading-api-testing-principles">API Testing Principles</h2>
<p>Having a set of standard rules is the best way to ensure the quality of your APIs and their implementations. You can apply these rules during testing, coding, and development, as well as in production. </p>
<p>Here are some principles that you should keep in mind:</p>
<ol>
<li>API testing should be a part of your continuous integration and delivery pipeline.</li>
<li>API tests should be easy to maintain and write.</li>
<li>A well-designed API will make your tests easier to write.</li>
<li>You should test at the boundary of your system.</li>
<li>Keep your tests small and focused.</li>
<li>Make sure your tests are deterministic.</li>
<li>Run your tests in parallel for speed.</li>
<li>Use the available and freely accessible tools to simplify API testing. </li>
</ol>
<h2 id="heading-how-to-start-api-testing">How to Start API Testing</h2>
<p>There are many different types of tests you can perform on your APIs:</p>
<ul>
<li>Functionality tests focus on ensuring that the API is able to perform its intended functions. </li>
<li>Performance tests measure the response times of the API and check for bottlenecks. </li>
<li>Security tests assess the vulnerability of the API to attack vectors such as SQL injection and cross-site scripting (XSS). </li>
</ul>
<p>To get started with API testing, you will need to have access to an application with an exposed API. You will also need to choose a method for sending requests to the API (manual or automated), and select a tool or framework for writing your tests (if using automated testing).</p>
<p>Once you have these things set up, you can begin writing your test cases and running them against the API.</p>
<h2 id="heading-api-testing-tips">API Testing Tips</h2>
<p>API testing can be a challenge, but regardless of the tools you decide to use here are some tips that can help:</p>
<ol>
<li>Make sure you have a clear understanding of the API before you start testing. Read the documentation and any other available materials. This will help you know what to expect and how the API should work. </li>
<li>Use API testing tools: This will give you a better understanding of how the API works and make it easier to find any issues. </li>
<li>Test all aspects of the API, including input validation, error handling, and security. These are all important factors in ensuring that your API is working correctly.</li>
<li>Keep your tests up-to-date as the API changes. This will help ensure that you catch any new issues that may arise. </li>
<li>Use mobile apps for API testing. Things are changing all the time these days, where many people are working remotely, and from their phones too. By using mobile apps you can increase your productivity, become more mobile, and work from anywhere in the world. A good example of such a tool is the <a target="_blank" href="https://apitester.org/">API Tester</a>. </li>
</ol>
<p>By following these tips, you can help make sure that your API testing is effective and efficient. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>API testing is a key part of ensuring the quality of your software. By following these highlighted rules you will be able to ensure your tests are functional, secure and have a reliable performance. </p>
<p>This will in turn help you improve the overall quality of your software applications and provide a better user experience.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React Unit Test Handbook + Redux Testing Toolkit ]]>
                </title>
                <description>
                    <![CDATA[ In this step-by-step tutorial, you'll learn how to easily start with Unit Tests in React. You'll learn how to write tests for Redux states as well as fundamental Redux slice tests using the React Testing Library and Jest. 🔐 Here's What We'll Cover ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-write-unit-tests-in-react-redux/</link>
                <guid isPermaLink="false">66bb8f42fce17a7d998852db</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unit testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Matthes B. ]]>
                </dc:creator>
                <pubDate>Wed, 09 Nov 2022 15:32:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/pexels-scott-webb-1527893.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this step-by-step tutorial, you'll learn how to easily start with Unit Tests in React. You'll learn how to write tests for Redux states as well as fundamental Redux slice tests using the React Testing Library and Jest.</p>
<h2 id="heading-heres-what-well-cover"><strong>🔐 Here's What We'll Cover</strong></h2>
<ul>
<li>You'll see how easy it is to set up your first unit tests in React.</li>
<li>You'll improve your general React knowledge.</li>
<li>You'll get the hang of why Test Driven Development (TDD) is helpful for your coding workflow.</li>
</ul>
<h2 id="heading-prerequisites"><strong>📝</strong> Prerequisites</h2>
<ul>
<li>You should be familiar with the basic React workflow structure (including functional components and hooks).</li>
<li>You should have a basic knowledge of Redux (I'm using Redux Toolkit for this guide).</li>
<li>You don't need any prior knowledge about testing.</li>
<li>I'm using the <code>npm</code> installation approach, not the <code>yarn</code> one.</li>
</ul>
<h2 id="heading-the-objective"><strong>🎯</strong> The Objective</h2>
<p>While learning advanced concepts of React, you'll probably stumble across the topic of testing. Being able to work with automatic tests is also quite handy for any upcoming frontend developers. </p>
<p>However, as I myself was learning React, I had a hard time finding information about how to implement tests for libraries like Redux (even though it's a library I work with all the time). </p>
<p>Beyond that, I found that doing any component testing in React is basically unfeasible when you don't know how to work with the Redux library.</p>
<p>So I took some time to read the Redux documentation and went back and forth with it a bit. Then I decided to write a practical starter guide for unit testing in React, including Redux, to share what I learned. </p>
<p>Since I would like to take a modern approach, I'm also going to use the Redux Toolkit. We'll cover the Redux implementation in this guide.</p>
<h3 id="heading-what-well-cover">What we'll cover:</h3>
<p>To start off, I will provide some general information about tests before I directly go into creating the first general unit tests. </p>
<p>Next I'll give a quick overview of how to implement some Redux Toolkit logic. </p>
<p>Then we will work on some unit tests within an application which uses Redux Toolkit. For this step we will adjust our previously created tests to the new Redux environment.</p>
<p>This is a step-by-step guide. If you are new to tests, I recommend following this guide in order from top to bottom.</p>
<p>I have also created a <a target="_blank" href="https://github.com/Matthes-Baer/unit-test-redux-article-app">public GitHub repository</a> for this guide with some commentary. You can use that if you want to look something up without scrolling through this guide in its full length again.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#what-different-kinds-of-tests-are-there">What Different Kinds of Tests Are There?</a></li>
<li><a class="post-section-overview" href="#how-to-set-up-your-react-testing-environment">How to Set Up Your React Testing Environment</a></li>
<li><a class="post-section-overview" href="#heading-check-out-your-created-react-application">Check Out Your Created React Application</a></li>
<li><a class="post-section-overview" href="#how-to-create-your-first-unit-test">How to Create Your First Unit Test</a></li>
<li><a class="post-section-overview" href="#how-to-create-a-failing-test-on-purpose">How to Create a Failing Test on Purpose</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-some-additional-tests">How to Create Some Additional Tests</a></li>
<li><a class="post-section-overview" href="#heading-how-to-perform-testing-with-the-react-redux-toolkit">How to Perform Testing with the React Redux Toolkit</a></li>
<li><a class="post-section-overview" href="#heading-outlook-for-advanced-testing">Outlook for Advanced Testing</a></li>
</ol>
<h2 id="heading-what-different-kinds-of-tests-are-there">📋 What Different Kinds of Tests Are There?</h2>
<p>This quick guide won't provide you with detailed theoretical knowledge about all the <a target="_blank" href="https://www.freecodecamp.org/news/types-of-software-testing/">different kinds of testing out there</a>. At this point, you should just understand that there are generally three kinds of tests:</p>
<ul>
<li>Unit Tests</li>
<li>Integration Tests</li>
<li>End-To-End Tests</li>
</ul>
<p>To put it in simple words: You can see these three types of tests as generally increasing in their complexity. </p>
<p>While <a target="_blank" href="https://www.freecodecamp.org/news/unit-tests-explained/">unit tests</a> cover single functions and components, integration tests typically focus on multiple functions and their connections to each other. <a target="_blank" href="https://www.freecodecamp.org/news/end-to-end-testing-tutorial/">End-to-end tests</a> are even more complex and give insights about multiple function and component structures. </p>
<p>There are other test concepts, but these three are the most important ones for web developers, for example.</p>
<p>Again, this is really put in simple words. But in this case it's sufficient to know that unit tests are basically the least complex tests out of these three. </p>
<p>It's also quite easy to work with unit tests as soon as you have a basic understanding of how testing in general works.</p>
<p>I would also like to quickly emphasize that there are mainly two ways of testing your application.</p>
<ul>
<li>Manual Testing</li>
<li>Automatic Testing</li>
</ul>
<p>Manual Testing is pretty much what you probably already do for all of your application which you create. When manually testing your application, you basically start your React application with <code>npm run start</code> and actually click on buttons to check if the corresponding function works.</p>
<p>Automatic Tests, on the other hand, are pretty much functions you create which automatically check your application to see if the respective steps work that you defined within these tests. This automatic kind of testing is extremely important for larger projects. </p>
<p>With this automatic approach, it's also way easier to scale your tests. In the end, you have a lot of tests which automatically test your whole application in a relatively short amount of time. These test can help you spot any potential errors which might have occurred during development. This would take much more time if you were to go back constantly to manually test your application.</p>
<p>Being able to work with automatic tests is also typically a big plus for your résumé as a frontend developer.</p>
<h2 id="heading-how-to-set-up-your-react-testing-environment">🔧 How to Set Up Your React Testing Environment</h2>
<p>In order to get a practical start, we are going to directly dive into our React application. </p>
<p>You will see that the setup of a testing environment is relatively easy in React – or, to be more precise, React does it all for you during the regular install setup.</p>
<p>Therefore, I'm creating a React application with the following line:</p>
<p><code>npx create-react-app &lt;name of your application&gt;</code></p>
<p>After this step, we need everything that should be added for using Redux in our React application:</p>
<ul>
<li><strong>React Redux:</strong> <code>npm install react-redux</code> (provides some mandatory hooks, for example)</li>
<li><strong>React Redux Toolkit:</strong> <code>npm install @reduxjs/toolkit</code> (provides logic for creating a store or slices, for example)</li>
</ul>
<p>It's worth mentioning that there also is the <strong>Redux core</strong> (<code>npm install redux</code>). But this is already part of the React Redux Toolkit installation, so we don't have to install it here too. </p>
<p>If you wanted to use React without the React Redux Toolkit, then you would have to separately reach out to the Redux core installation.</p>
<p>You can also create a new React application from scratch with <code>npx create-react-app my-app --template redux</code> which includes the React Redux Toolkit, the React core, React Redux, as well as a template from the React Redux Toolkit. </p>
<p>Choose this approach if you don't have any existing React applications, since it's probably more convenient.</p>
<p>Under the hood, you now have an application that uses the <strong>React Testing library</strong> combined with <strong>Jest</strong> (a testing framework). Together, they have pretty much everything that you will need for testing your React application. </p>
<p>You don't have to install anything else for this purpose. These tools come out of the box with a standard React installation.</p>
<h2 id="heading-check-out-your-created-react-application">🔎 Check Out Your Created React Application</h2>
<p>As you go into your newly created React application, you will find the folder and file structure you are likely familiar with. Besides others, there is the <code>App.js</code> file, which is created like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> logo <span class="hljs-keyword">from</span> <span class="hljs-string">'./logo.svg'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-header"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{logo}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App-logo"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"logo"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          Edit <span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>src/App.js<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span> and save to reload.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"App-link"</span>
          <span class="hljs-attr">href</span>=<span class="hljs-string">"https://reactjs.org"</span>
          <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
          <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>
        &gt;</span>
          Learn React
        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Within the <code>src</code> folder, you also have the file <code>App.test.js</code>. This file is actually a first test that came out of the box with React installation. This file is structured like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span>;

test(<span class="hljs-string">'renders learn react link'</span>, <span class="hljs-function">() =&gt;</span> {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
  <span class="hljs-keyword">const</span> linkElement = screen.getByText(<span class="hljs-regexp">/learn react/i</span>);
  expect(linkElement).toBeInTheDocument();
});
</code></pre>
<p>Even without fully understanding what <code>render</code> or <code>screen</code> is, for example, we can see that something is going on with our <code>App</code> component in there. In fact, this is a unit test that is focusing on a specific part of the <code>App</code> component.</p>
<p>While this first template for a test is a handy representation of what a test looks like, I would like to create a test file from scratch.</p>
<p>Generally speaking, tests are separated into different test suites. These test suites are typically a group of tests that focus on the same component, for example. Tests within the same test suite basically have the same superordinate topic.</p>
<p>To check this, try to enter <code>npm run test</code> in your terminal when you are within your React application. </p>
<p>It could say something like "There are no new tests or changes since the last commit" – in this case, just enter <code>a</code> in the terminal to run all tests regardless.</p>
<p>In the end, you should be able to see this within the terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-124.png" alt="Image" width="600" height="400" loading="lazy">
<em>Result of <code>npm run test</code></em></p>
<p>At the top, you can see that the <code>App.test.js</code> file passed. Basically, all tests within this file were successful. </p>
<p>Below that, you can see <code>renders learn react link</code>: This is the description for this particular test, which we can define individually. We will get back to this later.</p>
<p>Further down, we can finally see the test suites and tests. As you can see, we have one test suite and one test. To be more precise, we have one test suite that includes this one test. </p>
<p>Later on, you will recognize that we will use like 1-3 test suites while there will be around 5+ tests, for example. Again, test suites basically provide a structure that groups single tests together.</p>
<p>The stuff with the snapshots is not important for your specific case.</p>
<p><a target="_blank" href="https://jestjs.io/docs/snapshot-testing">Snapshots</a> are an advanced concept for testing. So a reference snapshot (like an image that was taken) is being compared with the version after some actions took place. This can help to check whether the UI stays the same after some actions or if some changes happened all of a sudden.</p>
<p>I won't focus on testing with snapshots in this article. This is a topic that you might want to look up after understanding some unit testing basics.</p>
<h2 id="heading-how-to-create-your-first-unit-test">🔨 How to Create Your First Unit Test</h2>
<p>Now that we've looked at a unit test, lets dive into the first test which we'll build from scratch on our own.</p>
<p>For that, I would like to create a new folder called <code>__tests__</code>. This is common when you are working with tests or checking out other applications. </p>
<p>I'm also dragging the already-available <code>App.test.js</code> file into this folder. This doesn't change anything about the result.</p>
<p>Our folder structure now looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-125.png" alt="Image" width="600" height="400" loading="lazy">
<em>general folder structure with <code>__tests__</code></em></p>
<p>Within <code>__tests__</code>, we create the file <code>myFirstTesting.test.js</code>. We need this file structure of <code>&lt;test name&gt;.test.js</code>. You can also create a test file with <code>&lt;test name&gt;.spec.js</code> – both approaches will work the same.</p>
<p>Our first step is to import the <code>App.js</code> component: <code>import App from "../App";</code>.</p>
<p>To create our first test, we have to make use of the <code>test()</code> function. You could also use <code>it()</code>. Both will achieve the same result. </p>
<p>The first parameter of this function has to be a string, which describes what we are going to test (remember the stuff with "renders learn react link" within the test file we viewed?). This is going to help you have a more precise overview after running all the tests. </p>
<p>In this case, I will use the description <code>"renders logo in App component"</code>. The second parameter is another function for which we are using an anonymous arrow function. Our <code>myFirstTesting.test.js</code> file now looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;

test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {

})
</code></pre>
<p>Even though there is not much going on, let's try entering <code>npm run test</code> again. We will find the following result in our terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-126.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Therefore, we now have two test files, resulting in two test suites and two tests.</p>
<p>Now we would actually like to test something. Since we added the description <code>"renders logo in App component"</code>, we are going to test exactly that. </p>
<p>In order to do that, we need the <code>render()</code> function, which we'll use whenever we actually want to render a component from our application.</p>
<p>In order to add the <code>render()</code> function, we have to import it from the React Testing library, which is already part of our React application without any other installations. </p>
<p>While we're at this step of importing, let's also import <code>screen</code> (also part of the React Testing library). It provides access to different functions that will look through the current screen after something gets rendered and find specific elements, for example.</p>
<p>After adding these two imports, our <code>myFirstTesting.test.js</code> file now looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;

test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {

})
</code></pre>
<p>Now that we have all that, let's actually start working on our test.</p>
<p>First of all, we need to render our component. Remember that tests are self-contained and don't know that we have an <code>App.js</code> with the corresponding content in our React application. We have to individually tell the specific test that a component exists by rendering it with <code>render()</code> at the top of the test. This is going to look like this: <code>render(&lt;App /&gt;);</code>.</p>
<p>Now that we have rendered the <code>App.js</code> component in this test, we should try to check if a specific content part can be found by the test. This way, we can actually test if <code>App.js</code> was rendered like it was supposed to be. </p>
<p>Assuming something went wrong, we would not be able to find the React logo, for example, which is currently part of the <code>App.js</code> component.</p>
<p>So we will try to find this logo, which is an <code>img</code> element. To do this, we can make use of the <code>getByAltText()</code> function that finds an element by its specific <code>alt</code> attribute, which is commonly utilized for images. We have access to this function with <code>screen</code> that we imported earlier. </p>
<p>We now have this expression: <code>screen.getByAltText("logo")</code>. So the test looks at the screen where we rendered the <code>App.js</code> component beforehand, and then gets an element, which has an <code>alt</code> attribute of <code>"logo"</code>. We will connect all this to a variable. </p>
<p>Our test file now looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;

test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
  <span class="hljs-keyword">const</span> image = screen.getByAltText(<span class="hljs-string">"logo"</span>);
})
</code></pre>
<p>There are a bunch of different functions like <code>getByAltText()</code> that you can use to look for elements with a specific text content, a specific role like a button, or even with a test id that you can add to the actual element.</p>
<p>You also have the opportunity to look for multiple elements. Besides that, you don't have to use a string as parameter. A regular expression with <code>/logo/i</code> is also feasible, for example. We will use different ways to find elements throughout this starter guide.</p>
<p>For the last step, we have to utilize <code>expect()</code>, which we use to see what behavior we can expect. In this case, we expect that our <code>image</code> variable is part of the component and therefore exists. </p>
<p>For this approach, our file would look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;

test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
  <span class="hljs-keyword">const</span> image = screen.getByAltText(<span class="hljs-string">"logo"</span>);
  expect(image).toBeInTheDocument();
})
</code></pre>
<h2 id="heading-how-to-create-a-failing-test-on-purpose">❗ How to Create a Failing Test on Purpose</h2>
<p>If we now run our tests with <code>npm run test</code>, everything will pass. Now, let's try to reverse this logic so that we actually create a failing test. This way, we can check if this test actually has some impact or not. </p>
<p>To do this, we can go into our <code>App.js</code> file and change the <code>alt</code> attribute for the logo image. If you change it to <code>alt="loo"</code>, the test will fail and it'll give you some information.</p>
<p>In our case, though, I would like to change something on the test itself to make it fail and show you another expression that is handy to know. Instead of <code>expect(image).toBeInTheDocument();</code> we can also type <code>expect(image).not.toBeInTheDocument();</code>. So here we added a <code>not</code>. This basically reversed the logic, and now the test expects that no such image element exists.</p>
<p>If we now try to run the test, we will find the following error message in our terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-127.png" alt="Image" width="600" height="400" loading="lazy">
<em>The error message we get</em></p>
<p>You can see that the test expected that there was no such element as <code>image</code>. However, it found something and therefore answered with an error message.</p>
<p>You don't have to make all your tests purposely fail to check if they work or not. I just wanted to show you what an actual failing test would look like.</p>
<h2 id="heading-how-to-create-some-additional-tests">✏️ How to Create Some Additional Tests</h2>
<p>Now we have finished with our first test and have some basic knowledge about what to expect when working with unit tests. Next, we will check out some other test examples.</p>
<p>In order to create a more realistic scenario, I will add one additional component, which we'll insert into the <code>App.js</code> component.</p>
<p>For this step, first we create a folder called <code>components</code> in our <code>src</code> folder. This is not a must, but it is common to structure your files like that.</p>
<p>Within the <code>components</code> folder, we create <code>List.js</code>. Our folder structure now looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-155.png" alt="Image" width="600" height="400" loading="lazy">
<em>current folder structure</em></p>
<p>Now, let's try to follow more of a test driven development (TDD) workflow, which is quite modern. I'm not necessarily sayint that this is always recommended. But a TDD approach is considered best practice by more and more people nowadays. </p>
<p>Of course, in this tutorial we'lre "only" talking about unit tests and not integration or end-to-end tests, but the general TDD workflow is similar for all three test categories.</p>
<p>So using this test driven development approach, we basically add tests and work on our application simultaneously. </p>
<p>To be more precise, we even create tests for single components and function parts before you even implement this tested logic in your application. </p>
<p>So there is a lot of going back and forth instead of creating all the tests at once at the end.</p>
<h3 id="heading-how-to-start-the-setup-for-listjs">How to Start the Setup for <code>List.js</code></h3>
<p>In our example, we have added the <code>List.js</code> component. Within this component, I would like to add a list with a button. When a user clicks on the button, it adds something to the list (an object with multiple keys and values).</p>
<p>In order to have some sort of frame, I will first add some <code>div</code> elements and similar stuff to our <code>List.js</code> component before we dive into the actual logic.</p>
<p>The <code>List.js</code> component now looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> List = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginLeft:</span> "<span class="hljs-attr">auto</span>", <span class="hljs-attr">marginTop:</span> "<span class="hljs-attr">500px</span>", <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">500px</span>" }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>This is a list<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">listStyleType:</span> "<span class="hljs-attr">none</span>" }}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>This is the first list entry<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>This button can add a new entry to the list<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> List;
</code></pre>
<p>I also added the <code>List.js</code> component as a child to <code>App.js</code> (below all the other stuff in <code>App.js</code>) so it will be visible without changing anything else.</p>
<p>The result looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-156.png" alt="Image" width="600" height="400" loading="lazy">
<em>How it looks</em></p>
<p>This won't win you any style competitions but it's sufficient for our case.</p>
<h3 id="heading-setup-for-the-test-for-listjs">Setup for the test for <code>List.js</code></h3>
<p>Since we want to test while we are working on our component, I will now jump directly to the testing part even if nothing really happened in our <code>List.js</code> component in terms of click functions, for example.</p>
<p>We could create a new test file, but I would like to show you a new function we can use for our test suites specifically. This function is called <code>describe()</code> and can be handy for further structuring our tests.</p>
<p>To use <code>describe()</code>, we jump to <code>myFirstTesting.test.js</code> within <code>__tests__</code>. Right now, this file basically serves as one test suite for the test we specifically created for the <code>App.js</code> component. But I would like to have two test suites within this test file: one for the <code>App.js</code> tests and one for the <code>List.js</code> tests.</p>
<p>For this step, I'm using the <code>describe()</code> function, which basically works like the <code>test()</code> function in terms of parameters. </p>
<p>The first parameter will be a string, describing the respective test suite. The second parameter is a function, which then includes our <code>test()</code> functions with their stuff.</p>
<p>It will look like this in our case:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;

describe(<span class="hljs-string">"App.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> image = screen.getByAltText(<span class="hljs-string">"logo"</span>);
    expect(image).toBeInTheDocument();
  });
});

describe(<span class="hljs-string">"List.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"example"</span>, <span class="hljs-function">() =&gt;</span> {});
});
</code></pre>
<p>Before we jump into this new test, I would actually like to add something for the <code>App.js</code> testing. Since we have this <code>describe()</code> block, we could just add a new <code>test()</code> function – and this is what I'm going to do.</p>
<p>See the following newly added test described with <code>"renders List.js component in App.js"</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;

describe(<span class="hljs-string">"App.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> image = screen.getByAltText(<span class="hljs-string">"logo"</span>);
    expect(image).toBeInTheDocument();
  });

   test(<span class="hljs-string">"renders List.js component in App.js"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> textInListJS = screen.getByText(<span class="hljs-regexp">/This is a list/i</span>);
    expect(textInListJS).toBeInTheDocument();
  });
});

describe(<span class="hljs-string">"List.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"example"</span>, <span class="hljs-function">() =&gt;</span> {});
});
</code></pre>
<p>So I'm rendering the <code>App.js</code> component and looking for text via a regular expression, which is part of the <code>List.js</code> component. This test can basically be understood as a render test for <code>List.js</code>. If <code>List.js</code> had not been able to be rendered within <code>App.js</code>, this test would not pass.</p>
<p>If you are confused that this works without separately using <code>render()</code> on the <code>List.js</code> component, remember that <code>List.js</code> is part of <code>App.js</code> and everything inside <code>App.js</code> will be rendered under typical conditions. If you tried to look for a text phrase that doesn't exist in <code>List.js</code>, this new test would fail. Right now, in our case, it passes.</p>
<p>I would also like to emphasize that you can have multiple <code>expect()</code> functions within the same test. Therefore, we also could have structured this new test like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;

describe(<span class="hljs-string">"App.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> image = screen.getByAltText(<span class="hljs-string">"logo"</span>);
    <span class="hljs-keyword">const</span> textInListJS = screen.getByText(<span class="hljs-regexp">/This is a list/i</span>);

    expect(image).toBeInTheDocument();
    expect(textInListJS).toBeInTheDocument();
  });
});

describe(<span class="hljs-string">"List.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"example"</span>, <span class="hljs-function">() =&gt;</span> {});
});
</code></pre>
<p>This would also work perfectly fine in our case. And this can be handy in situations where you test for some small elements which are directly connected to each other and have the same requirements to be rendered. </p>
<p>But keep in mind that in our case, we should have adjusted the description for this test. That's because <code>"renders logo in App component"</code> is not correct anymore if we are testing more than that in this test. So let's head back to the structure with two separate tests for now. But have in mind that you are able to work like this.</p>
<h3 id="heading-back-to-the-test-for-listjs">Back to the test for <code>List.js</code></h3>
<p>Now I would like to work with the second <code>describe()</code> block that we created a few moments ago, where we want to work with tests specifically for the <code>List.js</code> component.</p>
<p>Since we are aiming for a test-driven development approach, we should think about what we are going to build, write a test, and then implement that logic in our component.</p>
<p>We want to create a simple list in our <code>List.js</code> component. So there will be an array, which we will go through with <code>map()</code>. </p>
<p>For this approach, we will utilize the <code>useState()</code> hook so we have a state that can dynamically adjust (the array of list items). Our first test will be to check if the length of this array in its initial state is equal to <code>1</code>.</p>
<p>To find the items within this state, we will make use of the <code>getAllByTestId()</code> method, which allows us to search for specific elements we marked with a <code>data-testid</code> in the frontend. </p>
<p>The test with the description <code>"renders initial state of listData state"</code> that I created is now included:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;

describe(<span class="hljs-string">"App.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> image = screen.getByAltText(<span class="hljs-string">"logo"</span>);
    <span class="hljs-keyword">const</span> textInListJS = screen.getByText(<span class="hljs-regexp">/This is a list/i</span>);

    expect(image).toBeInTheDocument();
    expect(textInListJS).toBeInTheDocument();
  });
});

describe(<span class="hljs-string">"List.js component"</span>, <span class="hljs-function">() =&gt;</span> {
   test(<span class="hljs-string">"renders initial state of listData state"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">List</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> list = screen.getAllByTestId(<span class="hljs-string">"list-item"</span>);
    expect(list.length).toEqual(<span class="hljs-number">1</span>);
  });
});
</code></pre>
<p>Right now this test will fail, of course, because we haven't added any of this logic to the component yet.</p>
<p>So I adjusted the <code>List.js</code> component. It now looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> List = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> initialState = [
    {
      <span class="hljs-attr">id</span>: <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getSeconds()}</span>`</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"This is something"</span>,
      <span class="hljs-attr">significance</span>: <span class="hljs-number">7</span>,
    },
  ];
  <span class="hljs-keyword">const</span> [listData, setListData] = useState(initialState);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginLeft:</span> "<span class="hljs-attr">auto</span>", <span class="hljs-attr">marginTop:</span> "<span class="hljs-attr">500px</span>", <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">500px</span>" }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>This is a list<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">listStyleType:</span> "<span class="hljs-attr">none</span>" }}&gt;</span>
        {listData.map((listItem) =&gt; {
          return (
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{listItem.id}</span> <span class="hljs-attr">data-testid</span>=<span class="hljs-string">"list-item"</span>&gt;</span>
              {listItem.description}
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          );
        })}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>This button can add a new entry to the list<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> List;
</code></pre>
<p>Newly added was the <code>listData</code> state array via a <code>useState()</code> hook as well as an initialState, which I initialized with one object at the very top. I also made use of the <code>map()</code> function to go through this <code>listData</code> to create a list. </p>
<p>For each <code>&lt;li&gt;</code> element, I'm adding a key and a <code>data-testid</code>. This <code>data-testid</code> is the id we need for our test to find the respective elements.</p>
<p>On our actual application, we can see the <code>listItem.description</code> for this initial state:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-165.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>So by manually testing (actually looking at our application in the browser), we can see that this should work. If we now run our tests, we will also see that the test we created passed.</p>
<h3 id="heading-how-to-add-an-object-to-the-state">How to add an object to the state</h3>
<p>Now let's test something more exciting: the logic to add a new object to this <code>listData</code> state. Again, we will start by working on our test first before actually implementing the required logic within the React component.</p>
<p>With this newly added test described by <code>"adds a new data entry to listData after button click"</code>, our test file now looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> List <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/List"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;

<span class="hljs-keyword">import</span> userEvent <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/user-event"</span>;

describe(<span class="hljs-string">"App.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"renders logo in App component"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> image = screen.getByAltText(<span class="hljs-string">"logo"</span>);
    expect(image).toBeInTheDocument();
  });

  test(<span class="hljs-string">"renders List.js component in App.js"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> textInListJS = screen.getByText(<span class="hljs-regexp">/This is a list/i</span>);
    expect(textInListJS).toBeInTheDocument();
  });
});

describe(<span class="hljs-string">"List.js component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"renders initial state of listData state"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">List</span> /&gt;</span></span>);
    <span class="hljs-keyword">const</span> list = screen.getAllByTestId(<span class="hljs-string">"list-item"</span>);
    expect(list.length).toEqual(<span class="hljs-number">1</span>);
  });

  test(<span class="hljs-string">"adds a new data entry to listData after button click"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">List</span> /&gt;</span></span>);
    <span class="hljs-keyword">let</span> listItems = screen.getAllByTestId(<span class="hljs-string">"list-item"</span>);
    <span class="hljs-keyword">const</span> button = screen.getByRole(<span class="hljs-string">"button"</span>, {
      <span class="hljs-attr">name</span>: <span class="hljs-regexp">/This button can add a new entry to the list/i</span>,
    });

    expect(list.length).toEqual(<span class="hljs-number">1</span>);
    userEvent.click(button);
    list = screen.getAllByTestId(<span class="hljs-string">"list-item"</span>);
    expect(list.length).toEqual(<span class="hljs-number">2</span>);
  });
});
</code></pre>
<p>At the bottom, you can see this test. Therefore, we are first rendering the <code>List.js</code> component before looking for all available list items we assigned a test id to. You will see exactly where we put the test id in a few moments.</p>
<p>We also have to look for the button that we want to test to see if clicking on it adds something to the list. We do this with <code>getByRole()</code> which expects a role like <code>"button"</code> or <code>"table"</code> as a first parameter, for example (there are a bunch of different roles you can target). The second parameter is optional and is an object that can receive a value for the <code>name</code> key.</p>
<p><code>name</code> is pretty much the text content we have specifically for the button in this case. This optional second parameter is handy when you have multiple elements of type <code>"button"</code> in your component and want to get a specific button out of these.</p>
<p>After getting the <code>listItems</code> as well as the <code>button</code>, we start off with a first <code>expect()</code> to basically test the initial state. In this initial state, we expect to have only one list item.</p>
<p>Then, with the help of <code>userEvent</code>, we are going to click on the button. You could also use <code>fireEvent</code> for this situation (<code>userEvent</code> is still pretty new compared to the <code>fireEvent</code> approach). Both will work, and both are helpful for any action where you want to interact with specific elements. In this case, I want to simulate clicking on a button.</p>
<p>Tests generally follow a "arrange -&gt; act -&gt; assert" pattern that you can follow to structure them. Within the "arrange" part, you initialize and get all required elements. With the "act" part, you would simulate a mouse click (as in our case), for example. With "assert," you are checking if it all behaves like you expected it to.</p>
<p>In another case, you could simulate changing the value of an input field with <code>fireEvent.change(inputField, { target: { value: someValueVariable } })</code>, for example. Maybe you want to focus an input field or even drag an element - such actions can be simulated via <code>fireEvent</code> and <code>userEvent</code>.</p>
<p>After the button click, it again looked for all <code>listItems</code> since the current value of this variable would still be <code>1</code> from the previous initialization. As soon as this step is completed, it uses another <code>expect()</code> function to check if the length of the <code>listItems</code> array is now equal to <code>2</code> and not <code>1</code>.</p>
<p>Now that we have our test logic, let's jump back to the <code>List.js</code> component and implement the corresponding logic:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> List = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> initialState = [
    {
      <span class="hljs-attr">id</span>: <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getSeconds()}</span>`</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"This is something"</span>,
      <span class="hljs-attr">significance</span>: <span class="hljs-number">7</span>,
    },
  ];
  <span class="hljs-keyword">const</span> [listData, setListData] = useState(initialState);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginLeft:</span> "<span class="hljs-attr">auto</span>", <span class="hljs-attr">marginTop:</span> "<span class="hljs-attr">500px</span>", <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">500px</span>" }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>This is a list<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">listStyleType:</span> "<span class="hljs-attr">none</span>" }}&gt;</span>
        {listData.map((listItem) =&gt; {
          return (
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{listItem.id}</span> <span class="hljs-attr">data-testid</span>=<span class="hljs-string">"list-item"</span>&gt;</span>
              {listItem.description}
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          );
        })}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
          setListData([
            ...listData,
            { id: 999, description: "999", significance: 100 },
          ])
        }
      &gt;
        This button can add a new entry to the list
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> List;
</code></pre>
<p>The only part that changed is the button at the bottom of this file. So I added a function that gets invoked when clicking on this button. The function then adjusts the current state of <code>listData</code> which is responsible for rendering our list. I copied the current state with a spread operator and then added another hard-coded object as the new entry for this list. </p>
<p>Of course, there are more creative ways to fill in the values for the <code>id</code>, <code>description</code>, and <code>significance</code> keys.</p>
<p>I would also like to emphasize that you have the opportunity to create a separate function outside of the <code>return()</code> and access this function like this: <code>onClick={separateFunctionToAddObjectToState}</code> on the same button element. This would also work without having to render something additional within the test.</p>
<p>If we now run our test, we will see that it passes. If you try to still expect a length of <code>1</code> after clicking on the button, the test will fail like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-192.png" alt="Image" width="600" height="400" loading="lazy">
<em>error alert for length of 1</em></p>
<p>So it actually does what it is supposed to do.</p>
<h2 id="heading-setup-for-redux">🔧 Setup for Redux</h2>
<p>After working with local states via the <code>useState()</code> hook, I would like to work on the same files and adjust them for Redux (or the Redux Toolkit, to be precise). </p>
<p>I'm not going to dive deep into what Redux actually is and what every term like <code>action</code>, <code>store</code>, or <code>reducer</code> means in detail – since this would be worthy of a whole new guide. If you want that, you can <a target="_blank" href="https://www.freecodecamp.org/news/redux-for-beginners/">read this guide to Redux basics</a>.</p>
<p>Instead, I will give just a quick rundown and show which files I'm adding and editing. Then I'll talk about how to handle the <code>render()</code> method, including the Redux store provider, which can cause a lot of frustration when testing if you don't know about it.</p>
<h3 id="heading-overall-folder-structure">Overall folder structure:</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-14.png" alt="Image" width="600" height="400" loading="lazy">
<em>current overall folder structure with the React Redux Toolkit</em></p>
<p>You can see that I added an <code>app</code> (for the store) as well as a <code>features</code> (for the slice) folder.</p>
<h3 id="heading-updated-indexjs-file">Updated <code>index.js</code> file:</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App"</span>;
<span class="hljs-keyword">import</span> reportWebVitals <span class="hljs-keyword">from</span> <span class="hljs-string">"./reportWebVitals"</span>;
<span class="hljs-keyword">import</span> { Provider } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;
<span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">"./app/store"</span>;

<span class="hljs-keyword">const</span> root = ReactDOM.createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>));
root.render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Provider</span> <span class="hljs-attr">store</span>=<span class="hljs-string">{store}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>
);
</code></pre>
<p>You can see that I added a <code>provider</code> and wrapped it around the application so we have access to the store from anywhere.</p>
<h3 id="heading-created-storejs-file">Created <code>store.js</code> file:</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { configureStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;
<span class="hljs-keyword">import</span> { ListSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"../features/ListSlice"</span>;

<span class="hljs-keyword">const</span> store = configureStore({
  <span class="hljs-attr">reducer</span>: {
    <span class="hljs-attr">listReducers</span>: listSlice.reducer,
  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> store;
</code></pre>
<p>In this file, we have created the required store for the Redux implementation.</p>
<h3 id="heading-created-listslicejs-file-in-features-folder">Created <code>ListSlice.js</code> file in <code>features</code> folder:</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> initialState = {
  <span class="hljs-attr">value</span>: [
    {
      <span class="hljs-attr">id</span>: <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getSeconds()}</span>`</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"This is something"</span>,
      <span class="hljs-attr">significance</span>: <span class="hljs-number">7</span>,
    },
  ],
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ListSlice = createSlice({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"listReducers"</span>,
  initialState,
  <span class="hljs-attr">reducers</span>: {},
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { } = ListSlice.actions;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ListSlice.reducer;
</code></pre>
<p>Here we have created the slice that we added to the store. Notice that I haven't added any reducer yet. This slice just contains the current corresponding state.</p>
<h3 id="heading-updated-listjs-file-in-components-folder">Updated <code>List.js</code> file in <code>components</code> folder:</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useSelector, useDispatch } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;

<span class="hljs-keyword">const</span> List = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> listState = useSelector(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.listReducers.value);
  <span class="hljs-keyword">const</span> dispatch = useDispatch(); <span class="hljs-comment">// not used right now</span>

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginLeft:</span> "<span class="hljs-attr">auto</span>", <span class="hljs-attr">marginTop:</span> "<span class="hljs-attr">500px</span>", <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">500px</span>" }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>This is a list<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">listStyleType:</span> "<span class="hljs-attr">none</span>" }}&gt;</span>
        {listState.map((listItem) =&gt; {
          return (
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{listItem.id}</span> <span class="hljs-attr">data-testid</span>=<span class="hljs-string">"list-item"</span>&gt;</span>
              {listItem.description}
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          );
        })}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>This button can add a new entry to the list<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> List;
</code></pre>
<p>On the frontend, we swapped the local state (using the useState hook) with the Redux state (using the useSelctor hook). You'll also see that I adjusted the button. There is no click function anymore (we will get back to that later on).</p>
<h2 id="heading-how-to-perform-testing-with-the-react-redux-toolkit">🔎 How to Perform Testing with the React Redux Toolkit</h2>
<p>Now that we have updated and created all the necessary files for the React Redux Toolkit logic, I would like to run a quick test of all the tests we previously created.</p>
<p>The result is that all tests have failed:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-15.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Keep in mind that I adjusted the button in <code>List.js</code>, for example, so the corresponding test was expected to fail. However, not all tests should have failed.</p>
<p>The test environments are working in their own world. They don't know if you wrap a provider somewhere in <code>index.js</code> and enable Redux logic. So the tests are still trying to make the rendering work without Redux. But our application now depends on Redux to manage our main state.</p>
<p>This means that we have to adjust the <code>render()</code> function so that this function is actually aligned with the Redux logic.</p>
<p>A method to make this work is to introduce a helper function, which we will store in a new folder called <code>utils</code>. The file will be called <code>utils-for-tests.jsx</code>. The content will look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { render } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;
<span class="hljs-keyword">import</span> { configureStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@reduxjs/toolkit"</span>;
<span class="hljs-keyword">import</span> { Provider } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;
<span class="hljs-comment">// As a basic setup, import your same slice reducers</span>
<span class="hljs-keyword">import</span> { ListSlice } <span class="hljs-keyword">from</span> <span class="hljs-string">"../features/ListSlice"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderWithProviders</span>(<span class="hljs-params">
  ui,
  {
    preloadedState = {},
    <span class="hljs-regexp">//</span> Automatically create a store instance if no store was passed in
    store = configureStore({
      reducer: { listReducers: ListSlice.reducer },
      preloadedState,
    }),
    ...renderOptions
  } = {}
</span>) </span>{
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Wrapper</span>(<span class="hljs-params">{ children }</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Provider</span> <span class="hljs-attr">store</span>=<span class="hljs-string">{store}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Provider</span>&gt;</span></span>;
  }

  <span class="hljs-comment">// Return an object with the store and all of RTL's query functions</span>
  <span class="hljs-keyword">return</span> { store, ...render(ui, { <span class="hljs-attr">wrapper</span>: Wrapper, ...renderOptions }) };
}
</code></pre>
<p>This code information can be found in the <a target="_blank" href="https://redux.js.org/usage/writing-tests">Redux documentation</a>. You can almost copy and paste it all for your application.</p>
<p>But you have to adjust the slices that are used in there. Since in our application there is only the <code>ListSlice</code> we don't have much to add. Just import that and update the content of the <code>configureStore()</code> function, like we managed it in our <code>store.js</code> file.</p>
<p>This step is necessary to basically mock the entire Redux logic and put it together into one new <code>render()</code> function.</p>
<p>With that, we can import this new function into our test files (<code>App.test.js</code> and <code>myFirstTesting.test.js</code>) and then replace all <code>render()</code> functions with <code>renderWithProviders()</code>. The <code>App.test.js</code> file, for example, now looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"../App"</span>;
<span class="hljs-keyword">import</span> { renderWithProviders } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils/utils-for-tests"</span>;

test(<span class="hljs-string">"renders learn react link"</span>, <span class="hljs-function">() =&gt;</span> {
  renderWithProviders(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
  <span class="hljs-keyword">const</span> linkElement = screen.getByText(<span class="hljs-regexp">/learn react/i</span>);
  expect(linkElement).toBeInTheDocument();
});
</code></pre>
<p>There is not much more to do! If we now run our tests again (and comment out this one test, which is going to fail regardless because the button logic is not active anymore), it will work again.</p>
<h3 id="heading-slice-testing">Slice testing</h3>
<p>Another exciting part about testing with Redux is testing the slices. If you created your application with the React Redux Toolkit template, then you will be provided with some corresponding tests.</p>
<p>For our case, I also want to implement a new test file where we will specifically test <code>ListSlice.js</code> and its corresponding Redux logic.</p>
<p>For this slice, we have to import the slice and the corresponding reducers we want to test. To start, I will import the slice and test if it gets initialized with the initialState.</p>
<p>This is actually not the TDD approach since we already manually tested this part. Netherless, I would like to implement an automatic test as well:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> ListSlice, { initialState } <span class="hljs-keyword">from</span> <span class="hljs-string">"../features/ListSlice"</span>;

describe(<span class="hljs-string">"tests for ListSlice"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"initialize slice with initialValue"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> listSliceInit = ListSlice(initialState, { <span class="hljs-attr">type</span>: <span class="hljs-string">"unknown"</span> });
    expect(listSliceInit).toBe(initialState);
  });
});
</code></pre>
<p>Notice that I'm using <code>.spec</code> instead of <code>.test</code>. This doesn't matter. You can choose either. In this case, I'm going with <code>.spec</code> to remind you that this is also a viable option.</p>
<p>Also remember that we exported the initialState within our slice (see above). So we are able to import it here.</p>
<p>Other than that, we are already familiar with the <code>describe()</code> environment, which includes one <code>test()</code>. Within this test, I'm initializing a variable <code>listSliceInit</code>, which will hold the value we are receiving after the slice operation took place.</p>
<p>For this operation, we use <code>ListSlice</code> as a function and include the initial state as the first argument (in this case <code>initialState</code>). The second argument will be a reducer in most cases. </p>
<p>But in this case, we don't need to enter a reducer. Instead, we are using an object with <code>type: "unknown"</code>. This is basically telling the function that we don't want to perform any additional operations.</p>
<p>Therefore, <code>listSliceInit</code> should now include our state value, which includes an array with one entry. The corresponding test will pass.</p>
<p>To force a failure, I'm entering <code>expect(listSliceInit).toBe({ value: [] });</code> instead of the previous <code>expect()</code> function. So instead of our <code>initialState</code> we are expecting it to have an empty array. Now our test environment will tell us the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-24.png" alt="Image" width="600" height="400" loading="lazy">
<em>failing test</em></p>
<p>So it actually tells us what exactly it expected – in this case, it expected the <code>initialState</code>.</p>
<p>Next, I would like to test a reducer. However, we haven't added one yet. So I will adjust <code>ListSlice</code> in the <code>ListSlice.js</code> file like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ListSlice = createSlice({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"listReducers"</span>,
  initialState,
  <span class="hljs-attr">reducers</span>: {
    <span class="hljs-attr">testAddReducer</span>: <span class="hljs-function">(<span class="hljs-params">state, action</span>) =&gt;</span> {
      state.value.push(action.payload);
    },
  },
});
</code></pre>
<p>Thus, I added <code>testAddReducer()</code>, which is responsible for pushing one additional element to the current state value, which it receives via an input from the dispatch (through <code>action.payload</code>).</p>
<p>If we now jump back to the <code>listSlice.spec.js</code> file, I'm adding another unit test:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> ListSlice, { initialState, testAddReducer } <span class="hljs-keyword">from</span> <span class="hljs-string">"../features/ListSlice"</span>;

describe(<span class="hljs-string">"tests for ListSlice"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"initialize slice with initialValue"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> listSliceInit = ListSlice(initialState, { <span class="hljs-attr">type</span>: <span class="hljs-string">"unknown"</span> });
    expect(listSliceInit).toBe(initialState);
  });

  test(<span class="hljs-string">"testAddReducer"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> testData = {
      <span class="hljs-attr">id</span>: <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getSeconds()}</span>`</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"This is for the test section"</span>,
      <span class="hljs-attr">significance</span>: <span class="hljs-number">5</span>,
    };

    <span class="hljs-keyword">const</span> afterReducerOperation = ListSlice(
      initialState,
      testAddReducer(testData)
    );

    expect(afterReducerOperation).toStrictEqual({
      <span class="hljs-attr">value</span>: [initialState.value.at(<span class="hljs-number">0</span>), testData],
    });
  });
});
</code></pre>
<p>I added the test for <code>testAddReducer</code>. You can see that I imported the reducer as well.</p>
<p>Firstly, I'm initializing a new variable, <code>testData</code>, which contains the data I would like to push to the current state.</p>
<p>After that, we follow the same structure as before with <code>afterReducerOperation</code>. But instead of this <code>type: "unknown"</code> stuff, we add the reducer as the second argument. This receives the <code>testData</code> as a parameter – basically like you would see it in a dispatch.</p>
<p>Then, we expect this <code>afterReducerOperation</code> variable to be strictly equal to the value of an array, which has two entries: <code>initialState.value.at(0)</code> (the first entry of our <code>initialState</code>) and <code>testData</code>. And this test will pass like we actually expected it.</p>
<p>If we are trying to enter some other entries or change the current ones, you would be able to see this test failing:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-25.png" alt="Image" width="600" height="400" loading="lazy">
<em>forced error: I added a third entry to the array</em></p>
<h3 id="heading-how-to-make-the-button-click-function-work-again">How to make the button click function work again</h3>
<p>Remember the button click within the <code>List.js</code> component (for adding something to the <code>listData</code> state) that wasn't working anymore after we changed to the Redux setup? Let's quickly update that to make that logic work within a Redux environment for the sake of completeness. Since we have the required reducer now, this will be an easy step.</p>
<p>To make the test work again, which added a new element to the state, we have to adjust it a little on the frontend to implement the Redux logic. (Previously we used the useState hook for a local state.)</p>
<p>For this step, we are making use of the <code>dispatch()</code> function in order to reach out to the <code>testAddReducer</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useSelector, useDispatch } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-redux"</span>;
<span class="hljs-keyword">import</span> { testAddReducer } <span class="hljs-keyword">from</span> <span class="hljs-string">"../features/ListSlice"</span>;

<span class="hljs-keyword">const</span> List = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> listState = useSelector(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.listReducers.value);
  <span class="hljs-keyword">const</span> dispatch = useDispatch();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginLeft:</span> "<span class="hljs-attr">auto</span>", <span class="hljs-attr">marginTop:</span> "<span class="hljs-attr">500px</span>", <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">500px</span>" }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>This is a list<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">listStyleType:</span> "<span class="hljs-attr">none</span>" }}&gt;</span>
        {listState.map((listItem) =&gt; {
          return (
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{listItem.id}</span> <span class="hljs-attr">data-testid</span>=<span class="hljs-string">"list-item"</span>&gt;</span>
              {listItem.description}
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          );
        })}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
          dispatch(
            testAddReducer({
              id: `${new Date().getSeconds()}1`,
              description: "This is added",
              significance: 5,
            })
          )
        }
      &gt;
        This button can add a new entry to the list
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> List;
</code></pre>
<p>Besides the button logic, nothing else has changed in this file.</p>
<p>In the corresponding test (within <code>myFirstTesting.test.js</code> nothing has changed), if we now test everything – including this updated test – we will see that everything works fine:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-34.png" alt="Image" width="600" height="400" loading="lazy">
<em>final test run</em></p>
<p>And that's pretty much it for fundamental slice and general Redux unit testing!</p>
<h2 id="heading-outlook-for-advanced-testing">🔭 Outlook for Advanced Testing</h2>
<p>There are different topics like <a target="_blank" href="https://redux-toolkit.js.org/api/createAsyncThunk">thunks</a> (or <a target="_blank" href="https://redux-toolkit.js.org/rtk-query/overview">RTK Query</a> as an alternative) which could also be tested. But I'm considering this as an advanced topic, and it would take some more time to explain these processes.</p>
<p>If you are not aiming to be an expert in testing at this point, the topics we discussed for unit tests in Redux in this tutorial should be sufficient for you.</p>
<p>Generally speaking, I would recommend diving deeper into so-called mocks, spies, and also snapshots. These will be helpful if you are working on some other more advanced tests. </p>
<p>The stuff with <code>renderWithProvider()</code> is basically based on such a mock – there, we artificially created a store with reducers and a provider to create this new <code>render()</code> function. So mocks are especially helpful for any third-party libraries, for example. </p>
<p>As I said, though, mocks, spies, and snapshots are more of an advanced topic to wrap your head around.</p>
<h2 id="heading-further-learning-opportunities">📣 Further Learning Opportunities</h2>
<p>I recently started to work on my first <a target="_blank" href="https://www.udemy.com/user/matthes-bar/">free Udemy course</a>. While this first free course covers the basics of the React Redux Toolkit with German audio and manually added English subtitles, I'm also planning to publish other Udemy courses completely in English in the future. </p>
<p>I would really appreciate it if you would check out this cost-free course in order to provide me with some feedback.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Test Your Express.js and Mongoose Apps with Jest and SuperTest ]]>
                </title>
                <description>
                    <![CDATA[ By Rakesh Potnuru Testing is a vital part of software development. The sooner you start testing, the better.  In this article, I'll show you how to write tests for your NodeJs/ExpressJS and MongoDB/Mongoose applications with Jest and Supertest. Let's... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-test-in-express-and-mongoose-apps/</link>
                <guid isPermaLink="false">66d460c9787a2a3b05af43f6</guid>
                
                    <category>
                        <![CDATA[ Express JS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Jest ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mongoose ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 27 Sep 2022 23:26:16 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/how-to-write-tests.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Rakesh Potnuru</p>
<p>Testing is a vital part of software development. The sooner you start testing, the better. </p>
<p>In this article, I'll show you how to write tests for your NodeJs/ExpressJS and MongoDB/Mongoose applications with <strong>Jest</strong> and <strong>Supertest</strong>.</p>
<h2 id="heading-lets-get-started">Let's get started</h2>
<p>First let's set up a demo Express.js app.</p>
<p>Let's say we are building a backend REST API for an eCommerce application.</p>
<p>This app should:</p>
<ul>
<li>Get all the products</li>
<li>Get a product by id</li>
<li>Add product(s) to the database</li>
<li>Delete product(s) from the database</li>
<li>Update product information</li>
</ul>
<h2 id="heading-expressjs-app-set-up">Express.js App Set Up</h2>
<h3 id="heading-step-1-project-set-up">Step 1: Project set up</h3>
<p>First, create a folder and start a blank application with <code>npm</code>.</p>
<pre><code class="lang-bash">npm init
</code></pre>
<p>Fill all the details it asks for.</p>
<p>Then, install <code>express</code>, <code>mongoose</code>, <code>axios</code> and <code>dotenv</code> with the following command:</p>
<pre><code class="lang-bash">npm i express mongoose axios dotenv
</code></pre>
<p>Here's a link to the <a target="_blank" href="https://github.com/itsrakeshhq/jest-tests-demo/blob/a1725cb3379f78a03cf8d3d4cfa22127469e8b50/package.json">package.json</a> on my GitHub.</p>
<h3 id="heading-step-2-create-the-boilerplate">Step 2: Create the boilerplate</h3>
<p>Let's create all the folders and files and then fill them with some boilerplate code.</p>
<p>This is how your folder hierarchy should look:</p>
<pre><code class="lang-bash">.
├── controllers
│   └── product.controller.js
├── models
│   └── product.model.js
├── routes
│   └── product.route.js
├── package-lock.json
├── package.json
├── .env
├── app.js
└── server.js
</code></pre>
<p>Use these files' code by copying and pasting. Analyze the code and flow as best you can.</p>
<ul>
<li><code>[product.controller.js](https://github.com/itsrakeshhq/jest-tests-demo/blob/main/controllers/product.controller.js)</code></li>
<li><code>[product.model.js](https://github.com/itsrakeshhq/jest-tests-demo/blob/main/models/product.model.js)</code></li>
<li><code>[product.route.js](https://github.com/itsrakeshhq/jest-tests-demo/blob/main/routes/product.route.js)</code></li>
<li><code>[app.js](https://github.com/itsrakeshhq/jest-tests-demo/blob/main/app.js)</code> </li>
<li><code>[server.js](https://github.com/itsrakeshhq/jest-tests-demo/blob/main/server.js)</code></li>
</ul>
<h3 id="heading-step-3-database-setup">Step 3: Database setup</h3>
<p>I advise using two databases for a project—one for testing, the other for development. But just one database will be sufficient for learning purposes.</p>
<p>First, create a <a target="_blank" href="https://mongodb.com">MongoDB</a> account or log in.</p>
<p>Then create a new project. Give it a name and press the <strong>Next</strong> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Screenshot-2022-09-26-205148.png" alt="Naming the project" width="600" height="400" loading="lazy">
<em>Naming the project</em></p>
<p>Then click <strong>Create Project</strong> after that.</p>
<p>We must create a database in the following window by selecting a cloud provider, a location, and specs. So press <strong>Build a Database</strong> to get going.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Screenshot-2022-09-26-205911.png" alt="Build a database" width="600" height="400" loading="lazy">
<em>Build a database</em></p>
<p>Choose "Shared" because it is sufficient for learning purposes. And then click <strong>Create</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Screenshot-2022-09-26-211701.png" alt="Choose a deployment option" width="600" height="400" loading="lazy">
<em>Choose a deployment option</em></p>
<p>Next, select "aws" as your cloud provider and the region that is closest to you. Following your selection, click <strong>Create Cluster</strong>.</p>
<p>The cluster's formation will take some time. Create a user to access your database in the meanwhile.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Screenshot-2022-09-26-212537.png" alt="Create Superuser" width="600" height="400" loading="lazy">
<em>Create Superuser</em></p>
<p>Choose "My Local Environment" because we are developing our application. You can then add an IP addresses. To conclude, click <strong>Close</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/Screenshot-2022-09-26-213347.png" alt="Add IP addresses" width="600" height="400" loading="lazy">
<em>Add IP addresses</em></p>
<p>You will receive a URI string after the database is set up, which we'll use to connect to the database. The string appears as follows:</p>
<pre><code class="lang-bash">mongodb+srv://&lt;YOUR_USERNAME&gt;:&lt;YOUR_PASSWORD&gt;@&lt;YOUR_CLUSTER_URL&gt;/&lt;DATABASE_NAME&gt;?retryWrites=<span class="hljs-literal">true</span>&amp;w=majority
</code></pre>
<p>Put this string in the <code>.env</code> file.</p>
<pre><code class="lang-bash">MONGODB_URI=your database string
</code></pre>
<p>Now we're ready to start testing our app.</p>
<h2 id="heading-how-to-write-tests-with-jest-and-supertest">How to Write Tests with Jest and SuperTest</h2>
<h3 id="heading-step-1-install-packages">Step 1: Install packages</h3>
<p>You need three npm packages to begin writing tests: <code>jest</code>, <code>supertest</code>, and <code>cross-env</code>. You can install them like this:</p>
<pre><code class="lang-bash">npm i jest supertest cross-env
</code></pre>
<ul>
<li><code>jest</code>: Jest is a framework for testing JavaScript code. Unit testing is the main usage of it.</li>
<li><code>supertest</code>: Using Supertest, we can test endpoints and routes on HTTP servers.</li>
<li><code>cross-env</code>: You can set environmental variables inline within a command using cross-env.</li>
</ul>
<h3 id="heading-step-2-add-test-script">Step 2: Add test script</h3>
<p>Open your <code>package.json</code> file and add the test script to the scripts.</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"cross-env NODE_ENV=test jest --testTimeout=5000"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node server.js"</span>,
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"nodemon server.js"</span>
},
</code></pre>
<p>In this case, we're using <code>cross-env</code> to set environment variables, <code>jest</code> to execute test suites, and <code>testTimeout</code> is set to <code>5000</code> because certain requests might take a while to finish.</p>
<h3 id="heading-step-3-start-writing-tests">Step 3: Start writing tests</h3>
<p>First, create a folder called <code>tests</code> at the application's root, and then create a file there called <code>product.test.js</code>. Jest searches for the folder <code>tests</code> at the project's root when you do <code>npm run test</code>. As a result, you must place your test files in the <code>tests</code> folder.</p>
<p>Next, import the <code>supertest</code> and <code>mongoose</code> packages into the test file.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);
<span class="hljs-keyword">const</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">"supertest"</span>);
</code></pre>
<p>Import <code>dotenv</code> to load environment variables, and import <code>app.js</code> as that is where our application starts.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);
<span class="hljs-keyword">const</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">"supertest"</span>);
<span class="hljs-keyword">const</span> app = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../app"</span>);

<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();
</code></pre>
<p>You'll need to connect and disconnect the database before and after each test (because we don't require the database once testing is complete).</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/* Connecting to the database before each test. */</span>
beforeEach(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">await</span> mongoose.connect(process.env.MONGODB_URI);
});

<span class="hljs-comment">/* Closing database connection after each test. */</span>
afterEach(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">await</span> mongoose.connection.close();
});
</code></pre>
<p>Now you can write your first unit test.</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">"GET /api/products"</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">"should return all products"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app).get(<span class="hljs-string">"/api/products"</span>);
    expect(res.statusCode).toBe(<span class="hljs-number">200</span>);
    expect(res.body.length).toBeGreaterThan(<span class="hljs-number">0</span>);
  });
});
</code></pre>
<p>In the above code, </p>
<ul>
<li>We use <code>describe</code> to describe the unit test. Even though it is not required, it will be useful to identify tests in test results.</li>
<li>In <code>it</code>, we write the actual test code. Tell what the test performs in the first argument, and then in the second argument, write a callback function that contains the test code.</li>
<li>In the callback function, the request is sent to the endpoint first, and the expected and actual responses are then compared. The test passes if both answers match, else, it fails. ✨ As simple as that ✨.</li>
</ul>
<p>You can write tests for all the endpoints in the same manner.</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">"GET /api/products/:id"</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">"should return a product"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app).get(
      <span class="hljs-string">"/api/products/6331abc9e9ececcc2d449e44"</span>
    );
    expect(res.statusCode).toBe(<span class="hljs-number">200</span>);
    expect(res.body.name).toBe(<span class="hljs-string">"Product 1"</span>);
  });
});

describe(<span class="hljs-string">"POST /api/products"</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">"should create a product"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app).post(<span class="hljs-string">"/api/products"</span>).send({
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Product 2"</span>,
      <span class="hljs-attr">price</span>: <span class="hljs-number">1009</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Description 2"</span>,
    });
    expect(res.statusCode).toBe(<span class="hljs-number">201</span>);
    expect(res.body.name).toBe(<span class="hljs-string">"Product 2"</span>);
  });
});

describe(<span class="hljs-string">"PUT /api/products/:id"</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">"should update a product"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app)
      .patch(<span class="hljs-string">"/api/products/6331abc9e9ececcc2d449e44"</span>)
      .send({
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Product 4"</span>,
        <span class="hljs-attr">price</span>: <span class="hljs-number">104</span>,
        <span class="hljs-attr">description</span>: <span class="hljs-string">"Description 4"</span>,
      });
    expect(res.statusCode).toBe(<span class="hljs-number">200</span>);
    expect(res.body.price).toBe(<span class="hljs-number">104</span>);
  });
});

describe(<span class="hljs-string">"DELETE /api/products/:id"</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">"should delete a product"</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> request(app).delete(
      <span class="hljs-string">"/api/products/6331abc9e9ececcc2d449e44"</span>
    );
    expect(res.statusCode).toBe(<span class="hljs-number">200</span>);
  });
});
</code></pre>
<p>Then run <code>npm run test</code> to run the test suites (suite - test file).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-428.png" alt="Image" width="600" height="400" loading="lazy">
<em>Test results</em></p>
<p>And that's it! You now know how to test your Express/Mongoose apps with Jest and SuperTest.</p>
<p>Now go forth and create new tests for your apps. :)</p>
<p>If you have any questions, feel free to message me on <a target="_blank" href="https://twitter.com/rakesh_at_tweet">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Software Testing? A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ Software testing is essential to development. It saves you time and money in production mode.  But software testing is a complex topic and can be a bit difficult to understand. In this article, I'll explain the major topics in software testing and ho... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/software-testing-beginners-guide/</link>
                <guid isPermaLink="false">66c4c6501b22d2d8d9040ec8</guid>
                
                    <category>
                        <![CDATA[ beginner ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unit testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sophia Iroegbu ]]>
                </dc:creator>
                <pubDate>Wed, 21 Sep 2022 16:03:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/Tech-Blog-Cover--4-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Software testing is essential to development. It saves you time and money in production mode. </p>
<p>But software testing is a complex topic and can be a bit difficult to understand.</p>
<p>In this article, I'll explain the major topics in software testing and how this practice can help you. </p>
<h3 id="heading-table-of-contents">Table of Contents:</h3>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-software-testing">What is Software Testing?</a></li>
<li><a class="post-section-overview" href="#heading-types-of-software-testing">Types of Software Testing</a></li>
<li><a class="post-section-overview" href="#heading-different-types-of-functional-software-testing">Different Types of Functional Software Testing</a></li>
<li><a class="post-section-overview" href="#heading-software-testing-principles">Software Testing Principles</a></li>
<li><a class="post-section-overview" href="#heading-why-is-software-testing-needed">Why is Software Testing needed?</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-what-is-software-testing">What is Software Testing?</h2>
<p>Software testing is the process of making sure your software/app works as it should. There are various methods you can use to test your code, and each testing method has different requirements. </p>
<p>For instance, unit testing involves writing test cases to ensure the code works as it should, and Beta testing consists of testing the preview version of the software or app to make sure users can use the product.</p>
<p>Software testing is integral to the process of building good software that works as it should. It also helps improve productivity and performance. Testing is an important part of the <em>Software Development Life Cycle</em> (SDLC). </p>
<p>Other benefits of testing your code include preventing bugs, reducing cost, and reducing time of development.</p>
<h2 id="heading-types-of-software-testing">Types of Software Testing</h2>
<p>There are two general types of software testing:</p>
<h3 id="heading-functional-testing">Functional Testing:</h3>
<p>Functional Testing is a software testing method that validates the system against the customer's requirements or specifications. </p>
<p>This type of testing aims to test each function of the software by providing the correct input and ensuring the output is right. </p>
<p>For examples, let's say you write a test case to test creating a user. The test case provides the correct input (email, first name, last name and password) and ensures the output (success message) is accurate, as well.</p>
<p>Functional testing checks that everything is functioning properly by emulating business scenarios based on applicable requirements.</p>
<h3 id="heading-non-functional-testing">Non-functional Testing:</h3>
<p>Non-functional testing is a software testing method that tests for end-user experiences, such as performance and reliability under load. This could either make or break a user experience. </p>
<p>When your code fails at non-functional testing, it may not cause an issue that user would note but it can flag a problem in the system.</p>
<p>Non-functional testing is just about testing the software to know how it responds to load on the system.</p>
<p>In this guide, we will focus on Functional Software Testing.</p>
<h2 id="heading-different-types-of-functional-software-testing">Different Types of Functional Software Testing</h2>
<p>There are different types of software testing, and each has a specific aim. We'll look at each one quickly now. </p>
<h3 id="heading-unit-testing">Unit Testing:</h3>
<p>Unit testing is a type of software testing that validates how each software unit performs and whether that specific piece of code does what it should. A unit is the smallest testable component of an application.</p>
<p>The aim is to confirm that each unit of software code works as expected. You do unit testing during the coding (development) stage or phase. Developers write these tests as they go. </p>
<p>Unit tests isolate possible bugs in your code and help you correct them. A unit could be a single function, method, procedure, module, or object.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-404.png" alt="Image" width="600" height="400" loading="lazy">
<em>Code Snippet of a unit test case in Python</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/09/image-403.png" alt="Image" width="600" height="400" loading="lazy">
<em>Code Snippet of a unit test case in Java</em></p>
<h3 id="heading-integration-testing">Integration Testing:</h3>
<p>Integration Testing is software testing which helps ensure that software components or functions work together properly. This is the second phase of the software testing process that comes after unit testing.</p>
<p>In this type of testing, units or individual software components are tested in groups. This testing method mainly focuses on exposing defects in interactions between integrated components and units.</p>
<h3 id="heading-system-testing">System Testing:</h3>
<p>System testing involves the process of testing integrated software. The aim is to evaluate the system's compliance with specify requirements. </p>
<p>In system testing, the quality assurance team evaluates how each component of the application or software work together in a full, integrated environment.</p>
<h3 id="heading-acceptance-testing">Acceptance Testing:</h3>
<p>Acceptance testing is a software testing method where a system is tested or checked for acceptability. It evaluates the system's compatibility with the business requirements and assesses whether it is acceptable for delivery.</p>
<p>It is also known as formal testing performed to fit user needs, requirements, and business processes. It determines if a system satisfies the standard business criteria and if users or customers will be able to accept it. </p>
<p>Acceptance testing is the last stage of software testing done after system testing and before making the system available for public use.</p>
<h3 id="heading-regression-testing">Regression Testing:</h3>
<p>Regression testing ensures that a component continues working as it should, after including additional components in the program. You perform regression testing when something changes, such as adding a new module to the program.</p>
<p>This type of testing represents the complete testing of executed test cases that are re-executed to ensure the current functionalities still work just fine.</p>
<h3 id="heading-alpha-testing-and-beta-testing">Alpha Testing and Beta Testing:</h3>
<p>Alpha testing is also known as initial validation testing. It is an aspect of acceptance testing done before the product is given to the consumers or users. QA (Quality Assurance) testers usually do this. Alpha testing is done internally by the QA team.</p>
<p>Beta testing is also known as second phase of validation testing. But this type of testing is done externally, which means the public does it. </p>
<p>The version of the code/software for this phase of testing is released to a limited number of users for testing in a real-time scenario. For instance, freeCodeCamp's math curriculum is available for beta testing <a target="_blank" href="https://www.freecodecamp.org/news/freecodecamp-foundational-math-curriculum/">here</a>.</p>
<h2 id="heading-software-testing-principles">Software Testing Principles</h2>
<p>Everything in tech has principles. These are guidelines to help you build better software and avoid errors.</p>
<p>Here are some software testing principles you should follow when writing tests for your code:</p>
<h3 id="heading-testing-aims-to-show-the-presence-of-defects-not-the-absence">Testing aims to show the presence of defects, not the absence:</h3>
<p>Software testing aims to spot software failures. This reduces the presence of faults and errors. </p>
<p>Software testing ensures defects are visible to the developer but doesn't guarantee defect-free software. Multiple types of testing can't even ensure error-free software. Testing can only decrease the number of errors.</p>
<h3 id="heading-exhaustive-testing-is-not-possible">Exhaustive testing is not possible:</h3>
<p>Exhaustive Testing is the process of testing software for all valid and invalid inputs and pre-conditions. </p>
<p>This method of testing is not realistic because test cases presume that the software is correct and it produces the correct output in every test case. If you truly try to test every aspect and test case in your software, it will take too much time and effort, and it's not practical.</p>
<h3 id="heading-perform-early-testing">Perform early testing:</h3>
<p>Testing your software at an early phase helps avoid minor bugs or errors. When you can spot errors at an early stage of the Software Development Life Cycle(SDLC), it's always less expensive. It is best to start software testing from the beginning of the project.</p>
<h3 id="heading-defect-clustering">Defect clustering:</h3>
<p>Defect clustering refers to when most of the problems you find occur in just a few parts of the application or software. If you can identify the modules or areas where these defects occur, you can focus most of your testing efforts on them. </p>
<p>Keep the Pareto Principle in mind when testing your code: 80% of software defects tend to come from 20% of the modules.</p>
<h3 id="heading-beware-of-the-pesticide-paradox">Beware of the Pesticide paradox:</h3>
<p>This principle is based on a theory – "the more you use pesticide on a crop, the more immune the crop will eventually grow, and the pesticide will not be effective." </p>
<p>When you repeat particular test cases over and over, you will see fewer and fewer new bugs. So to find new bugs, update your test cases and run them once you add new test cases.</p>
<h3 id="heading-testing-is-context-dependent">Testing is context-dependent:</h3>
<p>Testing is context-dependent, which means that you should test your software based on its needs, functionalities, and requirements.</p>
<p>Your test approach should depend on what your software does. Not every software needs the same type/method of testing because every application has its unique functionalities.</p>
<p>For instance, when testing an eCommerce web app, you will focus on its functionality to display products, so you will test how it shows products to end-users. When dealing with an API, you will focus on the response the API returns when an endpoint is called. </p>
<p>You wouldn't necessarily use the same test cases for both – that is what it means that testing is context-dependent.</p>
<h3 id="heading-the-absence-of-errors-is-a-fallacy">The absence of errors is a fallacy:</h3>
<p>If you build software that is 99% bug-free, but it doesn't follow user requirements, it is not usable for end-users. </p>
<p>Know that it is very much necessary that your 99% bug-free software still meets or fulfills your user requirements. It is important to write test cases to find errors in the code, but you also need to test your software for your end-users (with them and how they'll use it in mind). The best way to do this is to carry out beta testing.</p>
<h2 id="heading-why-is-software-testing-needed">Why is Software Testing Needed?</h2>
<p>Besides making sure your software is bug-free and meets user requirements, software testing has other advantages. </p>
<h3 id="heading-software-testing-improves-security">Software testing improves security:</h3>
<p>When building software, security is a crucial part of your planning. This is because vulnerable software could jeopardize you users and their information, as hackers can use stolen info for malicious purposes.</p>
<p>As a product undergoes testing, the end-user can count on the fact that they will be getting a reliable product and their details will be secured and safe. So users are more likely to get a product that is free from vulnerabilities with the help of software testing.</p>
<h3 id="heading-software-testing-improves-product-quality">Software testing improves product quality:</h3>
<p>You want your software or product to be bug-free, low-risk, and effective at what it should do. And you can achieve this by including test cases and other testing methods when building out the code.</p>
<p>In addition, you won't know how good your product is until you test it. This helps you provide the best product version before it gets released (and discover any inconsistencies or pain points along the way – so you can improve them).</p>
<h3 id="heading-software-testing-improves-customer-satisfaction">Software testing improves customer satisfaction:</h3>
<p>For instance, let's say you download a new app and try to use some of its functionality – but it shows an error. This will probably frustrate you, and you might not want to use the app again, right?</p>
<p>This is exactly why software testing is important. It can help you discover such errors and detect them before you release the product to the user, and gives the developers a chance to prevent the error. </p>
<p>By investing in software testing early in the development stage, you are letting the users know that you care about their experience. It could also help you create a solid long-term customer relationship.</p>
<h3 id="heading-software-testing-saves-money">Software testing saves money:</h3>
<p>Software testing can save you a lot of money – but how?</p>
<p>Each stage of development involves many things, such as clear communication and coordination between multiple teams, and each step has a laundry list of things that could go awry.</p>
<p>Catching those errors when the product is live is a horrible experience because you may have to handle PR, retasking fixes, and trying to sort the problem in real time.</p>
<p>In addition, your users won't be able to access the app while you're fixing it, which defeats the app's purpose and provides a bad user experience in the meantime. Software testing helps resolve this stress, and once live, your user can enjoy your app/product to the fullest.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, software testing is a crucial part of development. It can help save your team a lot of trouble, and it feels great to create a usable, bug-free product that users enjoy and recommend.</p>
<p>If software testing interests you, you can check freeCodeCamp's QA certificate course <a target="_blank" href="https://www.freecodecamp.org/learn/quality-assurance/#quality-assurance-and-testing-with-chai">here</a> to learn more about QA testing. QA testers are techies that focus on testing softwares and apps for errors. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Perform Integration Testing using JUnit 5 and TestContainers with SpringBoot ]]>
                </title>
                <description>
                    <![CDATA[ TestContainers is a library that helps you run module-specific Docker containers to simplify Integration Testing. These Docker containers are lightweight, and once the tests are finished the containers get destroyed. In the article we are going to un... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/integration-testing-using-junit-5-testcontainers-with-springboot-example/</link>
                <guid isPermaLink="false">66d460f437bd2215d1e245c9</guid>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ spring-boot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sameer Shukla ]]>
                </dc:creator>
                <pubDate>Fri, 26 Aug 2022 15:46:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/testcontainers-logo.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>TestContainers is a library that helps you run module-specific Docker containers to simplify Integration Testing.</p>
<p>These Docker containers are lightweight, and once the tests are finished the containers get destroyed.</p>
<p>In the article we are going to understand what the TestContainers is and how it helps you write more reliable Tests.</p>
<p>We are also going to understand the important components (Annotations and Methods) of the library which help you write the Tests.</p>
<p>Finally, we will also learn to write a proper Integration Test in SpringBoot using the TestContainers library and its components.</p>
<h2 id="heading-limitations-of-testing-with-an-h2-in-memory-database">Limitations of Testing with an H2 In-Memory Database</h2>
<p>The most common approach to Integration Testing today is to use an H2 in-memory database. But there are certain limitations to this method.</p>
<p>First of all, say we are using version 8.0 of MySQL in production, but our integration tests are using H2. We can never execute our tests for the database version running on Production.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-303.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>SpringBoot app with MySQL DB and H2</em></p>
<p>‌Secondly, the test cases are less reliable because in production we are using an altogether different database and the tests are pointing to H2. The application may run into issues in production, but the integration tests may succeed.</p>
<p>I was trying to access my RESTful service on local and faced this error:</p>
<p>“<strong>Caused by: org.postgresql.util.PSQLException: FATAL: database "example_db" does not exist</strong>”.</p>
<p>It happened because of a permission issue, but the tests on local worked fine.</p>
<p>And finally, as documented <a target="_blank" href="http://h2database.com/html/features.html#compatibility">here</a>, H2 is compatible with other databases only up to a certain point. There are few areas where H2 is incompatible. If you need to use “nativeQueries” in a SpringBoot application, for example, then using H2 may cause problems.</p>
<h2 id="heading-enter-the-testcontainers-library">Enter the TestContainers Library</h2>
<p>By using TestContainers we can overcome the limitations of H2.</p>
<ul>
<li><p>Integration tests will point to the same version of the database as it’s in production. So we can tie our TestContainer Database Image to the same version running on production.</p>
</li>
<li><p>Integration tests are lot more reliable because both application and tests are using the same database type and version and there won't be any compatibility issues in Testcases.</p>
</li>
</ul>
<h2 id="heading-what-is-testcontainers">What is TestContainers?</h2>
<p>The TestContainers library is a wrapper API over Docker. When we write code to create a container behind the scenes it may be translated to some Docker command, for example‌:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-283.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>MySQLContainer Creation</em></p>
<p>This code may be translated to something like the following:</p>
<pre><code class="lang-docker">docker <span class="hljs-keyword">run</span><span class="bash"> -d --env MYSQL_DATABASE=example_db --env MYSQL_USER=<span class="hljs-built_in">test</span> --env MYSQL_PASSWORD=<span class="hljs-built_in">test</span> ‘mysql:latest’</span>
</code></pre>
<p>TestContainers has a method name “withCommand”. You use it to set the command that should be run inside the Docker container which confirms that TestContainers is a wrapper API over Docker.</p>
<p>TestContainers downloads the MySQL, Postgres, Kafka, Redis images and runs in a container. The MySQLContainer will run a MySQL Database in a container and the Testcases can connect to it on the local machine. Once the execution is over the Database will be gone – it just deletes it from the machine. In the Testcases we can start as many container images as we want.</p>
<p>TestContainers supports JUnit 4, JUnit 5 and Spock. If you go to the TestContainers.org website, just visit the QuickStart section that explains how to use it:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-284.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>TestContainers.org how to start with Test Framework</em></p>
<p>TestContainers supports almost every Database from MySQL and Postgres to CockroachDB. You can find more info about this on the TestContainers.org website under the Modules section:‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-285.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>TestContainers support for Database Modules</em></p>
<p>‌TestContainers also supports Cloud Modules like GCloud Module and Azure Module as well. If your application is running on Google Cloud, then TestContainers has support for Cloud Spanner, Firestore, Datastore and so on.‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-286.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>TestContainers support for GCloud Module</em></p>
<p>In the article so far, we have discussed only about Databases, but TestContainers supports various other components like Kafka, SOLR, Redis, and more.</p>
<h2 id="heading-how-to-use-the-testcontainers-library">How to Use the TestContainers Library</h2>
<p>In this article we are going to explore TestContainers with JUnit 5. To implement TestContainers we need to understand a few important TestContainers annotations, methods, and the libraries that we need to implement in our project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-288.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>TestContainers libraries</em></p>
<h3 id="heading-annotations-in-testcontainers">Annotations in TestContainers</h3>
<p>Two important annotations are required in our Tests for TestContainers to work: @TestContainers and @Container.</p>
<p>@TestContainer is JUnit-Jupiter extension which automatically starts and stops the containers that are used in the tests. This annotation finds the fields that are marked with @Container and calls the specific Container life-cycle methods. Here, MySQLContainer life-cycle methods will be invoked.‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-289.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>MySQLContainer</em></p>
<p>The MySQLContainer is declared as static because if we declare Container as static then a single container is started and it will be shared across all the test methods.</p>
<p>If it’s an instance variable, then a new container is created for each test method.</p>
<h2 id="heading-testcontainers-library-methods">TestContainers Library Methods</h2>
<p>There are few important methods in TestContainers library that you'll use in the tests. They're good to know before using the library.</p>
<ul>
<li><p><strong>withInitScript</strong>: Using ‘withInitScript’ we can execute the .SQL to define the schema, tables, and plus add the data into the database. In short, this method is used to run the .SQL to populate the database.</p>
</li>
<li><p><strong>withReuse</strong> (true): Using “withReuse” method we can enable the reuse of containers. This method works well in conjunction with enabling the “testcontainers.reuse.enable:true” property in the “.testcontainers.properties” file.</p>
</li>
<li><p><strong>start</strong>: we use this to start the container.</p>
</li>
<li><p><strong>withClasspathResourceMapping</strong>: This maps a resource (file or directory) on the classpath to a path inside the container. This will only work if you are running your tests outside a Docker container.</p>
</li>
<li><p><strong>withCommand</strong>: Set the command that should be run inside the Docker container.</p>
</li>
<li><p><strong>withExposedPorts</strong>: Used to set the port that the container listens on.</p>
</li>
<li><p><strong>withFileSystemBind</strong>: Used to map a file / directory from the local filesystem into the container.</p>
</li>
</ul>
<h2 id="heading-testcontainers-use-case">TestContainers Use Case</h2>
<p>In the example we'll look at now, the application will communicate only with the database and write the integration tests for it using TestContainers. Then we'll extend the use-case by implementing Redis in between.</p>
<p>If the data exists in the Redis cache it will be returned, otherwise it'll dip into the database for saving and retrieval based on the Key.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-308.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Use-Case</em></p>
<p>The service is simple. It has 2 endpoints – the first one is to create a user and the second one is to find a user by email. If the user is found it is returned, otherwise we get a 404. The service class code looks something like this:‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-291.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Service Component</em></p>
<p>We are going to write tests for this class. You can find the entire codebase <a target="_blank" href="https://github.com/sameershukla/testcontainers_demo">here</a>:‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-292.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Test Class</em></p>
<p>The test class is marked with @TestContainers annotation which starts/stops the container. We use the @Container annotation to call the specific container's life-cycle methods.</p>
<p>Also, the “MySQLContainer” is declared as static because then a single container is started. Then it gets shared across all the test methods (we have already discussed the importance of these annotations).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-293.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>BeforeAll</em></p>
<p>Next we need to write a setup method marked with @BeforeAll, where we have enabled the “withReuse” method. This helps us reuse the existing containers. We are using the “withInitScript” method to execute the “.sql” file and then starting the container.‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-294.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Overwriting Properties</em></p>
<p>‌@DynamicPropertySource helps us override the properties declared in the properties file. We write this method to allow TestContainers to create the URL, username, and password on its own – otherwise we may face errors.</p>
<p>For example on removing username and password we may face an ‘Access denied’ error which may confuse us. So it’s better to allow TestContainer to assign these properties dynamically on its own.</p>
<p>That’s it – we are ready to run the Testcases:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-295.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Test Cases</em></p>
<p>Execute @AfterAll to stop the container, otherwise it may keep running on your local machine if you don't explicitly stop it. ‌</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-296.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-use-genericcontainer">How to Use GenericContainer</h2>
<p>‌‌GenericContainer is the most flexible container. It makes it easy to run any container images within GenericContainer.</p>
<p>Now we have Redis in place, all we need to do in our Testcase is to spin up a GenericContainer with the Redis image.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-297.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>GenericContainer for Redis</em></p>
<p>Then we start the Generic Redis container in @BeforeAll and stop it with the @AfterAll tear down method.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-298.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Starting Containers</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/image-299.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Stopping Containers</em></p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>It's extremely easy to use TestContainers in our application to write better tests. The learning curve is not too steep and it has support for various different modules from a variety of databases like Kafka, Redis and others.</p>
<p>Writing tests using TestContainers makes our tests lot more reliable. The only flip side is that the tests are slow compared to H2. This is because H2 is in memory and TestContainers takes time to download the image, run the container, and execute the entire setup we have discussed in this article.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Test-Driven Development Tutorial – How to Test Your JavaScript and ReactJS Applications ]]>
                </title>
                <description>
                    <![CDATA[ Understanding test-driven development is an essential part of being a prolific software developer. Testing provides a solid platform for building reliable programs. This tutorial will show you all you need to implement test-driven development in your... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app/</link>
                <guid isPermaLink="false">66ba0e0ed14c87384322b685</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ test driven development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwatobi Sofela ]]>
                </dc:creator>
                <pubDate>Tue, 26 Jul 2022 17:51:22 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app-codesweetly-battlecreek-coffee-roasters-i22gbC3gFm4-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Understanding test-driven development is an essential part of being a prolific software developer. Testing provides a solid platform for building reliable programs.</p>
<p>This tutorial will show you all you need to implement test-driven development in your JavaScript and React applications.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-is-test-driven-development">What Is Test-Driven Development?</a></li>
<li><a class="post-section-overview" href="#heading-javascript-example-of-a-test-driven-development-workflow">JavaScript Example of a Test-Driven Development Workflow</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-jest-as-a-test-implementation-tool">How to Use Jest as a Test Implementation Tool</a></li>
<li><a class="post-section-overview" href="#heading-important-stuff-to-know-about-using-es6-modules-with-jest">Important Stuff to Know about Using ES6 Modules with Jest</a></li>
<li><a class="post-section-overview" href="#heading-what-are-the-advantages-of-test-driven-development">What Are the Advantages of Test-Driven Development?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-a-unit-test-in-test-driven-development">What is a Unit Test in Test-Driven Development</a>?</li>
<li><a class="post-section-overview" href="#heading-what-is-an-integration-test-in-test-driven-development">What is an Integration Test in Test-Driven Development</a>?</li>
<li><a class="post-section-overview" href="#heading-what-is-an-end-to-end-test-in-test-driven-development">What is an End-to-End Test in Test-Driven Development</a>?</li>
<li><a class="post-section-overview" href="#heading-what-are-test-doubles-in-test-driven-development">What are Test Doubles in Test-Driven Development</a>?</li>
<li><a class="post-section-overview" href="#heading-quick-overview-of-test-driven-development-so-far">Quick Overview of Test-Driven Development So Far</a></li>
<li><a class="post-section-overview" href="#heading-how-to-test-react-components">How to Test React Components</a></li>
<li><a class="post-section-overview" href="#heading-test-runner-vs-react-component-testing-tool-whats-the-difference">Test Runner vs. React Component Testing Tool: What's the Difference?</a></li>
<li><a class="post-section-overview" href="#heading-project-how-react-testing-works">Project: How React Testing Works</a></li>
<li><a class="post-section-overview" href="#heading-overview">Overview</a></li>
</ol>
<p>So, without any further ado, let's get started by discussing what test-driven development means.</p>
<h2 id="heading-what-is-test-driven-development">What Is Test-Driven Development?</h2>
<p><strong>Test-driven development (TDD)</strong> is a coding practice where you write the result you want your program to produce before creating the program.</p>
<p>In other words, TDD requires you to pre-specify the output your intended program must produce to pass the test of functioning the way you envisioned.</p>
<p>So, in an effective test-driven development practice, you would first write tests that express the result you expect from your intended program.</p>
<p>Afterward, you would develop the program to pass the prewritten test.</p>
<p>For instance, suppose you wish to create an addition calculator. In such a case, the TDD approach will be like so:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/test-driven-development-tdd-workflow-diagram-codesweetly.png" alt="Test-driven development workflow diagram" width="600" height="400" loading="lazy">
<em>Test-driven development workflow diagram</em></p>
<ol>
<li>Write a test specifying the result you expect the calculator to produce to pass the test of being the program you had in mind.</li>
<li>Develop the calculator to pass the prewritten test.</li>
<li>Run the test to check whether the calculator passes or fails the test.</li>
<li>Refactor your test code (if necessary).</li>
<li>Refactor your program (if necessary).</li>
<li>Continue the cycle until the calculator matches your vision.</li>
</ol>
<p>Let's now see a JavaScript example of a TDD workflow.</p>
<h2 id="heading-javascript-example-of-a-test-driven-development-workflow">JavaScript Example of a Test-Driven Development Workflow</h2>
<p>The steps below will use a simple JavaScript program to show you how to approach TDD.</p>
<h3 id="heading-1-write-your-test">1. Write your test</h3>
<p>Write a test that specifies the result you expect your calculator program to produce:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">additionCalculatorTester</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (additionCalculator(<span class="hljs-number">4</span>, <span class="hljs-number">6</span>) === <span class="hljs-number">10</span>) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"✔ Test Passed"</span>);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"❌ Test Failed"</span>);
  }
}
</code></pre>
<h3 id="heading-2-develop-your-program">2. Develop your program</h3>
<p>Develop the calculator program to pass the prewritten test:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">additionCalculator</span>(<span class="hljs-params">a, b</span>) </span>{
  <span class="hljs-keyword">return</span> a + b;
}
</code></pre>
<h3 id="heading-3-run-the-test">3. Run the test</h3>
<p>Run the test to check whether the calculator passes or fails the test:</p>
<pre><code class="lang-js">additionCalculatorTester();
</code></pre>
<p><a target="_blank" href="https://stackblitz.com/edit/js-ciui1u?devToolsHeight=33&amp;file=index.js"><strong>Try it on StackBlitz</strong></a></p>
<h3 id="heading-4-refactor-the-test">4. Refactor the test</h3>
<p>After you've confirmed that your program passed the prewritten test, it's time to check if there's any need to refactor it.</p>
<p>For instance, you could refactor <code>additionCalculatorTester()</code> to use a <a target="_blank" href="https://codesweetly.com/javascript-statement/#what-is-a-conditional-ternary-operator-in-javascript">conditional operator</a> like so:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">additionCalculatorTester</span>(<span class="hljs-params"></span>) </span>{
  additionCalculator(<span class="hljs-number">4</span>, <span class="hljs-number">6</span>) === <span class="hljs-number">10</span> 
    ? <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"✔ Test Passed"</span>) 
    : <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"❌ Test Failed"</span>);
}
</code></pre>
<h3 id="heading-5-refactor-the-program">5. Refactor the program</h3>
<p>Let's also refactor the program's code to use an <a target="_blank" href="https://codesweetly.com/javascript-function-object#arrow-function-expression-in-javascript">arrow function</a>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> additionCalculator = <span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a + b;
</code></pre>
<h3 id="heading-6-run-the-test">6. Run the test</h3>
<p>Rerun the test to ensure your program still works as intended.</p>
<pre><code class="lang-js">additionCalculatorTester();
</code></pre>
<p><a target="_blank" href="https://stackblitz.com/edit/js-xp732h?devToolsHeight=33&amp;file=index.js"><strong>Try it on StackBlitz</strong></a></p>
<p>Notice that in the examples above, we implemented TDD without using any libraries.</p>
<p>But you can also use powerful test-running tools like <a target="_blank" href="https://jasmine.github.io/">Jasmine</a>, <a target="_blank" href="https://mochajs.org/">Mocha</a>, <a target="_blank" href="https://github.com/substack/tape">Tape</a>, and <a target="_blank" href="https://jestjs.io/">Jest</a>, to make your test implementation faster, simpler, and more fun.</p>
<p>Let's see how to use Jest, for example.</p>
<h2 id="heading-how-to-use-jest-as-a-test-implementation-tool">How to Use Jest as a Test Implementation Tool</h2>
<p>Here are the steps you'll need to follow to get started using Jest as your test implementation tool:</p>
<h3 id="heading-step-1-get-the-right-node-and-npm-version">Step 1: Get the right Node and NPM version</h3>
<p>Make sure you have Node 10.16 (or greater) and NPM 5.6 (or greater) installed on your system.</p>
<p>You can get both by installing the latest LTS from the <a target="_blank" href="https://nodejs.org/en/">Node.js</a> website.</p>
<p>If you prefer to use Yarn, ensure you have <a target="_blank" href="https://yarnpkg.com/">Yarn 0.25 (or greater)</a>.</p>
<h3 id="heading-step-2-create-a-project-directory">Step 2: Create a project directory</h3>
<p>Create a new folder for your project.</p>
<pre><code class="lang-bash">mkdir addition-calculator-jest-project
</code></pre>
<h3 id="heading-step-3-navigate-to-your-project-folder">Step 3: Navigate to your project folder</h3>
<p>Using the command line, navigate to your project directory.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> path/to/addition-calculator-jest-project
</code></pre>
<h3 id="heading-step-4-create-a-packagejson-file">Step 4: Create a <code>package.json</code> file</h3>
<p>Initialize a <code>package.json</code> file for your project.</p>
<pre><code class="lang-bash">npm init -y
</code></pre>
<p>Or, if your <a target="_blank" href="https://codesweetly.com/package-manager-explained">package manager</a> is Yarn, run:</p>
<pre><code class="lang-bash">yarn init -y
</code></pre>
<h3 id="heading-step-5-install-jest">Step 5: Install Jest</h3>
<p>Install Jest as a development dependency package like so:</p>
<pre><code class="lang-bash">npm install jest --save-dev
</code></pre>
<p>Alternatively, if your package manager is Yarn, run:</p>
<pre><code class="lang-bash">yarn add jest --dev
</code></pre>
<h3 id="heading-step-6-make-jest-your-projects-test-runner-tool">Step 6: Make Jest your project's test runner tool</h3>
<p>Open your <code>package.json</code> file and add Jest to the <code>test</code> field.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"jest"</span>
  }
}
</code></pre>
<h3 id="heading-step-7-create-your-project-file">Step 7: Create your project file</h3>
<p>Create a file that you will use to develop your program.</p>
<pre><code class="lang-bash">touch additionCalculator.js
</code></pre>
<h3 id="heading-step-8-create-your-test-file">Step 8: Create your test file</h3>
<p>Create a file that you will use to write your test cases.</p>
<pre><code class="lang-bash">touch additionCalculator.test.js
</code></pre>
<p><strong>Note:</strong> Your test file's name must end with <code>.test.js</code>—so that Jest can recognize it as the file containing your test code.</p>
<h3 id="heading-step-9-write-your-test-case">Step 9: Write your test case</h3>
<p>Open your test file and write some test code that specifies the result you expect your program to produce.</p>
<p><strong>Here's an example:</strong></p>
<pre><code class="lang-js"><span class="hljs-comment">// additionCalculator.test.js</span>

<span class="hljs-keyword">const</span> additionCalculator = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./additionCalculator"</span>);

test(<span class="hljs-string">"addition of 4 and 6 to equal 10"</span>, <span class="hljs-function">() =&gt;</span> {
  expect(additionCalculator(<span class="hljs-number">4</span>, <span class="hljs-number">6</span>)).toBe(<span class="hljs-number">10</span>);
});
</code></pre>
<p>Here's what we did in the snippet above:</p>
<ol>
<li>We imported the <code>additionCalculator.js</code> project file into the <code>additionCalculator.test.js</code> test file.</li>
<li>We wrote a test case specifying that we expect the <code>additionCalculator()</code> program to output <code>10</code> whenever users provide <code>4</code> and <code>6</code> as its <a target="_blank" href="https://codesweetly.com/javascript-arguments">argument</a>.</li>
</ol>
<p><strong>Note:</strong></p>
<ul>
<li><a target="_blank" href="https://jestjs.io/docs/api#testname-fn-timeout"><code>test()</code></a> is one of Jest's global methods. It accepts three arguments:<ol>
<li>The name of the test (<code>"addition of 4 and 6 to equal 10"</code>).</li>
<li>A function containing the expectations you wish to test.</li>
<li>An optional timeout argument.</li>
</ol>
</li>
<li><a target="_blank" href="https://jestjs.io/docs/expect#expectvalue"><code>expect()</code></a> is a Jest method that lets you test the output of your code.</li>
<li><a target="_blank" href="https://jestjs.io/docs/expect#tobevalue"><code>toBe()</code></a> is a <a target="_blank" href="https://jestjs.io/docs/using-matchers">Jest matcher</a> function that enables you to compare <code>expect()</code>'s argument to primitive values.</li>
</ul>
<p>Suppose you run the test code now. The test would fail because you've not developed the program for which you created the test. So, let's do that now.</p>
<h3 id="heading-step-10-develop-your-program">Step 10: Develop your program</h3>
<p>Open your project file and develop a program to pass the prewritten test.</p>
<p><strong>Here's an example:</strong></p>
<pre><code class="lang-js"><span class="hljs-comment">// additionCalculator.js</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">additionCalculator</span>(<span class="hljs-params">a, b</span>) </span>{
  <span class="hljs-keyword">return</span> a + b;
}

<span class="hljs-built_in">module</span>.exports = additionCalculator;
</code></pre>
<p>The snippet above created an <code>additionCalculator()</code> program and exported it with the <code>module.exports</code> statement.</p>
<h3 id="heading-step-11-run-the-test">Step 11: Run the test</h3>
<p>Run the prewritten test to check if your program passed or failed.</p>
<pre><code class="lang-bash">npm run <span class="hljs-built_in">test</span>
</code></pre>
<p>Alternatively, you can use Yarn like so:</p>
<pre><code class="lang-bash">yarn <span class="hljs-built_in">test</span>
</code></pre>
<p>Suppose your project contains multiple test files, and you wish to run a specific one. In such a case, specify the test file as follow:</p>
<pre><code class="lang-bash">npm run <span class="hljs-built_in">test</span> additionCalculator.test.js
</code></pre>
<p>Alternatively, you can use Yarn like this:</p>
<pre><code class="lang-bash">yarn <span class="hljs-built_in">test</span> additionCalculator.test.js
</code></pre>
<p>Once you've initiated the test, Jest will print a pass or fail message on your editor's console. The message will look similar to this:</p>
<pre><code class="lang-bash">$ jest
 PASS  ./additionCalculator.test.js
  √ addition of 4 and 6 to equal 10 (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.002 s
Ran all <span class="hljs-built_in">test</span> suites.
Done <span class="hljs-keyword">in</span> 7.80s.
</code></pre>
<p>If you prefer Jest to run your test automatically, add the <code>--watchAll</code> option to your <code>package.json</code>'s <code>test</code> field.</p>
<p><strong>Here's an example:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"jest --watchAll"</span>
  }
}
</code></pre>
<p>After adding <code>--watchAll</code>, re-execute the <code>npm run test</code> (or <code>yarn test</code>) command to make Jest automatically begin rerunning your test whenever you save changes.</p>
<p><strong>Note:</strong> You can quit the watch mode by pressing the <strong>Q</strong> key on your keyboard.</p>
<h3 id="heading-step-12-refactor-the-test-code">Step 12: Refactor the test code</h3>
<p>So, now that you've confirmed that your program is working as intended, it's time to check if there's any need to refactor the test code.</p>
<p>For instance, suppose you realized that the <code>additionalCalculator</code> should allow users to add any number of digits. In that case, you can refactor your test code like so:</p>
<pre><code class="lang-js"><span class="hljs-comment">// additionCalculator.test.js</span>

<span class="hljs-keyword">const</span> additionCalculator = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./additionCalculator"</span>);

describe(<span class="hljs-string">"additionCalculator's test cases"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"addition of 4 and 6 to equal 10"</span>, <span class="hljs-function">() =&gt;</span> {
    expect(additionCalculator(<span class="hljs-number">4</span>, <span class="hljs-number">6</span>)).toBe(<span class="hljs-number">10</span>);
  });

  test(<span class="hljs-string">"addition of 100, 50, 20, 45 and 30 to equal 245"</span>, <span class="hljs-function">() =&gt;</span> {
    expect(additionCalculator(<span class="hljs-number">100</span>, <span class="hljs-number">50</span>, <span class="hljs-number">20</span>, <span class="hljs-number">45</span>, <span class="hljs-number">30</span>)).toBe(<span class="hljs-number">245</span>);
  });

  test(<span class="hljs-string">"addition of 7 to equal 7"</span>, <span class="hljs-function">() =&gt;</span> {
    expect(additionCalculator(<span class="hljs-number">7</span>)).toBe(<span class="hljs-number">7</span>);
  });

  test(<span class="hljs-string">"addition of no argument provided to equal 0"</span>, <span class="hljs-function">() =&gt;</span> {
    expect(additionCalculator()).toBe(<span class="hljs-number">0</span>);
  });
});
</code></pre>
<p>Note that the <a target="_blank" href="https://jestjs.io/docs/api#describename-fn">describe()</a> method we used in the snippet above is optional code—it helps organize related test cases into groups.</p>
<p><code>describe()</code> accepts two arguments:</p>
<ol>
<li>A name you wish to call the test case group—for instance, <code>"additionCalculator's test cases"</code>.</li>
<li>A function containing your test cases.</li>
</ol>
<h3 id="heading-step-13-refactor-the-program">Step 13: Refactor the program</h3>
<p>So, now that you've refactored your test code, let's do the same for the <code>additionalCalculator</code> program.</p>
<pre><code class="lang-js"><span class="hljs-comment">// additionCalculator.js</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">additionCalculator</span>(<span class="hljs-params">...numbers</span>) </span>{
  <span class="hljs-keyword">return</span> numbers.reduce(<span class="hljs-function">(<span class="hljs-params">sum, item</span>) =&gt;</span> sum + item, <span class="hljs-number">0</span>);
}

<span class="hljs-built_in">module</span>.exports = additionCalculator;
</code></pre>
<p>Here's what we did in the snippet above:</p>
<ol>
<li>The <code>...numbers</code> code used JavaScript's <a target="_blank" href="https://codesweetly.com/javascript-rest-operator">rest operator</a> (<code>...</code>) to put the function's arguments into an array.</li>
<li>The <code>numbers.reduce((sum, item) =&gt; sum + item, 0)</code> code used JavaScript's <a target="_blank" href="https://codesweetly.com/javascript-reduce-method">reduce()</a> method to sum up all the items in the <code>numbers</code> array.</li>
</ol>
<h3 id="heading-step-14-rerun-the-test">Step 14: Rerun the test</h3>
<p>Once you've finished refactoring your code, rerun the test to confirm that your program still works as expected.</p>
<h3 id="heading-and-thats-it">And that's it!</h3>
<p>Congratulations! You've successfully used Jest to develop an addition calculator program using a test-driven development approach! 🎉</p>
<h2 id="heading-important-stuff-to-know-about-using-es6-modules-with-jest">Important Stuff to Know about Using ES6 Modules with Jest</h2>
<p>Jest does not currently recognize ES6 modules.</p>
<p>However, suppose you prefer to use ES6's import/export statements. In that case, do the following:</p>
<h3 id="heading-1-install-babel-as-a-development-dependency">1. Install Babel as a development dependency</h3>
<pre><code class="lang-bash">npm install @babel/preset-env --save-dev
</code></pre>
<p>Or, you can use Yarn:</p>
<pre><code class="lang-bash">yarn add @babel/preset-env --dev
</code></pre>
<h3 id="heading-2-create-a-babelrc-file-in-your-projects-root">2. Create a <code>.babelrc</code> file in your project's root</h3>
<pre><code class="lang-bash">touch .babelrc
</code></pre>
<h3 id="heading-3-open-the-babelrc-file-and-replicate-the-code-below">3. Open the <code>.babelrc</code> file and replicate the code below</h3>
<pre><code class="lang-json">{ <span class="hljs-attr">"presets"</span>: [<span class="hljs-string">"@babel/preset-env"</span>] }
</code></pre>
<p>The configuration above will now allow you to change step 9's <code>require()</code> statement from this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> additionCalculator = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./additionCalculator"</span>);
</code></pre>
<p>...to this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> additionCalculator <span class="hljs-keyword">from</span> <span class="hljs-string">"./additionCalculator"</span>;
</code></pre>
<p>Likewise, you can now also substitute step 10's <code>export</code> statement from this:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = additionCalculator;
</code></pre>
<p>...to this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> additionCalculator;
</code></pre>
<p><strong>Note:</strong> Jest also specified similar instructions in their <a target="_blank" href="https://jestjs.io/docs/getting-started#using-babel">using Babel</a> documentation.</p>
<h3 id="heading-4-rerun-the-test">4. Rerun the test</h3>
<p>You can now rerun the test to confirm that your program still works!</p>
<p>So, now that we know what test-driven development is, we can discuss its advantages.</p>
<h2 id="heading-what-are-the-advantages-of-test-driven-development">What Are the Advantages of Test-Driven Development?</h2>
<p>Below are two main advantages of adopting test-driven development (TDD) in your programming workflow.</p>
<h3 id="heading-1-understand-your-programs-purpose">1. Understand your program's purpose</h3>
<p>Test-driven development helps you understand the purposes of your program.</p>
<p>In other words, since you write your test before the actual program, TDD makes you think about what you want your program to do.</p>
<p>Then, after you've documented the program's purposes using one or more tests, you can confidently proceed to create the program.</p>
<p>Therefore, TDD is a helpful way to jot down the specific results you expect your intended program to produce.</p>
<h3 id="heading-2-confidence-booster">2. Confidence booster</h3>
<p>TDD is a benchmark for knowing that your program is working as expected. It gives you the confidence that your program is working correctly.</p>
<p>Therefore, irrespective of any future development on your codebase, TDD provides an effective way to test if your program is still working appropriately.</p>
<p>Let's now discuss some popular TDD terms: "unit test," "integration test," "E2E," and "test doubles."</p>
<h2 id="heading-what-is-a-unit-test-in-test-driven-development">What is a Unit Test in Test-Driven Development?</h2>
<p>A <strong>unit test</strong> is a test you write to assess the functionality of an independent piece of a program. In other words, a unit test checks if a fully isolated unit of program is working as intended.</p>
<p>The test we wrote for step 10's <code>additionalCalculator</code> program is an excellent unit test example.</p>
<p>Step 10's <code>additionalCalculator()</code>'s test is a unit test because the program is an independent function that does not depend on any external code.</p>
<p>Note that a unit test's primary purpose is not to check for bugs. Instead, a unit test's core purpose is to check whether an independent piece of program (called unit) behaves as intended under various test cases.</p>
<h2 id="heading-what-is-an-integration-test-in-test-driven-development">What is an Integration Test in Test-Driven Development?</h2>
<p>An <strong>integration test</strong> assesses the functionality of a dependent piece of program. In other words, an integration test checks if a program—which depends on other code—is working as intended.</p>
<p>The test we wrote for step 13's <code>additionalCalculator</code> program is an excellent example of an integration test.</p>
<p>Step 13's <code>additionalCalculator()</code>'s test is an integration test because the program is a dependent function that depends on JavaScript's <a target="_blank" href="https://codesweetly.com/javascript-reduce-method">reduce()</a> method.</p>
<p>In other words, we used the prewritten test case to assess the integration of <code>additionalCalculator()</code> and <code>reduce()</code>.</p>
<p>Therefore, suppose JavaScript makes <code>reduce()</code> an obsolete method. In such a case, <code>additionalCalculator</code> will fail its test because of the <code>reduce()</code> method.</p>
<h2 id="heading-what-is-an-end-to-end-test-in-test-driven-development">What is an End-to-End Test in Test-Driven Development?</h2>
<p>An <strong>End-to-End (E2E) test</strong> assesses the functionality of a user interface. In other words, E2E checks if your user interface is working as intended.</p>
<p>Watch <a target="_blank" href="https://youtu.be/r9HdJ8P6GQI?t=1755">Max's YouTube video</a> for a good illustration of an End-to-End test.</p>
<h2 id="heading-what-are-test-doubles-in-test-driven-development">What are Test Doubles in Test-Driven Development?</h2>
<p><strong>Test doubles</strong> are the imitation objects used to mimic real dependencies like databases, libraries, networks, and APIs.</p>
<p>A test double allows you to bypass the natural objects on which your program depends. They let you test your code independently of any dependencies.</p>
<p>For instance, suppose you need to verify if an error detected in your app originates from an external API or your code.</p>
<p>But suppose the API's service is available only in production—not in the development environment. In that case, you've got two options:</p>
<ol>
<li>Wait until your app goes live—which could take months.</li>
<li>Clone the API so you can continue your test irrespective of the dependency's availability.</li>
</ol>
<p>Test doubles provide a helpful way to clone your program's dependencies so that your testing activities won't encounter any disruptions.</p>
<p>Typical examples of test doubles are dummy objects, mocks, fakes, and stubs. Let's discuss them below.</p>
<h3 id="heading-what-is-a-dummy-in-test-driven-development">What is a dummy in test-driven development?</h3>
<p>A <strong>dummy</strong> is a test double used to mimic the value of a specific dependency.</p>
<p>For instance, suppose your app depends on a third-party method that requires you to provide some arguments. In such a case, a dummy allows you to pass in pretend values to the parameters of that method.</p>
<h3 id="heading-what-is-a-mock-in-test-driven-development">What is a mock in test-driven development?</h3>
<p><strong>Mock</strong> is a test double used to mimic an external dependency without considering the responses the dependency may return.</p>
<p>For instance, suppose your app depends on a third-party API (for example, Facebook)—which you cannot access in the development mode. Mock allows you to bypass the API so that you can focus on testing your code regardless of the Facebook API's availability.</p>
<h3 id="heading-what-is-a-stub-in-test-driven-development">What is a stub in test-driven development?</h3>
<p>A <strong>stub</strong> is a test double used to mimic an external dependency while returning hand-coded values. You can use the returned value to assess your program's behavior with various test case responses from the dependency.</p>
<p>For instance, suppose your app depends on a third-party API (for example, Facebook)—which you cannot access in the development mode. Stub allows you to bypass the API while mimicking the exact values Facebook will return.</p>
<p>Therefore, stub helps you assess your program's behavior with various response scenarios.</p>
<h3 id="heading-what-is-a-fake-in-test-driven-development">What is a fake in test-driven development?</h3>
<p><strong>Fake</strong> is a test double used to create a working test implementation of an external dependency with dynamic values.</p>
<p>For instance, you can use fake to create a local database that allows you to test how a real database will work with your program.</p>
<h2 id="heading-quick-overview-of-test-driven-development-so-far">Quick Overview of Test-Driven Development So Far</h2>
<p>We've learned that test-driven development helps you jot down your program's behavior before creating the program.</p>
<p>We also saw a simple JavaScript test and used Jest as a test implementation tool.</p>
<p>Let's now see how to test React components.</p>
<h2 id="heading-how-to-test-react-components">How to Test React Components</h2>
<p>The two main tools you need to test your React components are:</p>
<ol>
<li>A test runner tool</li>
<li>A React component testing tool</li>
</ol>
<p>But what exactly is the difference between a test runner and a React component testing tool? Let's find out.</p>
<h2 id="heading-test-runner-vs-react-component-testing-tool-whats-the-difference">Test Runner vs. React Component Testing Tool: What's the Difference?</h2>
<p>Below are the differences between a test runner and a React component testing tool.</p>
<h3 id="heading-what-is-a-test-runner">What is a test runner?</h3>
<p>A <strong>test runner</strong> is a tool developers use to run a test script and print the test's results on the command line (CLI).</p>
<p>For instance, suppose you wish to run the test cases in your project's <code>App.test.js</code> test script. In such a case, you will use a test runner.</p>
<p>The test runner will execute <code>App.test.js</code> and print the test's results on the command line.</p>
<p>Typical examples of test runners are <a target="_blank" href="https://jasmine.github.io/">Jasmine</a>, <a target="_blank" href="https://mochajs.org/">Mocha</a>, <a target="_blank" href="https://github.com/substack/tape">Tape</a>, and <a target="_blank" href="https://jestjs.io/">Jest</a>.</p>
<h3 id="heading-what-is-a-react-component-testing-tool">What is a React component testing tool?</h3>
<p>A <strong>React component testing tool</strong> provides helpful APIs for defining a component's test cases.</p>
<p>For instance, suppose you need to test your project's <code>&lt;App /&gt;</code> component. In such a case, you will use a React component testing tool to define the component's test cases.</p>
<p>In other words, a React component testing tool provides the APIs for writing your component's test cases.</p>
<p>Typical examples are <a target="_blank" href="https://enzymejs.github.io/enzyme/">Enzyme</a> and the <a target="_blank" href="https://testing-library.com/docs/react-testing-library/intro">React Testing Library</a>.</p>
<p>So, now that we know what a test runner and React component testing tool are, let's use a mini-project to understand how React testing works.</p>
<h2 id="heading-project-how-react-testing-works">Project: How React Testing Works</h2>
<p>In the following steps, we will use <a target="_blank" href="https://en.wikipedia.org/wiki/Jest_(JavaScript_framework)">Jest</a> and the <a target="_blank" href="https://testing-library.com/docs/react-testing-library/intro">React Testing Library</a> (by Kent C. Dodds) to learn how React testing works.</p>
<p><strong>Note:</strong> The React official docs <a target="_blank" href="https://reactjs.org/docs/testing.html#tools">recommend</a> the Jest and React Testing Library combination for testing React components.</p>
<h3 id="heading-step-1-get-the-right-node-and-npm-version-1">Step 1: Get the right Node and NPM version</h3>
<p>Make sure that you have <a target="_blank" href="https://codesweetly.com/package-manager-explained#how-to-check-the-installed-node-version">Node 10.16</a> (or greater) and NPM 5.6 (or greater) installed on your system.</p>
<p>If you prefer to use Yarn, ensure you have Yarn 0.25 (or greater).</p>
<h3 id="heading-step-2-create-a-new-react-app">Step 2: Create a new React app</h3>
<p>Use NPM's <a target="_blank" href="https://create-react-app.dev/">create-react-app</a> package to create a new React app called <code>react-testing-project</code>.</p>
<pre><code class="lang-bash">npx create-react-app react-testing-project
</code></pre>
<p>Alternatively, you can use Yarn to configure your project like so:</p>
<pre><code class="lang-bash">yarn create react-app react-testing-project
</code></pre>
<h3 id="heading-step-3-go-inside-the-project-directory">Step 3: Go inside the project directory</h3>
<p>After the installation process, navigate into the project directory like so:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> react-testing-project
</code></pre>
<h3 id="heading-step-4-set-up-your-test-environment">Step 4: Set up your test environment</h3>
<p>Install the following test packages:</p>
<ul>
<li>jest</li>
<li>@testing-library/react</li>
<li>@testing-library/jest-dom</li>
<li>@testing-library/user-event</li>
</ul>
<p><strong>Note:</strong> If you've initialized your React project with <code>create-react-app</code> (step 2), you do not need to install any of the above packages. They come preinstalled and preconfigured in your <code>package.json</code> file.</p>
<p>Now, let's discuss the purpose of each of the above test packages.</p>
<h4 id="heading-what-is-jest">What is Jest?</h4>
<p><a target="_blank" href="https://www.npmjs.com/package/jest">jest</a> is the test runner tool we will use to run this project's test scripts and print the test results on the command line.</p>
<h4 id="heading-what-is-testing-libraryreact">What is @testing-library/react?</h4>
<p><a target="_blank" href="https://www.npmjs.com/package/@testing-library/react">@testing-library/react</a> is the React Testing Library which gives you the APIs you need to write test cases for your React components.</p>
<h4 id="heading-what-is-testing-libraryjest-dom">What is @testing-library/jest-dom?</h4>
<p><a target="_blank" href="https://www.npmjs.com/package/@testing-library/jest-dom">@testing-library/jest-dom</a> provides some set of custom Jest matchers for testing the DOM's state.</p>
<p><strong>Note:</strong> Jest already comes with lots of matchers, so using <code>jest-dom</code> is optional. <code>jest-dom</code> simply extends Jest by providing matchers that make your test more declarative, clear to read, and easy to maintain.</p>
<h4 id="heading-what-is-testing-libraryuser-event">What is @testing-library/user-event?</h4>
<p><a target="_blank" href="https://www.npmjs.com/package/@testing-library/user-event">@testing-library/user-event</a> provides the <code>userEvent</code> API for simulating users' interaction with your app on a web page.</p>
<p><strong>Note:</strong> <code>@testing-library/user-event</code> is a better alternative to the <a target="_blank" href="https://testing-library.com/docs/dom-testing-library/api-events/#fireevent">fireEvent</a> API.</p>
<h3 id="heading-step-5-clean-up-the-src-folder">Step 5: Clean up the <code>src</code> folder</h3>
<p>Delete all files inside the project directory's <code>src</code> folder.</p>
<h3 id="heading-step-6-create-your-code-files">Step 6: Create your code files</h3>
<p>Create the following files inside your project's <code>src</code> folder.</p>
<ul>
<li><code>index.js</code></li>
<li><code>App.js</code></li>
<li><code>App.test.js</code></li>
</ul>
<h3 id="heading-step-7-render-the-app-component">Step 7: Render the <code>App</code> component</h3>
<p>Open your <code>index.js</code> file and render the <code>App</code> component to the DOM like so:</p>
<pre><code class="lang-js"><span class="hljs-comment">// index.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { createRoot } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App"</span>;

<span class="hljs-comment">// Render the App component into the root DOM</span>
createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>)).render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
</code></pre>
<h3 id="heading-step-8-write-your-test-case">Step 8: Write your test case</h3>
<p>Suppose you want your <code>App.js</code> file to render a <code>&lt;h1&gt;CodeSweetly Test&lt;/h1&gt;</code> element to the web page. In that case, open your <em>test script</em> and write some test code specifying the result you expect your <code>&lt;App /&gt;</code> component to produce.</p>
<p><strong>Here's an example:</strong></p>
<pre><code class="lang-js"><span class="hljs-comment">// App.test.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@testing-library/jest-dom"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App"</span>;

test(<span class="hljs-string">"codesweetly test heading"</span>, <span class="hljs-function">() =&gt;</span> {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
  expect(screen.getByRole(<span class="hljs-string">"heading"</span>)).toHaveTextContent(<span class="hljs-regexp">/codesweetly test/i</span>);
});
</code></pre>
<p>Here are the main things we did in the test snippet above:</p>
<ol>
<li>We imported the packages needed to write our test case.</li>
<li><p>We wrote a test case specifying that we expect our <code>&lt;App /&gt;</code> component to render a heading element with a <code>"codesweetly test"</code> text.</p>
</li>
<li><p><a target="_blank" href="https://jestjs.io/docs/api#testname-fn-timeout"><code>test()</code></a> is one of Jest's global methods. We use it to run a test case. The method accepts three arguments:</p>
<ul>
<li>The name of the test (<code>"codesweetly test heading"</code>)</li>
<li>A function containing the expectations you wish to test</li>
<li>An optional timeout argument</li>
</ul>
</li>
<li><a target="_blank" href="https://testing-library.com/docs/react-testing-library/api/#render"><code>render()</code></a> is one of the React Testing Library APIs. We use it to render the component we wish to test.</li>
<li><a target="_blank" href="https://jestjs.io/docs/expect#expectvalue"><code>expect()</code></a> is a Jest method that lets you test the output of your code.</li>
<li><a target="_blank" href="https://testing-library.com/docs/queries/about/#screen"><code>screen</code></a> is a React Testing Library's object containing numerous methods for finding elements on a page.</li>
<li><a target="_blank" href="https://testing-library.com/docs/queries/about/#priority"><code>getByRole()</code></a> is one of the React Testing Library's query methods for finding elements on a page.</li>
<li><a target="_blank" href="https://github.com/testing-library/jest-dom#tohavetextcontent"><code>toHaveTextContent()</code></a> is one of <code>jest-dom</code>'s custom matchers that you can use to confirm the presence of a text content in a specific node.</li>
<li><code>/codesweetly test/i</code> is a <a target="_blank" href="https://codesweetly.com/javascript-regular-expression-object">regular expression</a> syntax that we used to specify a case-insensitive search for <code>codesweetly test</code>.</li>
</ol>
<p>Keep in mind that there are three alternative ways to write the above expect statement:</p>
<pre><code class="lang-js"><span class="hljs-comment">// 1. Using jest-dom's toHaveTextContent() method:</span>
expect(screen.getByRole(<span class="hljs-string">"heading"</span>)).toHaveTextContent(<span class="hljs-regexp">/codesweetly test/i</span>);

<span class="hljs-comment">// 2. Using the heading's textContent property and Jest's toMatch() method:</span>
expect(screen.getByRole(<span class="hljs-string">"heading"</span>).textContent).toMatch(<span class="hljs-regexp">/codesweetly test/i</span>);

<span class="hljs-comment">// 3. Using React Testing Library's name option and jest-dom's toBeInTheDocument() method</span>
expect(screen.getByRole(<span class="hljs-string">"heading"</span>, { <span class="hljs-attr">name</span>: <span class="hljs-regexp">/codesweetly test/i</span> })).toBeInTheDocument();
</code></pre>
<p><strong>Tip:</strong></p>
<p>Add a <code>level</code> option to the <code>getByRole()</code> method to specify your heading's level.</p>
<p><strong>Here's an example:</strong></p>
<pre><code class="lang-js">test(<span class="hljs-string">"codesweetly test heading"</span>, <span class="hljs-function">() =&gt;</span> {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
  expect(screen.getByRole(<span class="hljs-string">"heading"</span>, { <span class="hljs-attr">level</span>: <span class="hljs-number">1</span> })).toHaveTextContent(<span class="hljs-regexp">/codesweetly test/i</span>);
});
</code></pre>
<p>The <code>level: 1</code> option specifies an <code>&lt;h1&gt;</code> heading element.</p>
<p>Suppose you run the test code now. The test will fail because you've not developed the component for which you created the test. So, let's do that now.</p>
<h3 id="heading-step-9-develop-your-react-component">Step 9: Develop your React component</h3>
<p>Open your <code>App.js</code> file and develop the component to pass the prewritten test.</p>
<p><strong>Here's an example:</strong></p>
<pre><code class="lang-js"><span class="hljs-comment">// App.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>CodeSweetly Test<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>The <code>App</code> component, in the snippet above, renders a <code>&lt;h1&gt;</code> element containing the <code>"CodeSweetly Test"</code> text.</p>
<h3 id="heading-step-10-run-the-test">Step 10: Run the test</h3>
<p>Run the prewritten test to check if your program passed or failed.</p>
<pre><code class="lang-bash">npm <span class="hljs-built_in">test</span> App.test.js
</code></pre>
<p>Alternatively, you can use Yarn like so:</p>
<pre><code class="lang-bash">yarn <span class="hljs-built_in">test</span> App.test.js
</code></pre>
<p>Once you've initiated the test, Jest will print a pass or fail message on your editor's console. The message will look similar to this:</p>
<pre><code class="lang-bash">$ jest
 PASS  src/App.test.js
  √ codesweetly <span class="hljs-built_in">test</span> heading (59 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.146 s
Ran all <span class="hljs-built_in">test</span> suites related to changed files.
</code></pre>
<p><strong>Note:</strong> The <code>create-react-app</code> configured Jest in <a target="_blank" href="https://codesweetly.com/javascript-module-bundler/#what-is-webpack---progress---watch">watch mode</a> by default. Therefore, after running <code>npm test</code> (or <code>yarn test</code>), your currently opened terminal will continue to process the <code>test</code> command's activities. So, you won't be able to input any command on that terminal until you stop <code>test</code>'s execution. But you can open a new terminal window simultaneously with the one processing <code>test</code>. </p>
<p>In other words, use one terminal to run <code>test</code> and another to input commands.</p>
<h3 id="heading-step-11-run-the-application">Step 11: Run the application</h3>
<p>Take a look at your app in the browser by running:</p>
<pre><code class="lang-bash">npm start
</code></pre>
<p>Or, if your <a target="_blank" href="https://codesweetly.com/package-manager-explained">package manager</a> is Yarn, run:</p>
<pre><code class="lang-bash">yarn start
</code></pre>
<p>Once you run the command above, your app will automatically open on your default browser.</p>
<h3 id="heading-step-12-refactor-the-test-code-1">Step 12: Refactor the test code</h3>
<p>Suppose you wish to change the heading's text when users click a button. In that case, you can simulate users' interaction with the button to confirm that it works as intended.</p>
<p><strong>Here's an example:</strong></p>
<pre><code class="lang-js"><span class="hljs-comment">// App.test.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/react"</span>;
<span class="hljs-keyword">import</span> userEvent <span class="hljs-keyword">from</span> <span class="hljs-string">"@testing-library/user-event"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@testing-library/jest-dom"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App"</span>;

describe(<span class="hljs-string">"App component"</span>, <span class="hljs-function">() =&gt;</span> {
  test(<span class="hljs-string">"codesweetly test heading"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);
    expect(screen.getByRole(<span class="hljs-string">"heading"</span>)).toHaveTextContent(<span class="hljs-regexp">/codesweetly test/i</span>);
  });

  test(<span class="hljs-string">"a codesweetly project heading"</span>, <span class="hljs-function">() =&gt;</span> {
    render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span></span>);

    <span class="hljs-keyword">const</span> button = screen.getByRole(<span class="hljs-string">"button"</span>, { <span class="hljs-attr">name</span>: <span class="hljs-string">"Update Heading"</span> });

    userEvent.click(button);

    expect(screen.getByRole(<span class="hljs-string">"heading"</span>)).toHaveTextContent(<span class="hljs-regexp">/a codesweetly project/i</span>);
  });
});
</code></pre>
<p>Here are the main things we did in the test snippet above:</p>
<ol>
<li>We imported the packages needed to write our test case.</li>
<li>We wrote a test case specifying that we expect the <code>&lt;App /&gt;</code> component to render a heading element with a <code>"codesweetly test"</code> text.</li>
<li>We wrote another test case simulating users' interaction with the app's button element. In other words, we specified that whenever a user clicks the button, we expect <code>&lt;App /&gt;</code>'s heading to update to <code>"a codesweetly project"</code> text.</li>
</ol>
<p><strong>Note:</strong></p>
<ul>
<li><a target="_blank" href="https://jestjs.io/docs/api#describename-fn"><code>describe()</code></a> is one of Jest's global methods. It is optional code that helps organize related test cases into groups. <code>describe()</code> accepts two arguments:<ul>
<li>A name you wish to call the test case group—for instance, <code>"App component"</code>.</li>
<li>A function containing your test cases.</li>
</ul>
</li>
<li><a target="_blank" href="https://www.npmjs.com/package/@testing-library/user-event"><code>userEvent</code></a> is the React Testing Library's package containing several methods for simulating users' interaction with an app. For instance, in the snippet above, we used <code>userEvent</code>'s <code>click()</code> method to simulate a click event on the button element.</li>
<li>We rendered <code>&lt;App /&gt;</code> for each test case because React Testing Library unmounts the rendered components after each test. However, suppose you have numerous test cases for a component. In that case, use Jest's <a target="_blank" href="https://jestjs.io/docs/api#beforeeachfn-timeout"><code>beforeEach()</code></a> method to run <code>render(&lt;App /&gt;)</code> before each test in your file (or <code>describe</code> block).</li>
</ul>
<h3 id="heading-step-13-refactor-your-react-component">Step 13: Refactor your React component</h3>
<p>So, now that you've refactored your test code, let's do the same for the <code>App</code> component.</p>
<pre><code class="lang-js"><span class="hljs-comment">// App.js</span>

<span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [heading, setHeading] = useState(<span class="hljs-string">"CodeSweetly Test"</span>);

  <span class="hljs-keyword">const</span> handleClick = <span class="hljs-function">() =&gt;</span> {
    setHeading(<span class="hljs-string">"A CodeSweetly Project"</span>);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{heading}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>&gt;</span>
        Update Heading
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Here are the main things we did in the snippet above:</p>
<ol>
<li><code>App</code>'s <code>heading</code> state got initialized with a <code>"CodeSweetly Test"</code> string.</li>
<li>We programmed a <code>handleClick</code> function to update the <code>heading</code> state.</li>
<li>We rendered a <code>&lt;h1&gt;</code> and <code>&lt;button&gt;</code> elements to the DOM.</li>
</ol>
<p>Note the following:</p>
<ul>
<li><code>&lt;h1&gt;</code>'s content is the <code>heading</code> state's current value.</li>
<li>Whenever a user clicks the button element, the <code>onClick()</code> event listener will trigger the <code>handleClick()</code> function. And <code>handleClick</code> will update <code>App</code>'s <code>heading</code> state to <code>"A CodeSweetly Project"</code>. Therefore, <code>&lt;h1&gt;</code>'s content will change to <code>"A CodeSweetly Project"</code>.</li>
</ul>
<h3 id="heading-step-14-rerun-the-test-1">Step 14: Rerun the test</h3>
<p>Once you've refactored your component, rerun the test (or check the actively running test) to confirm that your app still works as expected.</p>
<p>Afterward, check the browser to see your recent updates.</p>
<h3 id="heading-and-thats-it-1">And that's it!</h3>
<p>Congratulations! You've successfully used Jest and the React Testing Library to test a React component. 🎉</p>
<h2 id="heading-overview">Overview</h2>
<p>This article discussed how test-driven development works in JavaScript and ReactJS applications. </p>
<p>We also learned how to use Jest and the React Testing Library to make testing simpler and faster.</p>
<p>Thanks for reading!</p>
<h3 id="heading-and-heres-a-useful-reactjs-resource"><strong>And here's a useful ReactJS resource:</strong></h3>
<p>I wrote a book about React!</p>
<ul>
<li>It's beginner friendly ✔</li>
<li>It has live code snippets ✔</li>
<li>It contains scalable projects ✔</li>
<li>It has plenty of easy-to-grasp examples ✔</li>
</ul>
<p>The <a target="_blank" href="https://www.amazon.com/dp/B09KYGDQYW">React Explained Clearly</a> book is all you need to understand ReactJS.</p>
<p><a target="_blank" href="https://www.amazon.com/dp/B09KYGDQYW"><img src="https://www.freecodecamp.org/news/content/images/2022/01/Twitter-React_Explained_Clearly-CodeSweetly-Oluwatobi_Sofela.jpg" alt="React Explained Clearly Book Now Available at Amazon" width="600" height="400" loading="lazy"></a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is DevTestOps? How to Combine DevOps and TestOps to Build Better Products ]]>
                </title>
                <description>
                    <![CDATA[ Technology is always improving, and developers are always trying to find easier and better ways to improve the software. This is how DevOps came to be, and over time DevTestOps emerged as a new strategy. Today, companies like Spotify apply DevTestOps... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-devtestops/</link>
                <guid isPermaLink="false">66d84fe7f6b5e038a1bde7f0</guid>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ EZINNE ANNE EMILIA ]]>
                </dc:creator>
                <pubDate>Mon, 18 Jul 2022 14:22:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/DEVTESTOPS_1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Technology is always improving, and developers are always trying to find easier and better ways to improve the software. This is how DevOps came to be, and over time DevTestOps emerged as a new strategy.</p>
<p>Today, companies like Spotify apply DevTestOps in software production which makes it easier for them to update, test, and deploy any changes quickly and easily.</p>
<p>In this article, we are going to look at DevTestOps, its objectives, and processes. We'll also look at how to get started with DevTestOps, best practices, and why a company should adopt DevTestOps.</p>
<p>Before we dive into DevTestOps, let’s look at what DevOps and TestOps are and how they work.</p>
<h2 id="heading-what-are-devops-and-testops">What are DevOps and TestOps?</h2>
<p>DevOps is a combination of Development and Operations. DevOps was introduced in the software development life cycle because of the need for continuous product development. It helps teams more easily integrate and deploy software updates.</p>
<p>TestOps involves carrying out large automation test suites across software so that the software is highly efficient with fewer bugs.</p>
<p>Initially, DevOps was introduced in software teams which enabled collaboration across developers and operators only. Then testers carried out tests towards the end of development.</p>
<p>This posed some challenges to the team, though. Especially after working on some software and concluding it was ready for production – only for the testers to run it and discover complex bugs. It was through these challenges that DevTestOps came to be.</p>
<h2 id="heading-what-is-devtestops">What is DevTestOps?</h2>
<p>DevTestOps is a pattern and part of the software development life cycle that includes continuous testing, integration, and deployment.</p>
<p>In DevTestOps, you test the product throughout the different development stages, which helps reduce the number of bugs at later stages.</p>
<p>In DevTestOps, the Development, Testing, and Operations Teams work hand in hand to ensure quality and proper product testing.</p>
<p>This results in quick delivery, with few to no bugs at the end of the software development lifecycle.</p>
<p>DevTestOps came about as an improved version of DevOps with TestOps included. This makes it easier to build, deploy, and develop quality products with fewer bugs.</p>
<h2 id="heading-objectives-of-devtestops">Objectives of DevTestOps</h2>
<p>DevTestOps has objectives that are called manifestos. The manifestos define the goals that you expect to achieve.</p>
<p>According to <a target="_blank" href="https://www.google.com/amp/s/www.mabl.com/blog/the-road-to-devtestops%3fhs_amp=true">mabl.com</a>:</p>
<blockquote>
<p>"There are five manifestos of DevTestOps which include: Continuous testing over testing at the end. Embracing all testing activities over only automated functional testing. Testing what gives value over testing everything. Testing across the team over testing in siloed testing departments. Product coverage over code coverage." (Source: <a target="_blank" href="https://www.google.com/amp/s/www.mabl.com/blog/the-road-to-devtestops%3fhs_amp=true">mabl.com</a>)</p>
</blockquote>
<h2 id="heading-how-devtestops-works">How DevTestOps Works</h2>
<p>There are various stages of the DevTestOps process. These stages are:</p>
<ol>
<li><p><strong>Plan:</strong> In this stage, you define product details and cross-check to make sure everything aligns with the market.</p>
</li>
<li><p><strong>Build:</strong> In this stage, you build the program and upload it to the repository, carry out unit tests, and if you found the program to have no bugs, then it becomes the codebase. You can add possible updates (suggestions or improvements) before moving to the next stage.</p>
</li>
<li><p><strong>Testing:</strong> In this stage, you carry out and analyse all test cases. You can make subsequent updates and test the program again before delivering it and considering it ready for deployment.</p>
</li>
<li><p><strong>Release:</strong> You launch the product and test other written updates before it is added to the codebase.</p>
</li>
<li><p><strong>Monitor:</strong> You constantly check the product for feedback and issues which are handled and updated immediately.</p>
</li>
</ol>
<h2 id="heading-how-to-get-started-with-devtestops">How to Get Started With DevTestOps:</h2>
<p>Many organisations already use DevOps but keep producing buggy software. If you want to switch to DevTestOps to try to reduce the bugs in your code, here are some steps you can follow.</p>
<h3 id="heading-add-continuous-testing-to-yout-devops-roadmap-or-guide">Add continuous testing to yout DevOps roadmap or guide:</h3>
<p>DevTestOps has a similar culture to DevOps except that in this case, you add continuous testing. Testers should become part of the DevOps team so they can test the software immediately after an update.</p>
<h3 id="heading-create-a-devtestops-toolchain">Create a DevTestOps toolchain:</h3>
<p>A toolchain includes all the tools required to implement DevTestOps. For example, your toolchain might comprise tools like Jira, Kubernetes, Selenium, GitHub, Jenkins and more.</p>
<p>You can assign roles to different teams using these tools so they can work effectively with them.</p>
<h3 id="heading-implement-the-tools-in-your-organization">Implement the tools in your organization:</h3>
<p>Next, you'll need to teach teams how to implement these tools and follow the strategies you put in place for software production.</p>
<p>You could add testing roles to each team, which would transform the work culture and encourage collaboration between the developers, testers and operators in each team.</p>
<h3 id="heading-automation">Automation:</h3>
<p>You should apply automation in each of these processes, from the build phase to the deployment phase. This will help all the developers and testers work more efficiently.</p>
<h3 id="heading-make-constant-improvements">Make Constant improvements:</h3>
<p>Finally, you can constantly update the tools and processes to accommodate relevant trends and updates in the technological space.</p>
<h2 id="heading-best-practices-for-devtestops">Best Practices for DevTestOps:</h2>
<p>Here are some best practices you can follow to implement DevTestOps in your team:</p>
<ul>
<li><p>Use the right tools and frameworks for integration, testing, delivery and deployment. This will depend on what works best for the organization.</p>
</li>
<li><p>Apply automation in testing, deployment, and even other stages as well. Automation will help to speed up the process and make it easier for teams to produce faster and meet with deadlines.</p>
</li>
<li><p>All teams should communicate effectively. They should promote collaboration across teams to increase understanding which will increase productivity and eliminate confusion.</p>
</li>
<li><p>The pipeline which comprises continuous integration, testing, delivery and deployment should be monitored always. If you spot a problem, teams can make changes and integrate the updates immediately.</p>
</li>
</ul>
<h2 id="heading-why-should-you-adopt-devtestops">Why Should You Adopt DevTestOps?</h2>
<p>‌There are many reasons to adopt DevTestOps. First, it improves code quality.</p>
<p>It also improves collaboration between developers, testers, and operators. Like in the manifesto, testing is no longer done in siloed testing departments but across each phase.</p>
<p>Additionally, it saves time that is spent on fixing bugs when testing at the end instead of in each stage.</p>
<p>And finally, it makes it easier to perform continuous integration and deployment.‌</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In a world of constant improvements in technology, companies must continually improve their software to meet market standards.</p>
<p>With DevTestOps, it becomes easier for software teams to make updates and improve their products.</p>
<h3 id="heading-resources">Resources:</h3>
<ul>
<li><p><a target="_blank" href="https://www.google.com/amp/s/www.xenonstack.com/insights/devtestops-advantages%3fhs_amp=true">DevOps Advantages And Best Practices | Quick Guide</a></p>
</li>
<li><p><a target="_blank" href="https://www.google.com/amp/s/blog.kms-solutions.asia/an-ultimate-guide-to-devtestops%3fhs_amp=true">What Is DevTestOps And How Can It Transform Agile</a></p>
</li>
<li><p><a target="_blank" href="https://www.google.com/amp/s/katalon.com/resources-center/blog/devtestops-orchestration-agile-teams%3fhs_amp=true">What Is DevOps Orchestration For Agile Teams?</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
