<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Akansh Singhal</title>
    <description>The latest articles on DEV Community by Akansh Singhal (@akansh09).</description>
    <link>https://dev.to/akansh09</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F994796%2F0551b521-c004-43c1-9571-92f73ed3b01f.JPG</url>
      <title>DEV Community: Akansh Singhal</title>
      <link>https://dev.to/akansh09</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/akansh09"/>
    <language>en</language>
    <item>
      <title>Is Your Test Automation Effective? Metrics That Matter..</title>
      <dc:creator>Akansh Singhal</dc:creator>
      <pubDate>Sat, 09 Nov 2024 16:17:41 +0000</pubDate>
      <link>https://dev.to/akansh09/is-your-test-automation-effective-metrics-that-matter-1d2f</link>
      <guid>https://dev.to/akansh09/is-your-test-automation-effective-metrics-that-matter-1d2f</guid>
      <description>&lt;p&gt;In the software testing world, we often hear debates about roles—SDET vs. QA, Manual vs. Automation Testing. But instead of diving into these debates, let’s discuss something critical: how to measure the effectiveness of an automation suite. How do we know we’re building value rather than automating for the sake of automation?&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fallacy of "Automation as Magic Bullet"
&lt;/h2&gt;

&lt;p&gt;A common misconception is that automation can replace manual testing altogether, reducing the time needed to test new features. But automation isn’t a magic bullet; it doesn’t eliminate the need for thoughtful, hands-on testing. Automation is an asset primarily for regression testing—saving time only if it’s done effectively.&lt;/p&gt;

&lt;p&gt;Many of us asked questions like "&lt;strong&gt;Why we are taking so much time when we have automation?&lt;/strong&gt;". These questions often arises from the belief that automation is one time effort and if it is done then no manual effort is required for testing new features. But that is not true, It can only check existing feature, for new feature to test it we need to write new automation and cover cases. It is like development you write some line of code, similarly we need to write some line of code to test it. In fact, sometimes creating robust automation can take longer than manual testing.&lt;/p&gt;

&lt;p&gt;To ensure your automation is truly effective, focus on these key metrics:&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Coverage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What It Means:&lt;/strong&gt; Effective automation ensures significant coverage of critical paths and high-risk areas in the application.&lt;br&gt;
&lt;strong&gt;How to Quantify:&lt;/strong&gt; Track the percentage of smoke, regression &amp;amp; E2E test cases for every feature and classify them into Automated, Non Automatable and Non Automated. With this we can able to say how much percentage of critical and regression test cases are automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug Detection Capability
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What It Means:&lt;/strong&gt; Automation should actively contribute to detecting issues, especially regressions.&lt;br&gt;
&lt;strong&gt;How to Quantify:&lt;/strong&gt; Monitor how often bugs are caught by automated tests, especially during CI/CD processes. A meaningful automation suite detects regressions before code hits production. Tracking metrics like the percentage of escaped defects (bugs caught in production) that could have been detected by automation can highlight areas for improvement. It should have proper assertions at different layers like UI, API and datasources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Segregation of test case on area / priority
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What It Means:&lt;/strong&gt; Tests should be organized by module and priority to ensure focused testing.&lt;br&gt;
&lt;strong&gt;How to Quantify:&lt;/strong&gt; Prioritize tests based on their importance (e.g., smoke, sanity, regression, end-to-end). Measure execution time by category and analyze whether high-priority areas receive faster feedback than lower-priority ones. Clear categorization allows quick, effective testing based on what’s changing in the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speed of Execution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What It Means:&lt;/strong&gt; Effective automation runs tests quickly and scales well with added tests.&lt;br&gt;
&lt;strong&gt;How to Quantify:&lt;/strong&gt; Track the time it takes to run the entire suite and look for opportunities to optimize (e.g., parallelization, selective execution). Execution time should ideally stay within a CI/CD-compatible timeframe, allowing quick feedback without bottlenecks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reporting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What It Means:&lt;/strong&gt; Automation should produce clear, actionable reports.&lt;br&gt;
&lt;strong&gt;How to Quantify:&lt;/strong&gt; Effective reporting includes metrics on test pass/fail rates, trends over time, and drill-down capabilities for failed cases. Good reporting also captures historical trends to aid in proactive decision-making, like identifying flaky tests or frequent failure points.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintenance Effort
&lt;/h2&gt;

&lt;p&gt;The effort required to maintain the suite is a direct indicator of its effectiveness. High maintenance might mean tests are too brittle or aren’t built with flexibility in mind.&lt;/p&gt;




&lt;p&gt;Automation can be transformative when done right. However, it's crucial to measure and refine it continuously to ensure it’s delivering true value. An effective automation suite frees up time for testing new features and exploratory testing, allowing your team to focus on innovation rather than constantly patching a brittle system.&lt;/p&gt;

&lt;p&gt;By quantifying these aspects, you can ensure your automation effort is purposeful, measurable, and valuable—not just “there” for the sake of having automation.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>testing</category>
      <category>testcoverage</category>
      <category>testautomation</category>
    </item>
    <item>
      <title>Visual Regression Testing - Demo using TestPlane</title>
      <dc:creator>Akansh Singhal</dc:creator>
      <pubDate>Thu, 15 Aug 2024 08:43:47 +0000</pubDate>
      <link>https://dev.to/akansh09/visual-regression-testing-2g37</link>
      <guid>https://dev.to/akansh09/visual-regression-testing-2g37</guid>
      <description>&lt;h2&gt;
  
  
  What is Visual Regression Testing?
&lt;/h2&gt;

&lt;p&gt;Visual regression testing is like your superpower to catch those sneaky, unintended visual changes in your UI before they reach production. It’s the difference between saying, “I hope this doesn’t break anything” and knowing it won’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works:
&lt;/h2&gt;

&lt;p&gt;Visual regression tools are like high-tech spies for your UI. They take snapshots of your application before and after changes, comparing them to uncover any sneaky differences. Think of it as a high-stakes game of "Spot the Difference" with your app’s layout and design. Here’s the process in a nutshell:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snapshot Time:&lt;/strong&gt; Capture your UI’s look before changes (baseline snapshot).&lt;br&gt;
&lt;strong&gt;Change Detection:&lt;/strong&gt; Snap your UI again after changes (current release snapshot).&lt;br&gt;
&lt;strong&gt;Comparison:&lt;/strong&gt; The tool compares these snapshots, identifying any discrepancies.&lt;br&gt;
&lt;strong&gt;Feedback Loop:&lt;/strong&gt; If differences are intended, update the baseline image. If not, you’ll be alerted to potential issues.&lt;/p&gt;

&lt;p&gt;Remember those “Spot the Difference” games from newspapers? Visual regression testing is like that, but supercharged. Imagine finding all those differences in seconds, effortlessly!&lt;/p&gt;



&lt;p&gt;Ready to put your observation skills to the test? Let’s play a game!&lt;/p&gt;

&lt;p&gt;Take a look at these two images:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqr5859x6ns588i4k3nl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqr5859x6ns588i4k3nl.png" alt="Image 1" width="451" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa71jnje0ob6otkjuqug0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa71jnje0ob6otkjuqug0.png" alt="Image 2" width="449" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They might look identical, but can you spot all the differences? Try your hand at it, and see how you compare to a visual regression testing tool. For a fun challenge, play the same game &lt;a href="https://www.puzzlesandriddles.com/SpotTheDifference02.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See how it went?&lt;/p&gt;

&lt;p&gt;I have marked all the issues which were reported by the tool!! Check out how the tool's findings matched up:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F498cwi7vm64r4j032m5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F498cwi7vm64r4j032m5u.png" alt="Game Result" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Result from tool :) All 14 issues matched!! Seems efficient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftezh4qiu5kc3pjmvu82f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftezh4qiu5kc3pjmvu82f.png" alt="Tools Matching" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comments how many issues you find in the game..&lt;/p&gt;

&lt;p&gt;The visual regression testing tool caught all 14 issues, proving its efficiency and reliability. Testing isn't just about catching bugs; it's a skill in observation and reporting. And now you’ve seen it in action!&lt;/p&gt;

&lt;p&gt;As some wise man said, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The only way to go fast is to go well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So this testing can help us in moving shift left and reduce our regression time.&lt;/p&gt;


&lt;h2&gt;
  
  
  Want to know Tool?
&lt;/h2&gt;

&lt;p&gt;Now that you understand the concept of visual testing, let’s dive into how I use this tool.&lt;/p&gt;

&lt;p&gt;A few years back, I explored a tool called &lt;a href="https://github.com/gemini-testing/gemini" rel="noopener noreferrer"&gt;Gemini&lt;/a&gt; by Yandex for visual regression testing. Although Gemini is deprecated now, and they created another open source tool &lt;a href="https://github.com/gemini-testing/testplane" rel="noopener noreferrer"&gt;TestPlane&lt;/a&gt; which we are discussing  further and can be explored with the link given. &lt;/p&gt;

&lt;p&gt;Let's see how we can install this tool on our local system&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init testplane@latest demoProject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation you will get new folder called &lt;code&gt;demoProject&lt;/code&gt; and in that folder you will see node_modules where all node packages installed will come and config file with name &lt;code&gt;.testplane.conf.ts&lt;/code&gt;. I have added some additional configs in it (Example: Running suite on mobile and desktop chrome browsers).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
    gridUrl: "http://localhost:4444/wd/hub",
    baseUrl: "http://localhost",
    pageLoadTimeout: 0,
    httpTimeout: 60000,
    testTimeout: 90000,
    resetCursor: false,
    sets: {
        desktop: {
            files: [
                "testplane-tests/**/*.testplane.(t|j)s"
            ],
            browsers: [
                "chrome-phone",
                "chrome"
            ]
        }
    },
    browsers: {
        "chrome-phone": {
            automationProtocol: "devtools",
            headless: true,
            desiredCapabilities: {
                browserName: "chrome",
                "goog:chromeOptions": {
                    args: ["--hide-scrollbars"],
                    mobileEmulation: {
                        deviceMetrics: {
                            width: 360,
                            height: 640,
                            pixelRatio: 3.0
                        },
                        userAgent: "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19"
                    }
                }
            },
            sessionEnvFlags: {
                isMobile: true
            }
        },
        "chrome": {
            automationProtocol: "devtools",
            headless: true,
            desiredCapabilities: {
                browserName: "chrome",
                "goog:chromeOptions": {
                    args: ["--hide-scrollbars", "--start-maximized","--window-size=1920,1080"]
                }
            },
            windowSize: {
                width: 1920,
                height: 1080
            },
            sessionEnvFlags: {
                isMobile: false
            }
        }
    },
    plugins: {
        "html-reporter/testplane": {
            // https://github.com/gemini-testing/html-reporter
            enabled: true,
            path: "testplane-report",
            defaultView: "all",
            diffMode: "3-up-scaled"
        }
    }
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are defining desired capabilities in json format where we are defining browser properties as we defined in ChromeDriver initialisation in Selenium. You can read more about it &lt;a href="https://testplane.io/docs/v8/config/browsers/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And here in &lt;code&gt;example.testplane.ts&lt;/code&gt; we are asserting github testplane repository home page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
describe("Github Public Repository", () =&amp;gt; {
    it("Home Page", async ({ browser }) =&amp;gt; {
        await browser.url("https://github.com/gemini-testing/testplane");
        await browser.$("body").assertView("Full View Port",
            {
                allowViewportOverflow: true,
                captureElementFromTop: true,
                compositeImage: true,
                screenshotDelay: 1000
            }
        )
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see it is finding difference on last released items [18 hours] since our baseline image has [17 hours]. So it is giving failure on dynamic element which is blocker for using above code. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faivugw9xjce2aics7849.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faivugw9xjce2aics7849.png" alt="Diff on dynamic element" width="736" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we have error on dynamic element it is observing correct but seems false positive as a tester we have information about what is dynamic or what is static.&lt;/p&gt;

&lt;p&gt;You can define dynamic elements in &lt;code&gt;ignoreElements&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("Github Public Repository", () =&amp;gt; {
    it("Home Page", async ({ browser }) =&amp;gt; {
        await browser.url("https://github.com/gemini-testing/testplane");
        await browser.$("body").assertView("Full View Port",
            {
                allowViewportOverflow: true,
                captureElementFromTop: true,
                compositeImage: true,
                screenshotDelay: 1000,
                ignoreElements: [
                    "div[data-testid='latest-commit-details']",
                    "a[class='Link--primary d-flex no-underline']",
                    "div[class='react-directory-commit-age']"
                ]
            }
        )
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add fields like &lt;code&gt;ignoreDiffPixelCount&lt;/code&gt;, &lt;code&gt;tolerance&lt;/code&gt;, &lt;code&gt;antialiasingTolerance&lt;/code&gt;, &lt;code&gt;screenshotDelay&lt;/code&gt; if you have some specific requirements. If you see dynamic element is coming as black coloured which we define in &lt;code&gt;ignoreElements&lt;/code&gt;. Commit and Release elements are coming black. With this we can ignore dynamic elements.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkshh6tpiww2o9iqmk2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkshh6tpiww2o9iqmk2p.png" alt="Ignoring Dynamic Elements" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if we have some intended dev changes in the build and you want to change baseline image. You can go to accept mode and convert into baseline image, and then rerun the tests. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05gkb8j4vfbbdw6p0tos.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05gkb8j4vfbbdw6p0tos.png" alt="Accept Base Line" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can improve above code by defining types and constants in typescript for make it more reusable. &lt;/p&gt;

&lt;p&gt;For executing test case use below command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx testplane gui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that will open url mentioned in &lt;code&gt;baseUrl&lt;/code&gt; defined in &lt;code&gt;.testplane.conf.ts&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;If you want to check baseline images &lt;code&gt;testplane/screens/&lt;/code&gt;. &lt;br&gt;
Above executable code can be found in &lt;a href="https://github.com/Akansh09/visualtesting/" rel="noopener noreferrer"&gt;repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Moreover we can use this to visually test different elements as in above code we are testing whole &lt;code&gt;body&lt;/code&gt;. In other case we can check any specific section then i.e also possible similarly.&lt;/p&gt;

&lt;p&gt;If you have use cases related to Before test, Before suite or clean up at the end. You can check this &lt;a href="https://github.com/gemini-testing/testplane/blob/master/docs/writing-tests.md" rel="noopener noreferrer"&gt;resource&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Integration
&lt;/h2&gt;

&lt;p&gt;Integrate TestPlane with your functional testing framework to check visual changes across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different Browser Versions&lt;/li&gt;
&lt;li&gt;Different Browser Types&lt;/li&gt;
&lt;li&gt;Different Devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visual regression testing is not just about identifying bugs; it's about ensuring a seamless user experience.&lt;/p&gt;

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

&lt;p&gt;Visual regression testing is a game-changer for maintaining the visual integrity of your UI. By integrating this testing technique into your workflow, you can catch unintended visual changes early, ensuring a seamless user experience and boosting confidence in your releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spot the Differences:&lt;/strong&gt; Visual regression testing tools act as vigilant eyes, identifying discrepancies between snapshots of your UI and alerting you to potential issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supercharge Your Testing:&lt;/strong&gt; Just like those “Spot the Difference” games we loved as kids, visual regression testing allows you to find discrepancies quickly and efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streamline Your Workflow:&lt;/strong&gt; Tools like TestPlane provide robust solutions for visual regression testing, integrating smoothly with your existing setup and offering powerful configuration options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhance Your Coverage:&lt;/strong&gt; Beyond simple UI checks, visual regression testing can be integrated into various scenarios, including different browsers, devices, and screen sizes, ensuring comprehensive coverage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have used this tool in one of my project and I found it very efficient and it reduce lots of time and small issues which can be missed. &lt;/p&gt;

&lt;p&gt;Got any questions or experiences with visual regression testing tools? Share them in the comments below!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>visualtesting</category>
      <category>testplane</category>
      <category>automation</category>
    </item>
    <item>
      <title>Journey to Integrate SonarQube Analysis on every pull request - Part 3</title>
      <dc:creator>Akansh Singhal</dc:creator>
      <pubDate>Sat, 03 Aug 2024 12:42:35 +0000</pubDate>
      <link>https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-3-355a</link>
      <guid>https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-3-355a</guid>
      <description>&lt;p&gt;Until now we have gone through two parts of this blog series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-1-3kpo"&gt;Problem Statement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-2-5848"&gt;Executing Sonar Analysis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this part, we will explore how to efficiently report issues found on pull requests. I suggest reading the previous two parts for a better understanding of our approach.&lt;/p&gt;

&lt;p&gt;We will divide this into two sections:&lt;br&gt;
SonarQube Analysis&lt;br&gt;
Post SonarQube Analysis: Updating the Report on the PR&lt;/p&gt;

&lt;p&gt;SonarQube performs analysis on a commit level, but pull requests often contain multiple commits. This can lead to false positives if we communicate issues based solely on commit IDs.&lt;/p&gt;

&lt;p&gt;So we are creating a storage in Dynamo DB. You can see how to set up DynamoDB on &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html" rel="noopener noreferrer"&gt;local&lt;/a&gt;. We are using below query to create table in dynamo db. You can refer this on &lt;a href="https://github.com/Akansh09/sonar-analysis/blob/main/sonar_ddb_query.txt" rel="noopener noreferrer"&gt;github&lt;/a&gt; also.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws dynamodb create-table \
    --table-name SonarIssues \
    --attribute-definitions \
        AttributeName=pull_request_id,AttributeType=N\
        AttributeName=analysed_at,AttributeType=S\
    --key-schema \
        AttributeName=pull_request_id,KeyType=HASH\
        AttributeName=analysed_at,KeyType=RANGE\
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 --endpoint=http://localhost:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are storing data in below schema in DynamoDB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class AnalysisRequest {
    private long pullRequestId;
    private String repositoryName;
    private SonarQubeIssuesResponse issues;
    private String analysedAt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we have storage of All analysis on particular PR, we will collate all the issues and if Issue count is 0 we will APPROVE the PR otherwise we will do CHANGES_REQUESTED which is similar to the way common user perform in real life. &lt;br&gt;
Example State Transitions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk87jrww3w90pur9jlqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk87jrww3w90pur9jlqo.png" alt="Approved In case All Issues Resolved" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ccgdmqvc62lzuyl9ke3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ccgdmqvc62lzuyl9ke3.png" alt="Changes Requested" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have created pipeline in Jenkins on the view of above two stages:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3eb3mt3c7po59hm90h6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3eb3mt3c7po59hm90h6x.png" alt="Pipeline" width="800" height="206"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!groovy

pipeline {
    agent any
    parameters {
            string(name: 'REPO_OWNER', defaultValue: 'Akansh09', description: 'Git Repo Owner?')
            string(name: 'REPO_NAME', defaultValue: 'sonar-analysis', description: 'Git Repo Name?')
            string(name: 'SONAR_PROJECT', defaultValue: 'sonar-analysis', description: 'Sonar Project?')
            string(name: 'TARGET_BRANCH', defaultValue: 'main', description: 'Target branch?')
            string(name: 'SONAR_KEY', defaultValue: 'sonar-analysis', description: 'Sonar Key?')
    }

    triggers {
        pollSCM('*/5 * * * *')
    }

    stages {
     stage('SonarQube Analysis') {
       steps {
           script {
              def gitCommitHash = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
              sh "$MAVEN_HOME/bin/mvn clean verify sonar:sonar -Dsonar.projectKey=${params.SONAR_KEY} -Dsonar.projectName=${params.SONAR_PROJECT} -Dsonar.host.url=$SONARQUBE_URL -Dsonar.token=$SONARQUBE_LOGIN -Dsonar.projectVersion=$gitCommitHash"
           }
        }
      }
      stage('Post SonarQube Analysis') {
             steps {
             script {
                 sleep(time:120,unit:"SECONDS")
                     sh "$MAVEN_HOME/bin/mvn clean compile exec:java -Dexec.mainClass=\"com.sonar.analysis.SonarAnalysis\" -DSONAR_KEY=${params.SONAR_KEY} -DTARGET_BRANCH=${params.TARGET_BRANCH} -DGIT_REPO_OWNER=${params.REPO_OWNER} -DGIT_REPO_NAME=${params.REPO_NAME}"
                 }
             }
      }
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First stage we had already discussed in &lt;a href="https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-2-5848"&gt;blog&lt;/a&gt;. In second stage we are doing following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get Corresponding Pull Request Id Source --&amp;gt; Target&lt;/li&gt;
&lt;li&gt;Persist issue in DynamoDB for the current analysis&lt;/li&gt;
&lt;li&gt;Get All Those unresolved issues which are present in the SonarQube for this PR&lt;/li&gt;
&lt;li&gt;Approve in case 0 issues left, CHANGES_REQUESTED in case issues are still present&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since SonarQube do not have mapping on the basis of commit id or PR id. We are getting all OPEN issues and on the basis of that we are finding key which are still unclosed&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public List&amp;lt;SonarQubeIssuesResponse.Issues&amp;gt; getListOfUnResolvedIssues(long pullRequestId){
        DynamoDBUtil dynamoDBUtil = new DynamoDBUtil();
        List&amp;lt;AnalysisRequest&amp;gt; allIssuesPertainingToPullRequest = dynamoDBUtil.getAnalysisRequest(DYNAMO_SONAR_TABLE, pullRequestId);

        Set&amp;lt;SonarQubeIssuesResponse.Issues&amp;gt; analysisRequestSet = allIssuesPertainingToPullRequest.stream().flatMap(analysisRequest -&amp;gt; {
            if(analysisRequest!=null &amp;amp;&amp;amp; analysisRequest.getIssues()!=null){
                return Arrays.stream(analysisRequest.getIssues().getIssues());
            } else {
                return Stream.empty();
            }
        }).collect(Collectors.toSet());

        SonarClient sonarClient = new SonarClient();
        SonarQubeIssuesResponse sonarQubeIssuesResponse = sonarClient.getAllIssuesFromSonarQube();
        List&amp;lt;String&amp;gt; allKeys = Arrays.stream(sonarQubeIssuesResponse.getIssues()).map(SonarQubeIssuesResponse.Issues::getKey).toList();

        return analysisRequestSet.stream().filter(s-&amp;gt;allKeys.stream().anyMatch(s1-&amp;gt;s1.equals(s.getKey()))).toList();
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample PR where this job executed can be seen &lt;a href="https://github.com/Akansh09/sonar-analysis/pull/1" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can refer code with the current implementation on my github &lt;a href="https://github.com/Akansh09/sonar-analysis" rel="noopener noreferrer"&gt;repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we have successfully integrated SonarQube &amp;lt;&amp;gt; Jenkins &amp;lt;&amp;gt; Github. And we are able to achieve our initial goal.&lt;/p&gt;

&lt;p&gt;Hope this resource help you. Thank you for reading this. If you have any questions or need further information, feel free to contact me at &lt;a href="mailto:akanshsinghal7@gmail.com"&gt;akanshsinghal7@gmail.com&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Journey to Integrate SonarQube Analysis on every pull request - Part 2</title>
      <dc:creator>Akansh Singhal</dc:creator>
      <pubDate>Fri, 02 Aug 2024 02:30:02 +0000</pubDate>
      <link>https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-2-5848</link>
      <guid>https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-2-5848</guid>
      <description>&lt;p&gt;In this we will continue with implementing SonarQube with github pull Request. If you want to learn more about SonarQube and its integration with Github, please refer to my previous &lt;a href="https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-1-3kpo"&gt;blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This solution involves integrating Jenkins, SonarQube, and GitHub. Let's divide this problem into two parts:&lt;/p&gt;

&lt;p&gt;Triggering SonarQube analysis from Jenkins to GitHub as soon as a PR is raised.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reporting issues found on the GitHub PR.&lt;/li&gt;
&lt;li&gt;We will start by addressing the first part.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are trying to solve animated part 1st.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favl7oy2snrm2dh00unks.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favl7oy2snrm2dh00unks.gif" alt="Animated Part we are solving first" width="600" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can start Jenkins on your local using &lt;a href="https://www.jenkins.io/download/lts/macos/" rel="noopener noreferrer"&gt;resource&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once Jenkins is ready we have to create Jenkins job of MultiBranch Pipeline&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzq8repelt9n6mjhdz609.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzq8repelt9n6mjhdz609.png" alt="Jenkins Pipeline" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now configure your pipeline as per below image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykjcq6xrqszj65fsd8qd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykjcq6xrqszj65fsd8qd.png" alt="Configuring pipeline" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwo8gzi30ngek3oth9cq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwo8gzi30ngek3oth9cq.png" alt="Configuring pipeline1" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now after setting Jenkins job and adding below pipeline in code base, we are able to execute sonar analysis on this code base.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!groovy
pipeline {
    agent any
    parameters {
            string(name: 'REPO_OWNER', defaultValue: 'Akansh09', description: 'Git Repo Owner?')
            string(name: 'REPO_NAME', defaultValue: 'sonar-analysis', description: 'Git Repo Name?')
            string(name: 'SONAR_PROJECT', defaultValue: 'sonar-analysis', description: 'Sonar Project?')
            string(name: 'TARGET_BRANCH', defaultValue: 'develop', description: 'Target branch?')
    }

    triggers {
        pollSCM('*/5 * * * *')
    }

    stages {
      stage('SonarQube Analysis') {
       steps {
           def gitCommitHash = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
           sh "$MAVEN_HOME/bin/mvn clean verify sonar:sonar -Dsonar.projectKey=Akansh09_sonar-analysis_15fb42fe-8cb3-459f-86ea-7eb5b2e2db21 -Dsonar.projectName=${params.SONAR_PROJECT} -Dsonar.host.url=$SONARQUBE_URL -Dsonar.token=$SONARQUBE_LOGIN -Dsonar.projectVersion=$gitCommitHash"
        }
      }
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include MAVEN_HOME, SONARQUBE_LOGIN &amp;amp; SONARQUBE_URL in environment variable of your jenkins node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9y7j0bm1i2bqg0msknz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9y7j0bm1i2bqg0msknz.png" alt="Sonar Analysis after phase 1" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now part second of this solution is to have this issues persisted on the Github PR which we solve in next part of this blog.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h4dm7wxepw84rbclk3k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h4dm7wxepw84rbclk3k.gif" alt="2nd Part of problem" width="600" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have to fetch issues from Sonarqube and comment on Github. We have to use APIs for it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'http://127.0.0.1:9000/api/issues/search?componentKeys=${SONAR_PROJECT_KEY}&amp;amp;sinceLeakPeriod=true' \
--header 'Authorization: Basic ${SONAR_BASIC_TOKEN}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give you all new issues come in new code changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location 'https://api.github.com/repos/${GIT_REPO_OWNER}/${GIT_REPO_NAME}/pulls/${PR_ID}/reviews' \
--header 'Authorization: Bearer ${GIT_TOKEN}' \
--header 'Content-Type: application/json' \
--data '{
    "body": "ddd",
    "event": "REQUEST_CHANGES"
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above API will comment on the pull request. Now that we know the APIs to perform both steps, there is still one more challenge: the SonarQube API does not provide context about which issues are associated with specific commit IDs. Therefore, there is no direct mapping between Commit ID &amp;lt;&amp;gt; Issue or PR &amp;lt;&amp;gt; Issue.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-3-355a"&gt;Part 3&lt;/a&gt; of this series, we will stitch these APIs together and create a complete solution by writing a wrapper over the SonarQube API.&lt;/p&gt;

&lt;p&gt;If you have any questions or need further information, feel free to contact me at &lt;a href="mailto:akanshsinghal7@gmail.com"&gt;akanshsinghal7@gmail.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sonarqube</category>
      <category>sonar</category>
      <category>codequality</category>
      <category>programming</category>
    </item>
    <item>
      <title>Journey to Integrate SonarQube Analysis on every pull request - Part 1</title>
      <dc:creator>Akansh Singhal</dc:creator>
      <pubDate>Fri, 02 Aug 2024 02:26:58 +0000</pubDate>
      <link>https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-1-3kpo</link>
      <guid>https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-1-3kpo</guid>
      <description>&lt;h2&gt;
  
  
  What is SonarQube?
&lt;/h2&gt;

&lt;p&gt;SonarQube is an open-source platform for inspecting code quality. It performs static code analysis and reports vulnerabilities, code smells, and duplications in the code under analysis. &lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Statement:
&lt;/h2&gt;

&lt;p&gt;With the increase in code velocity and the shift-left approach, it is beneficial to execute linting at multiple phases of the development cycle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;During development:&lt;/strong&gt; Use SonarLint.&lt;br&gt;
&lt;strong&gt;On raising a PR:&lt;/strong&gt; Run SonarQube on new code changes.&lt;br&gt;
&lt;strong&gt;On merging a PR:&lt;/strong&gt; Execute SonarQube on the develop branch.&lt;/p&gt;

&lt;p&gt;If you want to know difference between SonarQube and SonarLint, you can refer this &lt;a href="https://www.bitegarden.com/differences-sonarqube-sonarlint#:~:text=1%20%2D%20SonarQube%20is%20a%20server,code%20status%20of%20a%20project." rel="noopener noreferrer"&gt;link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are using SonarQube Community version, It only supports &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Single Branch&lt;/li&gt;
&lt;li&gt;No PR level segregation. &lt;/li&gt;
&lt;li&gt;Segregation between Overall Code Issues and New Code Issues (Last commit on which analysis was done)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgkrliiypwylcuenka5b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgkrliiypwylcuenka5b.png" alt="Challenge with Community Edition of SonarQube" width="752" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, if you are using the Developer Edition of SonarQube, you can utilize its built-in features to keep your code clean. This edition does come at a cost.&lt;/p&gt;

&lt;p&gt;To address the limitations of the Community Edition, we need to implement a custom solution. This involves writing a wrapper and creating a multi-branch Jenkins pipeline.&lt;/p&gt;

&lt;p&gt;We will use some public APIs from GitHub and SonarQube to make this fully functional. In this blog, I will share my approach to solving this problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ixfua4q6u4eua3l3mej.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ixfua4q6u4eua3l3mej.gif" alt="Problem Statement" width="600" height="321"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation &amp;amp; Setup
&lt;/h2&gt;

&lt;p&gt;You can run sonarqube using docker command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -d --name sonarqube -e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true -p 9000:9000 sonarqube:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More can be checked &lt;a href="https://docs.sonarsource.com/sonarqube/latest/try-out-sonarqube/#installing-a-local-instance-of-sonarqube" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbo3v851cbxhtgjx9dtu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbo3v851cbxhtgjx9dtu.png" alt="SonarQube Login" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Login with default username and password. Now you will see a page to create new project. There are two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Either use any source code management tool like Github, BitBucket&lt;/li&gt;
&lt;li&gt;Other one is to Create New Project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forfw3f4dqs57pj8g9js6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forfw3f4dqs57pj8g9js6.png" alt="Possible Ways to Create Project" width="800" height="421"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Using Github&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we are setting up with direct source code tool then we should be able to create communication channel between Github &amp;lt;&amp;gt; SonarQube. If Sonar is under some firewall or VPN. Please don't go ahead with this approach. &lt;/p&gt;

&lt;p&gt;Now for this we need to perform following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create App in Github :

&lt;ul&gt;
&lt;li&gt;Go to Settings&lt;/li&gt;
&lt;li&gt;Move to Developer's Setting&lt;/li&gt;
&lt;li&gt;Create on New Github app button&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cfn74f7ttx0aaw0o3j3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cfn74f7ttx0aaw0o3j3.png" alt="How Page will be look like?" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add App Name &amp;amp; other mandatory details&lt;/li&gt;
&lt;li&gt;Now Save App&lt;/li&gt;
&lt;li&gt;You will get option to generate private key and client secret&lt;/li&gt;
&lt;li&gt;Add redirection and callback url as &lt;a href="http://localhost:9000/" rel="noopener noreferrer"&gt;http://localhost:9000/&lt;/a&gt; If you 
do not have any valid page.&lt;/li&gt;
&lt;li&gt;Add permission for Pull Request, Webhook, Projects for read &amp;amp; 
write access.&lt;/li&gt;
&lt;li&gt;Save the application and install it by moving to instal menu.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this is done move to SonarQube and Enter github app details like AppId, Client Id, Client Secret &amp;amp; Private Key and Save it. Once that is done, It will ask for authorization, Give permission. &lt;br&gt;
Now you will get the option to select all those projects which you want to import in sonarqube from github. &lt;br&gt;
After selection you will get projects like below image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frglb7n6i5cic8pv8m4pb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frglb7n6i5cic8pv8m4pb.png" alt="Projects imported after selection" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our task is to select way of analysis &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmxbrd80qzeho48n3qel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmxbrd80qzeho48n3qel.png" alt="Way of Analysis" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Going with the Github Actions only in this approach, because I want to get most of the benefit from Github. You will get screen like below on selecting Github Actions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3scljaqusvan1rqsqkq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3scljaqusvan1rqsqkq.png" alt="Github Actions" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now go to repository which you want to integrate and enter generated token there in Secrets field and sonar url in other secret.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5e8lg1y8qop6k4mghym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5e8lg1y8qop6k4mghym.png" alt="Repository Secret" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we want to execute every pull request raised to &lt;code&gt;develop&lt;/code&gt;, we can use below YAML file to do so.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Build

on:
  pull_request:
        branches: [develop]

jobs:
  build:
    name: Build and analyze
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
      - name: Set up JDK 17
        uses: actions/setup-java@v1
        with:
          java-version: 17
      - name: Cache SonarQube packages
        uses: actions/cache@v1
        with:
          path: ~/.sonar/cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar
      - name: Cache Maven packages
        uses: actions/cache@v1
        with:
          path: ~/.m2
          key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-m2
      - name: Build and analyze
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
        run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=${{Project key generated}} -Dsonar.projectName='card'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's raise a PR:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fedoovkge02d002wgloi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fedoovkge02d002wgloi2.png" alt="Once PR is raised it will be shown like" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After raising PR, actions will start executing itself:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4wd5cqqylfdidoufep4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4wd5cqqylfdidoufep4.png" alt="Actions State" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you see analysis got failed: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0hcqco7p0t74z6j35wtn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0hcqco7p0t74z6j35wtn.png" alt="Failed Analysis Reason" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The initial attempt failed because the SonarQube server is not reachable from GitHub's public IP. For the integration to work, our SonarQube instance must be publicly accessible, which is often challenging in organizational settings. In my case, SonarQube is set up on my local machine (127.0.0.1:9000), which is not accessible to GitHub. &lt;/p&gt;

&lt;p&gt;Above PR and actions can be checked on &lt;a href="https://github.com/Akansh09/card/actions" rel="noopener noreferrer"&gt;https://github.com/Akansh09/card/actions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading this. In the &lt;a href="https://dev.to/akansh09/journey-to-integrate-sonarqube-analysis-on-every-pull-request-part-2-5848"&gt;next part&lt;/a&gt;, we will discuss the second approach: integrating GitHub, Jenkins, and SonarQube.&lt;/p&gt;

&lt;p&gt;If you have any questions or need further information, feel free to contact me at &lt;a href="mailto:akanshsinghal7@gmail.com"&gt;akanshsinghal7@gmail.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sonar</category>
      <category>codequality</category>
      <category>sonarqube</category>
      <category>programming</category>
    </item>
    <item>
      <title>Automating Google Mails in your Test Suites</title>
      <dc:creator>Akansh Singhal</dc:creator>
      <pubDate>Sat, 04 Feb 2023 07:37:58 +0000</pubDate>
      <link>https://dev.to/akansh09/automating-google-mails-in-your-automation-suites-1k8o</link>
      <guid>https://dev.to/akansh09/automating-google-mails-in-your-automation-suites-1k8o</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Do you have use cases for validating mails or reading data from them?&lt;/li&gt;
&lt;li&gt;Are you trying to automate Google Mail through Frontend?&lt;/li&gt;
&lt;li&gt;Are you blocked by google while login through frontend?&lt;/li&gt;
&lt;li&gt;Are you validating your mails on Mailinator or yopmail not on google?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you are on correct location. I will be explaining how easy is to validate mails through java. You can Follow below steps for verifying emails using Java:&lt;/p&gt;

&lt;h2&gt;
  
  
  Create App password
&lt;/h2&gt;

&lt;p&gt;As a very first step create an app password for authentication purpose on Gmail. With this you need not to worry about two factor authentication.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move to Manage your google Account Section&lt;/li&gt;
&lt;li&gt;Select Security&lt;/li&gt;
&lt;li&gt;Select App password&lt;/li&gt;
&lt;li&gt;Select Mail as the app and Select Any device&lt;/li&gt;
&lt;li&gt;Click on Generate button&lt;/li&gt;
&lt;li&gt;Save the token for future purpose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqoz4ppaw965v6zb8p0j1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqoz4ppaw965v6zb8p0j1.png" alt="Token Generation" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Folder and filter
&lt;/h2&gt;

&lt;p&gt;Now to have distinction between mails you want to validate and other random emails in your inbox. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on search bar&lt;/li&gt;
&lt;li&gt;And Click on icon on right side&lt;/li&gt;
&lt;li&gt;And Enter values according to your use case&lt;/li&gt;
&lt;li&gt;And Click on click filter button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4i0yyspv784819luiha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4i0yyspv784819luiha.png" alt="Creating Filter" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;And Click on Apply the label &lt;/li&gt;
&lt;li&gt;Then Click on Create Filter button as shown below&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11zox9tasrrkevntetxs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11zox9tasrrkevntetxs.png" alt="Applying Label" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Now we will add code where we will be using above two things&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Include dependency in pom.xml
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- https://mvnrepository.com/artifact/javax.mail/javax.mail-api --&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;javax.mail&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;javax.mail-api&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.5.4&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add code to read Mail
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Properties properties = new Properties();  
properties.put("mail.store.protocol", "imaps");
properties.put("mail.imap.host", "imap.gmail.com");
properties.put("mail.imap.port", "993");
properties.put("mail.imap.starttls.enable", "true");
Session session = Session.getDefaultInstance(properties);
Store store=session.getStore("imaps");
store.connect("imap.gmail.com",USER_NAME, PASSWORD);
//Put folder you have created instead of Mail Folder
Folder folder = store.getFolder("MailFolder"); 
folder.open(Folder.READ_ONLY);
int totalMessagesInFolder=folder.getMessageCount();
System.out.println(totalMessagesInFolder);

Message[] message = folder.getMessages();
//Prints Subject
System.out.println(message[0].getSubject());

//Get Email Body
String value = getTextFromMessage(message[0]);
System.out.println(value);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now next step is to parse the Email Body since this contains multiple parts like HTML, ICS file, Attachment so it is bit complicated.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static String getTextFromMessage(Message message) throws MessagingException, IOException {
        if (message.isMimeType("text/plain")) {
            return message.getContent().toString();
        } 
        if (message.isMimeType("multipart/*")) {
            MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
            return parseMultiPart(mimeMultipart);
        }
        return "";
    }

    public static String parseMultiPart(MimeMultipart multipart) throws IOException, MessagingException {
        String result = "";
        for (int j = 0; j &amp;lt; multipart.getCount(); j++) {
            BodyPart bodyPart = multipart.getBodyPart(j);
            String disposition = bodyPart.getDisposition();
            if (disposition != null &amp;amp;&amp;amp; (disposition.equalsIgnoreCase("ATTACHMENT"))) { 
                System.out.println("Mail have some attachment");
                DataHandler handler = bodyPart.getDataHandler();
                System.out.println("file name : " + handler.getName());                                 
            }
            else { 
                if(bodyPart.isMimeType("text/plain")) {
                    return bodyPart.getContent().toString();
                } else {
                    result+=parseBodyPart(bodyPart);
                }
            }
        }
        return result;
    }

    public static String parseBodyPart(BodyPart bodyPart) throws MessagingException, IOException { 
        if (bodyPart.isMimeType("text/html")) {
            return "\n"+bodyPart.getContent().toString();
        } 
        if (bodyPart.getContent() instanceof MimeMultipart){
            return parseMultiPart((MimeMultipart)bodyPart.getContent());
        }
        return "";
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;With this you can automate your email workflows like reading OTP, verifying mail after particular actions in your automation. You can also use below methods for further validations&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;message.getAllRecipients() //For verifying Recipient email address 
message.getFrom() //For verifying Sender's address
message.getReceivedDate() //For verifying Received date &amp;amp; time
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>web3</category>
      <category>crypto</category>
      <category>webdev</category>
      <category>announcement</category>
    </item>
    <item>
      <title>Selenium: ExpectedCondition vs ExpectedConditions</title>
      <dc:creator>Akansh Singhal</dc:creator>
      <pubDate>Tue, 31 Jan 2023 15:47:00 +0000</pubDate>
      <link>https://dev.to/akansh09/selenium-expectedcondition-vs-expectedconditions-5hfg</link>
      <guid>https://dev.to/akansh09/selenium-expectedcondition-vs-expectedconditions-5hfg</guid>
      <description>&lt;p&gt;Selenium is a popular testing framework used for automating web applications. One of the key features of Selenium is the ability to wait for specific conditions to be met before performing actions on a web page. The two main methods used for waiting in Selenium are ExpectedCondition and ExpectedConditions. In this post, we'll explore the difference between these two methods and when to use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are ExpectedCondition and ExpectedConditions?
&lt;/h2&gt;

&lt;p&gt;ExpectedConditions is utility class which we use to apply waits in Explicit Wait in Selenium. This class includes conditions on which wait can be applied. But what we have to do, if we have to apply wait on some custom condition? Then ExpectedCondition come into picture. &lt;/p&gt;

&lt;p&gt;ExpectedCondition is an interface which inherits (extends) Function interface which is introduced in Java8 along with Consumer, Supplier and Predicate interface. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Function Interface and application in ExpectedCondition?
&lt;/h2&gt;

&lt;p&gt;Function Interface is a type of functional interface that receives one argument and returns a value after the required processing. It has one method named apply which takes one parameter and return a value with specified type in second argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface ExpectedCondition&amp;lt;T&amp;gt; extends Function&amp;lt;WebDriver, T&amp;gt; {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see in above code snippet, we are inheriting Function interface with one argument as WebDriver (which act as input) and T as return type which depends on value passed while implementing this interface. &lt;/p&gt;

&lt;p&gt;Let's see how this can help us while creating automation framework. As we have to use explicit wait with some custom condition which is not provided by &lt;code&gt;ExpectedConditions&lt;/code&gt; class. Then we can implement this interface and override apply method given by this &lt;code&gt;ExpectedCondition&lt;/code&gt; interface (which is inherited from Function interface).&lt;br&gt;
Below code snippet helping us in creating a condition to have wait till Javascript get completely loaded on page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ExpectedCondition&amp;lt;Boolean&amp;gt; expectedConditon=new ExpectedCondition&amp;lt;Boolean&amp;gt;() {
        @Override
        public Boolean apply(WebDriver driver) {
            return ((JavascriptExecutor) driver).executeScript("return document.readyState")
                    .toString().equals("complete");
        }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In above code snippet we are using anonymous class (class without any name) to implement &lt;code&gt;ExpectedCondition&amp;lt;T&amp;gt;&lt;/code&gt; interface by passing Boolean as return type. And then implementing &lt;code&gt;apply&lt;/code&gt; method which this interface inherits from &lt;code&gt;Function&amp;lt;F, T&amp;gt;&lt;/code&gt; interface (a sort of functional interface).&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Using ExpectedCondition and ExpectedConditions
&lt;/h2&gt;

&lt;p&gt;Here are some best practices to keep in mind when using ExpectedCondition and ExpectedConditions in your Selenium tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use ExpectedConditions when possible to save time and ensure consistency in your tests.&lt;/li&gt;
&lt;li&gt;Use ExpectedCondition when you need to define a custom condition that is not provided by Selenium.&lt;/li&gt;
&lt;li&gt;Always specify a timeout when using ExpectedCondition or ExpectedConditions to avoid waiting indefinitely.&lt;/li&gt;
&lt;li&gt;Use the ExpectedConditions.not() method to wait for an element to disappear from the page.&lt;/li&gt;
&lt;li&gt;Use the ExpectedConditions.and() and ExpectedConditions.or() methods to create more complex conditions.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;ExpectedCondition and ExpectedConditions are powerful tools in Selenium for waiting for specific conditions to be met before performing actions on a web page. By understanding the difference between these two methods and when to use them, you can write more efficient and effective Selenium tests. Remember to always use best practices like specifying a timeout and using pre-defined conditions when possible. ExpectedCondition is an interface and ExpectedConditions is a collection of static methods in Selenium that return ExpectedCondition objects. &lt;/p&gt;

</description>
      <category>crypto</category>
      <category>web3</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Prospect or Usage of ChatGPT in Test Automation</title>
      <dc:creator>Akansh Singhal</dc:creator>
      <pubDate>Thu, 22 Dec 2022 17:49:28 +0000</pubDate>
      <link>https://dev.to/akansh09/prospect-of-chatgpt-in-test-automation-4ig7</link>
      <guid>https://dev.to/akansh09/prospect-of-chatgpt-in-test-automation-4ig7</guid>
      <description>&lt;p&gt;As ChatGPT shattered Internet with its launch, So I tried to explore it in terms of Test Automation &lt;/p&gt;

&lt;h2&gt;
  
  
  What is ChatGPT?
&lt;/h2&gt;

&lt;p&gt;ChatGPT is a natural language processing (NLP) model developed by OpenAI that can generate human-like responses to a given prompt. In recent years, the use of ChatGPT has gained popularity in various industries, including test automation. In this post, we'll explore the prospect of ChatGPT in test automation and how it can be used to improve the testing process.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use ChatGPT in Test Automation?
&lt;/h2&gt;

&lt;p&gt;Let's start with first step of software testing i.e &lt;em&gt;Requirement Understanding and Test Case Creation&lt;/em&gt;. So I have send basic requirement of ecommerce search and asked it to write test cases for it.&lt;br&gt;
&lt;strong&gt;Query to ChatGpt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature: User is able to search on different products in ecommerce site
Inputs: Price Range, Item Category, Size, Color, Brand

Can you write all test cases for it?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And response by ChatGPT is truly amazing.. You wanna know?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqo1n7ouj93gq37rt15x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqo1n7ouj93gq37rt15x.png" alt="Test Cases using ChatGPT" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It didn't stop here, you can also get Cucumber Feature file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature: Search for products on ecommerce site
  As a user, I want to be able to search for products on the ecommerce site by specifying different search criteria such as price range, item category, size, color, and brand.

Scenario: Search for products by price range
  Given I am on the ecommerce site's home page
  When I enter a price range in the search bar and press enter
  Then I should see a list of products within the specified price range

Scenario: Search for products by item category
  Given I am on the ecommerce site's home page
  When I select an item category from the dropdown menu and press search
  Then I should see a list of products in the specified category

Scenario: Search for products by size
  Given I am on the ecommerce site's home page
  When I select a size from the size dropdown menu and press search
  Then I should see a list of products in the specified size

Scenario: Search for products by color
  Given I am on the ecommerce site's home page
  When I select a color from the color dropdown menu and press search
  Then I should see a list of products in the specified color

Scenario: Search for products by brand
  Given I am on the ecommerce site's home page
  When I enter a brand name in the search bar and press enter
  Then I should see a list of products from the specified brand

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reaction:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmt0rucpooih2i05pqzmc.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmt0rucpooih2i05pqzmc.jpeg" alt="Reaction" width="386" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then my expectation increase more, and I asked it to automate APIs. So below is my query to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can you automate below curl request using rest assured and java?

curl --location --request POST 'https://localhost:8080/login' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username":"akansh@yopmail.com",
    "password":"12222"
}'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And response can shock you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprz77vbho9zrdg52q2mg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprz77vbho9zrdg52q2mg.png" alt="API Automation using ChatGPT" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Till now it is meeting my expectations. So as per human basic nature, you can't be satisfied with what you have, you always need more. So I pass query to find xpath:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can you write xpath of 2nd &amp;lt;li&amp;gt; tag in below html?

"&amp;lt;div&amp;gt; &amp;lt;li id='akansh'&amp;gt;Abc&amp;lt;/li&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;li&amp;gt;Abc&amp;lt;/li&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;What do you think it meets my expectation?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqe18iv5wzjw4g263tmi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqe18iv5wzjw4g263tmi6.png" alt="Xpath Creation" width="800" height="715"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No, this tool doesn't meet my expectation here, since it doesn't provide me relative xpath. Moreover, here I provide a dummy HTML block, in real life situation we have 1000s Line of code and it has word limit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g62gvioqebo8tacq3gh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9g62gvioqebo8tacq3gh.png" alt="Question Limit" width="800" height="92"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;It is definitely reduce our effort but it can not completely take over us right now. Following are reason why I think so:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Maintainability &amp;amp; Reusability of code:&lt;br&gt;
As we all work on some sort of framework (TDD, BDD etc.) but it provide us solution that uses methods which make us framework useless. Moreover there will be duplicity of code which in return increase maintenance cost. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exploration of locator:&lt;br&gt;
As we seen above, it is unable to find relative xpaths/locators, which are basic building block of UI Automation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Database Validation:&lt;br&gt;
As each organization wants to keep their Database schema private, so it will be conflict of interest if someone tries to write DB query from it or want to validate data from it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integration Test Cases:&lt;br&gt;
You can't rely on ChatGPT to write integration test cases for you. It can't map the impact area between modules and features. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can assume things/Biased: &lt;br&gt;
As tester can never assume requirement or flows, but ChatGPT can assume things since it uses AI and which can lead to miss of potential bug. So we can't totally rely on it. It can add biases in test case formation or data generation on the basis of its AI model.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;ChatGPT is optimzed version of Google Search or Stack Overflow where you can put your query and you will get some instant solution instead of moving to different web links. And it also uses some AI to predict particular solution as per your requirement while current solutions are based on static webpages. ChatGPT can be used in test automation to generate test cases, test data, and test scripts. This can save time and effort in writing and maintaining tests.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
  </channel>
</rss>
