<?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" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Test Automation Master - Medium]]></title>
        <description><![CDATA[This space will help you to learn test automation, and DevOps, follow this space for latest test automation tools, frameworks and many more! - Medium]]></description>
        <link>https://medium.com/automationmaster?source=rss----3f3ec55f5af9---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Test Automation Master - Medium</title>
            <link>https://medium.com/automationmaster?source=rss----3f3ec55f5af9---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 30 May 2026 23:57:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/automationmaster" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[My Engineering Manager asked for a test coverage dashboard. Here’s what I built]]></title>
            <link>https://medium.com/automationmaster/my-engineering-manager-asked-for-a-test-coverage-dashboard-heres-what-i-built-55b81b789327?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/55b81b789327</guid>
            <category><![CDATA[web-automation]]></category>
            <category><![CDATA[automation-engineering]]></category>
            <category><![CDATA[test-automation]]></category>
            <category><![CDATA[test-coverage-report]]></category>
            <category><![CDATA[test-coverage]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Sun, 01 Mar 2026 13:47:49 GMT</pubDate>
            <atom:updated>2026-03-02T08:01:29.842Z</atom:updated>
            <content:encoded><![CDATA[<h4>Automation test coverage report with GitHub Actions and GitHub Pages</h4><p>A few weeks ago, my Engineering Manager came to me with a simple ask:</p><blockquote><em>“Can we have a dashboard where I can see how much of our end-to-end test coverage is actually automated?”</em></blockquote><p>Simple question. Not so simple answer.</p><p>We had a Cucumber + Playwright test suite running in a Gradle Kotlin project, with feature files neatly sitting inside the repository. But there was no easy way for anyone outside the QE team to see at a glance — “hey, we have 40 features, and 80% of the planned scenarios are covered by automation.”</p><p>So I sat down and figured it out. Here’s the full story.</p><h3>First, we had to decide where to track the scenarios</h3><p>Before writing a single line of code, I had a conversation with my engineering manager and head of QE about where we’d actually store the E2E scenario data that we’d measure coverage against. We came up with three options.</p><h4>Option 1: Jira</h4><p>The idea was to create and maintain E2E scenarios as Jira issues, link them to features or epics, and then use the Jira API to pull counts and calculate coverage. The formula would be straightforward:</p><p><strong>Automation Coverage % = (Automated Scenarios / Total Defined Scenarios) × 100</strong></p><p>The pros were real. You’d get full traceability from feature to story to scenario to automation. It scales well. It fits nicely into engineering governance.</p><p>But the cons were just as real. You’d need to document every single existing scenario in Jira first — a significant upfront effort as we don’t have any test case in Jira. You’d need to manage a Jira API token. And every PR would need to make live API calls to Jira, which adds latency and a hard external dependency to your CI pipeline.</p><h4>Option 2: A shared Excel/Google Sheet</h4><p>Keep a spreadsheet with three columns: feature name, scenario name, and status (Automated / Not Automated). A Python script reads it and does the math.</p><p>Quick to set up. No tokens needed.</p><p>But spreadsheets have a way of rotting quietly. Ownership gets fuzzy. Even though Google Sheets has version history, it’s not very useful for developers. People may forget to update it. After six months it becomes a liability rather than an asset. Not ideal.</p><h4>Option 3: A CSV file inside the repository</h4><p>Keep a simple CSV file right inside the E2E test repository. Two columns: feature name and total planned scenario count. A Python script reads it directly during the CI run.</p><p>No Jira dependency. No external API calls. No spreadsheet chaos. The data lives right next to the code it describes, so it gets reviewed in the same pull requests.</p><p>The trade-off is honest and manageable: the QE team needs to remember to update the CSV whenever they add new features or change scenario counts. That’s it.</p><h3>We decided to go with option 3</h3><p>For our team’s situation, this was the right call. We didn’t have Xray or any Jira test management plugin in place, and setting that up just to get a coverage number felt like over-engineering. The CSV approach kept the solution inside our own repository, under version control, with no external dependencies touching our CI pipeline.</p><p>The QE team owns the CSV. When they add a new feature file, they add a row to the CSV. Simple contract, easy to enforce in code review.</p><h3>What I actually built</h3><p>With the approach decided, here’s what the solution looks like end to end.</p><h4>The CSV file</h4><p>A file called planned-automation-tests.csv sits in the repository under the src/test/resources/features directory . It looks like this:</p><pre>Feature,Planned Automation Test Count<br>Login,6<br>Navigation Bar,12<br>Checkout,8<br>User Profile,5</pre><h4>The Python script</h4><p>A script called generate_coverage.py reads the CSV, walks through the actual .feature files in the repository, counts how many scenarios are automated (by counting Scenario: and Scenario Outline: lines), and produces two outputs:</p><ul><li>A Markdown formatted feature coverage summary as a PR comment</li><li>A standalone HTML report for the live dashboard</li></ul><p>The Python script will generate a Markdown comment using the template located at scripts/test-coverage-report/templates/test-coverage.md.tpl.</p><p>It will also generate an HTML report based on the same template found at scripts/test-coverage-report/templates/test-coverage.md.tpl.</p><p>The coverage percentage per feature is simply the automated count divided by the planned count from the CSV.</p><h3>The coverage report dashboard</h3><p>This is the part I’m most happy with. The HTML report isn’t just a plain table — it’s a proper interactive dashboard with dark theme styling that I’m hosting for free on GitHub Pages.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K4a1GN8zoZSLrdwyIef3Dw.png" /><figcaption>Automation Test Coverage HTML Report</figcaption></figure><p>At the top of the report, four summary cards give an instant health check at a glance:</p><ul><li><strong>Overall Coverage %</strong> — the combined coverage across all features, with a colour-coded progress bar (green when high, amber when moderate, red when low)</li><li><strong>Total Planned</strong> — total scenarios defined across all features in the CSV</li><li><strong>Total Automated</strong> — total scenarios actually implemented across all feature files</li><li><strong>Total Missing</strong> — how many scenarios are planned but not yet automated</li></ul><p>These four numbers are the first thing my Engineering Manager looks at. He doesn’t need to scroll a table to understand where we stand.</p><h4>Search, sort, and filter</h4><p>The table below the summary cards isn’t static. It has three interactive controls built in.</p><ul><li><strong>Search</strong> — a search box at the top lets you type any feature name and the table filters in real time. If you have 30+ features, this saves a lot of scrolling.</li><li><strong>Sort</strong> — every column header is clickable and toggles between ascending and descending order. You can sort by feature name alphabetically, by planned count, by automated count, by coverage percentage, or by missing count. The most useful one in practice is sorting by coverage percentage ascending — it immediately surfaces the features with the lowest coverage so the team knows where to focus next.</li><li><strong>Filter by coverage tier</strong> — a dropdown above the Coverage % column lets you filter by health status. The three tiers are:<br>🟢 <strong>≥ 90%</strong> — well covered<br>🟡 <strong>80–89%</strong> — getting there<br>🔴 <strong>&lt; 80%</strong> — needs attention</li></ul><p>Each row’s coverage badge is colour-coded to match these tiers, so the table is easy to scan even without using the filter.</p><h4>The “Missing from CSV” warning</h4><p>This one saved us from a real problem in the first week.</p><p>When a QE engineer adds a new .feature file to the repository but forgets to add a corresponding row to the CSV, the script detects it. It knows the feature file exists (because it can see it in the repo) but has no planned count to measure against.</p><p>Instead of silently skipping it or crashing, the report handles it in two ways. First, a warning banner appears at the top of the table.</p><p><strong><em>⚠️ Attention:</em></strong><em> Some feature files exist but are not included in </em><em>planned-automation-tests.csv. Please define their planned automation test count to ensure accurate coverage reporting.</em></p><p>Second, the affected row appears in the table with a warning icon (<strong><em>⚠️) </em></strong>and a tooltip <em>“Feature not defined in planned-automation-tests.csv”</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3HMkrwPG5dhSrli9oYfdkw.png" /><figcaption>Missing feature file in the planned-automation-tests.csv</figcaption></figure><p>This means the oversight gets caught as soon as the next report generates — either during the PR review or after merge when the dashboard updates. The QE engineer sees it, goes back, adds the CSV row, and raises another PR. It’s a lightweight safety net that keeps the data honest without requiring any manual audit process.</p><h3>The GitHub Actions pipeline</h3><p>The CI pipeline is where all of this gets automated. The workflow has four stages, and the design decisions behind each one matter.</p><h4>Stage 1 — Build</h4><p>Runs mvn -B clean compile to verify the project compiles. Every PR triggers this regardless of what files changed. This is the baseline check.</p><h4>Stage 2 — Test</h4><p>Installs Chrome, runs the full Cucumber test suite using mvn clean test -Pheadless-chrome,qa, and uploads the test report as a build artifact. Again, this runs on every PR — not just ones that touch feature files. You always want your tests to run on every change.</p><h4>Stage 3 — Report (PR coverage comment)</h4><p>This stage is smarter. It only does meaningful work when a PR actually changes a .feature file. Here&#39;s how that works:</p><pre>- name: Detect Feature File Changes<br>  id: changes<br>  uses: dorny/paths-filter@v3<br>  with:<br>    filters: |<br>      features:<br>        - &#39;src/test/resources/features/**/*.feature&#39;</pre><p>If steps.changes.outputs.features == &#39;true&#39;, the stage runs generate_coverage.py, uploads the HTML as a build artifact, and posts a Markdown coverage summary as a comment directly on the PR. The comment includes a table of all features with their planned, automated, and missing counts, plus a link to the full HTML report artifact.</p><p>If no feature files changed in the PR, every coverage step is skipped and a simple “⏭️ No feature file changes detected — skipping coverage report and PR comment.” notice appears in the log. No noise. No redundant comments on PRs that have nothing to do with test coverage.</p><h4>Stage 4 — Deploy to GitHub pages</h4><p>The deploy stage only runs on a push to master — meaning a merged PR. It applies the same path filter logic: if no feature files were part of the merge, the entire deploy is skipped. If feature files did change, it regenerates the report fresh from the merged codebase and pushes it to GitHub Pages.</p><pre>deploy:<br>  if: github.event_name == &#39;push&#39; &amp;&amp; github.ref == &#39;refs/heads/master&#39;</pre><p>A key detail here is that each GitHub Actions job runs on a completely fresh runner. Files generated in one job don’t carry over to another. So the deploy job doesn’t try to download the artifact from the report job — it runs generate_coverage.py again independently. This makes the deploy stage fully self-contained and resilient.</p><h4>The trigger design</h4><p>The trigger setup is something I had to think carefully about:</p><pre>on:<br>  pull_request:      # ALL PRs trigger build + test<br>  push:<br>    branches:<br>      - master       # Merges to master trigger deploy</pre><p>There’s no paths filter on the pull_request trigger. I made this mistake initially — I had set it to only trigger when feature files changed, which meant PRs that touched only Java code didn&#39;t run any CI at all. Build and test were being skipped silently.</p><p>The fix was to let all PRs trigger the workflow, and move the feature file filtering inside the specific jobs that need it. Build and test always run. Coverage reporting only runs when it’s relevant.</p><h4>A verification step before every deployment</h4><p>One more thing I added to the deploy stage is a verification step before it tries to copy and publish anything:</p><pre>- name: Verify coverage report was generated<br>  run: |<br>    if [ ! -f artifacts/feature-coverage.html ]; then<br>      echo &quot;❌ artifacts/feature-coverage.html was not generated. Aborting deploy.&quot;<br>      exit 1<br>    fi<br>    echo &quot;✅ feature-coverage.html found ($(wc -c &lt; artifacts/feature-coverage.html) bytes)&quot;</pre><p>Without this, if generate_coverage.py fails silently or writes to the wrong path, the deploy would still proceed — publishing an empty directory, which GitHub Pages then serves as the repository README. This guard turns that silent failure into a loud, obvious CI error with a clear message.</p><h3>A few things I learned along the way</h3><ul><li><strong>The </strong><strong>cache: &#39;pip&#39; trap: <br></strong>When using actions/setup-python with cache: &#39;pip&#39;, GitHub Actions looks for a requirements.txt or pyproject.toml to use as the cache key. If neither exists, the job fails with a confusing error about no matched files. For a single pip install requests, the cache saves maybe two seconds and isn&#39;t worth it. Just drop the option.</li><li><strong>GitHub Pages source setting:<br></strong>After the deploy workflow successfully pushed to the gh-pages branch, the dashboard was still showing the repository README. The reason was that GitHub Pages was configured to serve from the master branch, not gh-pages. You have to go to Settings → Pages → Branch and change it manually. GitHub doesn&#39;t do this automatically even when you start pushing to gh-pages.</li><li><strong>fetch-depth: 0 for path filtering on push events:</strong> <br>The dorny/paths-filter action diffs the current commit against the previous one to determine which files changed. With the default shallow clone, there&#39;s no previous commit to diff against. Adding fetch-depth: 0 to the checkout step gives it the full history it needs.</li><li><strong>force_orphan: true on the Pages deploy:</strong> <br>Without this, every merge that triggers a deploy adds a new commit to the gh-pages branch. After a few months you&#39;d have hundreds of commits that all just say &quot;update coverage report.&quot; Setting force_orphan: true means the branch always has exactly one commit — the latest snapshot. Clean and simple.</li><li><strong>[skip ci] on the deploy commit message:</strong> <br>The deploy job pushes a commit to the gh-pages branch. Without the [skip ci] tag in the commit message, that push can trigger the CI workflow to run again on itself, creating a loop. Adding it to the commit message tells GitHub Actions to ignore that push.</li></ul><h3>The Result</h3><p>My Engineering Manager now has a URL he can bookmark and check anytime. Every time a feature file changes and gets merged, the dashboard updates automatically. No manual reports. No stale spreadsheets. No one needs to remember to do anything.</p><p>The PR comment means the QE team gets immediate coverage feedback during code review, right where they’re already looking, without switching tools. The “missing from CSV” warning catches the most common mistake — adding a feature file and forgetting to register it — before it has a chance to silently skew the numbers.</p><p>And the whole thing runs on tools we already had: GitHub Actions, Python, and GitHub Pages. Nothing new to pay for or maintain.</p><p>It took a few days to get the workflow logic right, but the end result is clean, reliable, and genuinely useful. Sometimes the best dashboards are the ones that just get out of the way and show you what you need.</p><h3>GitHub Repository</h3><p>It’s hard to get real value without looking at a concrete implementation and a working example. To make things clearer, I’ve put together a sample repository you can explore and reference.</p><p><a href="https://github.com/osandadeshan/cucumber-selenium-java-web-automation-demo">https://github.com/osandadeshan/cucumber-selenium-java-web-automation-demo</a></p><p><em>If you’re working with Cucumber and want to set something similar up, the full workflow YAML and Python scripts can be adapted to most Maven or Gradle Java projects with minimal changes. The CSV approach in particular is underrated — it’s boring in the best possible way.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=55b81b789327" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/my-engineering-manager-asked-for-a-test-coverage-dashboard-heres-what-i-built-55b81b789327">My Engineering Manager asked for a test coverage dashboard. Here’s what I built</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why I Switched from Percy to Playwright for Visual Testing]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/automationmaster/why-i-switched-from-percy-to-playwright-for-visual-testing-7fe3bfa77056?source=rss----3f3ec55f5af9---4"><img src="https://cdn-images-1.medium.com/max/646/0*FGmUeS4x1IjBYuNO.png" width="646"></a></p><p class="medium-feed-snippet">Zero cost, full control, and automated baseline snapshots management</p><p class="medium-feed-link"><a href="https://medium.com/automationmaster/why-i-switched-from-percy-to-playwright-for-visual-testing-7fe3bfa77056?source=rss----3f3ec55f5af9---4">Continue reading on Test Automation Master »</a></p></div>]]></description>
            <link>https://medium.com/automationmaster/why-i-switched-from-percy-to-playwright-for-visual-testing-7fe3bfa77056?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/7fe3bfa77056</guid>
            <category><![CDATA[playwright-test]]></category>
            <category><![CDATA[web-automation]]></category>
            <category><![CDATA[test-automation]]></category>
            <category><![CDATA[visual-testing]]></category>
            <category><![CDATA[playwrights]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Mon, 26 Jan 2026 12:06:04 GMT</pubDate>
            <atom:updated>2026-01-26T12:06:02.889Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Quick guide to being solid with SOLID principles in Java]]></title>
            <link>https://medium.com/automationmaster/quick-guide-to-being-solid-with-solid-principles-in-java-fd758aa11e08?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/fd758aa11e08</guid>
            <category><![CDATA[best-practices]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Sat, 17 Aug 2024 14:14:28 GMT</pubDate>
            <atom:updated>2024-08-22T05:56:15.257Z</atom:updated>
            <content:encoded><![CDATA[<h4>A beginner’s guide to better Java programming</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NLObPUEEyakUrXpBtqZ7Iw.jpeg" /></figure><h3>Introduction</h3><p>In software development, creating maintainable and scalable code is essential. One way to achieve this is by adhering to SOLID principles. SOLID is an acronym for five design principles that make software designs more understandable, flexible, and maintainable. In this article, I am going to break down each principle with real-world examples using Java.</p><h3>What is SOLID?</h3><p><strong>SOLID</strong> stands for:</p><ol><li><strong>S</strong>ingle Responsibility Principle (SRP)</li><li><strong>O</strong>pen/Closed Principle (OCP)</li><li><strong>L</strong>iskov Substitution Principle (LSP)</li><li><strong>I</strong>nterface Segregation Principle (ISP)</li><li><strong>D</strong>ependency Inversion Principle (DIP)</li></ol><h3>1. Single Responsibility Principle (SRP)</h3><p><strong>Definition:</strong> A class should have only one reason to change. This means that a class should have only one responsibility or job.</p><p><strong>Concept:</strong> Imagine a class that handles multiple unrelated responsibilities. If a change occurs in one responsibility, it might impact the other responsibilities, making the class harder to maintain.</p><p><strong>Detailed Example: </strong>Consider a Report class that handles both report generation and formatting:</p><pre>public class Report {<br>    private String content;<br><br>    public void generateReport() {<br>        // Logic to generate the report content<br>        this.content = &quot;Report Content&quot;;<br>    }<br><br>    public void formatReport() {<br>        // Logic to format the report content<br>        System.out.println(&quot;Formatted Report: &quot; + content);<br>    }<br>}</pre><p><strong>Issue:</strong> The Report class has two responsibilities: generating content and formatting it. If formatting requirements change, you might need to modify the Report class, impacting both responsibilities.</p><p><strong>Solution:</strong> Apply SRP by splitting the responsibilities into different classes:</p><pre>public class Report {<br>    private String content;<br><br>    public void generateContent() {<br>        // Logic to generate the report content<br>        this.content = &quot;Report Content&quot;;<br>    }<br><br>    public String getContent() {<br>        return content;<br>    }<br>}</pre><pre>public class ReportFormatter {<br>    public void format(Report report) {<br>        // Logic to format the report content<br>        System.out.println(&quot;Formatted Report: &quot; + report.getContent());<br>    }<br>}</pre><p><strong>Explanation:</strong> Now, Report is only responsible for content generation, while ReportFormatter handles formatting. This makes each class easier to maintain and change independently.</p><h3>2. Open/Closed Principle (OCP)</h3><p><strong>Definition:</strong> Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.</p><p><strong>Concept:</strong> You should be able to add new functionality without altering existing code. This prevents the risk of introducing bugs when modifying existing, stable code.</p><p><strong>Detailed Example: </strong>Consider a payment processing system that initially supports only credit card payments:</p><pre>public class PaymentProcessor {<br>    public void processCreditCardPayment(double amount) {<br>        // Process credit card payment<br>    }<br>}</pre><p><strong>Issue:</strong> If you want to add support for another payment method (e.g., PayPal), you would need to modify the PaymentProcessor class, which could introduce errors.</p><p><strong>Solution:</strong> Use an interface and separate classes for each payment method:</p><pre>public interface PaymentMethod {<br>    void processPayment(double amount);<br>}</pre><pre>public class CreditCardPayment implements PaymentMethod {<br>    public void processPayment(double amount) {<br>        // Process credit card payment<br>    }<br>}</pre><pre>public class PayPalPayment implements PaymentMethod {<br>    public void processPayment(double amount) {<br>        // Process PayPal payment<br>    }<br>}</pre><pre>public class PaymentProcessor {<br>    private PaymentMethod paymentMethod;<br><br>    public PaymentProcessor(PaymentMethod paymentMethod) {<br>        this.paymentMethod = paymentMethod;<br>    }<br><br>    public void processPayment(double amount) {<br>        paymentMethod.processPayment(amount);<br>    }<br>}</pre><p><strong>Explanation:</strong> PaymentProcessor no longer depends on specific payment methods but rather on the PaymentMethod interface. New payment methods can be added without changing PaymentProcessor.</p><h3>3. Liskov Substitution Principle (LSP)</h3><p><strong>Definition:</strong> Subtypes must be substitutable for their base types without altering the correctness of the program.</p><p><strong>Concept:</strong> If a class is a subtype of another, it should be able to replace the parent class without causing issues. This ensures that the derived class adheres to the behavior expected by the base class.</p><p><strong>Detailed Example: </strong>Consider a base class Bird and a subclass Penguin:</p><pre>public class Bird {<br>    public void fly() {<br>        // Fly implementation<br>    }<br>}</pre><pre>public class Penguin extends Bird {<br>    @Override<br>    public void fly() {<br>        // Penguins can&#39;t fly<br>        throw new UnsupportedOperationException(&quot;Penguins can&#39;t fly&quot;);<br>    }<br>}</pre><p><strong>Issue:</strong> Penguin violates LSP because it cannot substitute Bird without causing issues.</p><p><strong>Solution:</strong> Refactor to ensure that subclasses adhere to the expected behavior:</p><pre>public abstract class Bird {<br>    public abstract void move();<br>}</pre><pre>public class Sparrow extends Bird {<br>    @Override<br>    public void move() {<br>        // Flying implementation<br>    }<br>}</pre><pre>public class Penguin extends Bird {<br>    @Override<br>    public void move() {<br>        // Walking implementation<br>    }<br>}</pre><p><strong>Explanation:</strong> Both Sparrow and Penguin correctly implement move, allowing them to be used interchangeably where Bird is expected.</p><h3>4. Interface Segregation Principle (ISP)</h3><p><strong>Definition:</strong> A class should not be forced to implement interfaces it does not use. Clients should not depend on methods they do not use.</p><p><strong>Concept:</strong> Large interfaces that contain methods unrelated to specific clients should be split into smaller, more specific interfaces. This ensures that implementing classes only need to be concerned with methods that are relevant to them.</p><p><strong>Detailed Example: </strong>Consider a Worker interface with unrelated methods:</p><pre>public interface Worker {<br>    void work();<br>    void eat();<br>}</pre><p><strong>Issue:</strong> A RobotWorker might not need the eat method:</p><pre>public class RobotWorker implements Worker {<br>    public void work() {<br>        // Robot working logic<br>    }<br><br>    public void eat() {<br>        // Unnecessary or throws exception<br>    }<br>}</pre><p><strong>Solution:</strong> Split the interface into more specific ones:</p><pre>public interface Workable {<br>    void work();<br>}</pre><pre>public interface Eatable {<br>    void eat();<br>}</pre><pre>public class HumanWorker implements Workable, Eatable {<br>    public void work() {<br>        // Human working logic<br>    }<br><br>    public void eat() {<br>        // Human eating logic<br>    }<br>}</pre><pre>public class RobotWorker implements Workable {<br>    public void work() {<br>        // Robot working logic<br>    }<br>}</pre><p><strong>Explanation:</strong> Workable and Eatable interfaces are more specific, and RobotWorker implements only the Workable interface, adhering to ISP.</p><h3>5. Dependency Inversion Principle (DIP)</h3><p><strong>Definition:</strong> High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.</p><p><strong>Concept:</strong> High-level modules should rely on interfaces or abstract classes rather than concrete implementations. This reduces the coupling between different parts of the system and enhances flexibility.</p><p><strong>Detailed Example: </strong>Consider a UserService that directly depends on UserRepository:</p><pre>public class UserRepository {<br>    public void save(User user) {<br>        // Save user logic<br>    }<br>}</pre><pre>public class UserService {<br>    private UserRepository userRepository = new UserRepository();<br><br>    public void register(User user) {<br>        userRepository.save(user);<br>    }<br>}</pre><p><strong>Issue:</strong> UserService is tightly coupled with UserRepository, making it hard to change the repository implementation or to test UserService in isolation.</p><p><strong>Solution:</strong> Use dependency injection and abstractions:</p><pre>public interface UserRepository {<br>    void save(User user);<br>}</pre><pre>public class JdbcUserRepository implements UserRepository {<br>    public void save(User user) {<br>        // Save user logic using JDBC<br>    }<br>}</pre><pre>public class UserService {<br>    private UserRepository userRepository;<br><br>    public UserService(UserRepository userRepository) {<br>        this.userRepository = userRepository;<br>    }<br><br>    public void register(User user) {<br>        userRepository.save(user);<br>    }<br>}</pre><p><strong>Explanation:</strong> UserService depends on the UserRepository interface rather than a concrete implementation. This allows for different implementations of UserRepository and simplifies testing and maintenance.</p><h3>Conclusion</h3><p>SOLID principles needs to be complied when developing software in order to develop code that is reliable and flexible. These SOLID principles Single Responsibility Principle (SRP), Open/Closed Principle (OCP), Liskov Substitution Principle (LSP), Interface Segregation Principle (ISP), and Dependency Inversion Principle (DIP) serve as guidelines to ensure that your code remains clean, maintainable, and scalable.</p><p>By following SRP, you ensure that each class has a single responsibility, which simplifies maintenance and reduces the risk of unintended side effects when changes are made. The OCP encourages you to design systems that can be extended with new functionality without altering existing code, thereby minimizing the risk of introducing bugs. LSP ensures that derived classes can be used interchangeably with their base classes without breaking the functionality, fostering code reliability and consistency. ISP advocates for creating smaller, more specific interfaces, which makes your code more modular and easier to understand. Finally, DIP promotes the use of abstractions rather than concrete implementations, which enhances flexibility and makes your codebase more adaptable to change.</p><p>Incorporating these principles into your Java projects not only helps in building high-quality software but also equips you with best practices that are recognized and valued across the software development industry. By continuously applying SOLID principles, you pave the way for developing software that is easier to test, extend, and maintain ultimately leading to more successful and sustainable software solutions.</p><p>Utilize these principles, practice them regularly, and observe how they transform your approach to software design, making your code more robust and adaptable to future changes.</p><p><strong>Happy Programming !!!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fd758aa11e08" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/quick-guide-to-being-solid-with-solid-principles-in-java-fd758aa11e08">Quick guide to being solid with SOLID principles in Java</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unit Testing Best Practices In React]]></title>
            <link>https://medium.com/automationmaster/react-unit-testing-best-practices-46451ce9b79e?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/46451ce9b79e</guid>
            <category><![CDATA[jest]]></category>
            <category><![CDATA[unit-testing]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Wed, 15 Mar 2023 05:43:09 GMT</pubDate>
            <atom:updated>2023-03-15T05:50:25.346Z</atom:updated>
            <content:encoded><![CDATA[<h4>React application unit testing using Jest</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Odp4AW66gOWIuN9u83WMoA.png" /></figure><h3><strong>What is Unit Testing?</strong></h3><ul><li>Unit testing is a testing method that tests an individual unit of software in isolation.</li><li>Unit testing for React Apps means testing an individual React Component.</li></ul><blockquote><strong><em>“Unit testing is a great discipline, which can lead to 40% — 80% reductions in bug density.</em>” — Eric Elliotte</strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rVYu7hW7MFjEbMdF.png" /></figure><h3><strong>The Test Pyramid</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/466/0*w-yYpZIjydDh7jaD.png" /><figcaption>The test pyramid</figcaption></figure><p>Stick to the pyramid shape to come up with a healthy, fast, and maintainable test suite: Write <strong><em>lots</em></strong> of small and fast <strong><em>unit tests</em></strong>. Write <strong><em>some</em></strong> more <strong><em>coarse-grained tests</em></strong> and <strong><em>very few</em></strong> <strong><em>high-level tests</em></strong> that test your application from end to end. Watch out that you don’t end up with a <a href="https://watirmelon.blog/testing-pyramids/">test ice-cream cone</a> that will be a nightmare to maintain and takes way too long to run.</p><h3><strong>Benefits of Unit Testing</strong></h3><ol><li><strong>Process becomes Agile</strong></li></ol><p>The Agile Testing process is the main advantage of unit testing. When you add more features to the software, it might affect the older designs and you might need to make changes to the old design and code later. This can be expensive and require extra effort. But if you do unit testing, the whole process becomes much faster and easier.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/534/1*rPWPQetB6LI0TUVoYkUdVw.png" /><figcaption>Agile Workflow</figcaption></figure><p><strong>2. Quality of code</strong></p><p>Unit testing significantly improves the quality of the code. It helps developers to identify the smallest defects that can be present in the units before they go for the integration testing.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/378/1*GIZgETmKaa_01w3zApqKMg.png" /></figure><p><strong>3. Facilitates changes</strong></p><p>Refactoring the code or updating the system library becomes much easier when you test each component of the app individually.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*NSBqesDjm9bO4PbR.png" /></figure><p><strong>4. Smooth debugging</strong></p><p>The debugging process is very simplified by doing unit testing. If a certain test fails, then only the latest changes that have been made to the code need to be debugged.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/0*gMSHPJH4IjJfz8n-.png" /></figure><p><strong>5. Reduction in cost</strong></p><p>When bugs are detected at an early stage, through unit testing, they can be fixed at almost no cost as compared to a later stage, let’s say during production, which can be really expensive.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EyV7prWzdDivLqx7.png" /></figure><p><strong>6. Act as the best form of documentation</strong></p><p>Unit tests tell you how the original authors intended their code to be used. Read the unit tests and you can learn how the authors consume their own produce.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/0*7ZJwlLg6M-scReuE.png" /></figure><h3><strong>Best Practices</strong></h3><ol><li><strong>Use one test file per component</strong></li></ol><p>Separate different tests into different files to make them well separated, keep the files slim, and make them easily maintainable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/560/1*u-Oz0QuV4enPi_TarY-FWQ.png" /><figcaption>Separating different tests into different files</figcaption></figure><p><strong>2. Organize your tests with <em>describe</em> and <em>test</em>/<em>it</em></strong></p><p>Use <strong><em>describe</em></strong> to create blocks that group several related tests together and use <strong><em>test</em></strong> or <strong><em>it</em></strong> to execute individual test cases.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*sB4m_FBUNj13cCYgil-1Sg.png" /><figcaption>Using <strong><em>describe</em></strong> as blocks and <strong><em>test/it</em></strong> as individual test cases</figcaption></figure><p><strong>3. Setup and reset commons in <em>beforeEach</em>/<em>afterEach</em> hooks</strong></p><p>Use <strong><em>beforeEach</em></strong> and <strong><em>afterEach</em></strong> hooks to set up common codes for test cases and reset all mocks after your tests.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/928/1*rzoA-tYSyAgkwfbWuyQbjg.png" /><figcaption>Using <strong><em>beforeEach/afterEach</em></strong> hooks</figcaption></figure><p><strong>4. Use the three A’s pattern</strong></p><p>Arrange/Act/Assert (AAA) is a pattern for organizing unit tests. It breaks tests down into three clear and distinct steps:</p><ul><li><strong>Arrange</strong>: Perform the setup and initialization required for the test.</li><li><strong>Act</strong>: Take action(s) required for the test.</li><li><strong>Assert</strong>: Verify the outcome(s) of the test.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/934/1*uXXe4S1qB7hoHIHLm64rtw.png" /><figcaption>Using 3A’s pattern</figcaption></figure><p><strong>5. Also test what should not happen</strong></p><p>When writing tests, a common practice is to test what a function should do. But you should not only test the obvious but also test the edge cases and negative cases.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/792/1*_R4fT32HvZaSP1uWsTHVyg.png" /><figcaption>Testing the negative cases</figcaption></figure><p><strong>6. Make your tests deterministic</strong></p><p>Your tests should not depend on each other. It goes without saying, but for your tests to be deterministic, you want to ensure that your tests don’t depend on each other. Each test case should be independent of the other and should be able to pass on its own.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*IDYo-pDLePq4cNtg16RsKw.png" /><figcaption>Using mocking to independent tests</figcaption></figure><p><strong>7. Don’t duplicate implementation logic</strong></p><p>You might copy the same flaws in the logic over to your test suite which means your test suite will pass, but not because it is working correctly. Rather, it contains the same bug as your function. Always test the outcomes, not the implementation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/778/1*KhlxfHopjCz6cb9GulS2oQ.png" /><figcaption>Testing outcomes, not the implementation</figcaption></figure><p><strong>8. Run your tests as part of your deployment</strong></p><p>Run your test suite before each deployment to ensure you didn’t accidentally break any functionality. That way, you can ensure that no bugs are released into a test/production environment for parts of your application that are already covered by tests.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/804/1*hrQytCZ6CrbZC-1p9AUZOA.png" /><figcaption>Deployment life-cycle</figcaption></figure><h3><strong>Demo Repository</strong></h3><p><a href="https://github.com/osandadeshan/react-jest-unit-testing-demo">GitHub - osandadeshan/react-jest-unit-testing-demo: A sample project to demonstrate unit testing for a React project using Jest.</a></p><h3><strong>References</strong></h3><ul><li><a href="https://www.webtips.dev/webtips/jest/jest-best-practices">https://www.webtips.dev/webtips/jest/jest-best-practices</a></li><li><a href="https://martinfowler.com/articles/practical-test-pyramid.html">https://martinfowler.com/articles/practical-test-pyramid.html</a></li><li><a href="https://reactjs.org/docs/testing.html">https://reactjs.org/docs/testing.html</a></li></ul><p><strong>Happy Unit Testing !!!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=46451ce9b79e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/react-unit-testing-best-practices-46451ce9b79e">Unit Testing Best Practices In React</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[No more test flakiness from Selenium locator changes]]></title>
            <link>https://medium.com/automationmaster/no-more-test-flakiness-from-selenium-locator-changes-c25a96d0736d?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/c25a96d0736d</guid>
            <category><![CDATA[healenium]]></category>
            <category><![CDATA[selenium]]></category>
            <category><![CDATA[web-automation]]></category>
            <category><![CDATA[mobile-automation]]></category>
            <category><![CDATA[flaky-tests]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Thu, 21 Oct 2021 08:44:20 GMT</pubDate>
            <atom:updated>2021-10-21T12:04:09.579Z</atom:updated>
            <content:encoded><![CDATA[<h4>Heal your web/mobile locators using Healenium</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rr3TJZf1cfzE4MO-TSJW0A.png" /></figure><h3>What is Healenium?</h3><p><a href="https://healenium.io/">Healenium</a> is an open-source test framework extension that enhances the stability of Selenium-based test cases. It automatically handles the updated web and mobile elements. In practical scenarios, Web and mobile applications are updated constantly in every sprint and that may cause locator changes. Healenium uses a type of machine-learning algorithm to analyze the current page state for changes, handle <strong><em>NoSuchElement </em></strong>test failures, and fix broken tests at runtime by replacing the failed locator with a new value that matches the best and performs an action with the new element successfully. After the test run, Healenium provides detailed reporting with the fixed locators and screenshots. In addition, it supports advanced features like a parallel test run, remote test run, iFrames, actions, selenide integration.</p><p>All of this decreases the time and effort required to write reliable Selenium tests, as well as the number of test cases that fail due to test defects.</p><h3>How Does It Work?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6KPrRJrMW4uqQoht.jpg" /></figure><p>Healenium has a client component that connects to test automation frameworks. It has a Tree-comparison dependence in it. An algorithm with self-healing capabilities that examines the current DOM state and generates a new CSS locator by searching for the best subsequence in the current state. It also implements Selenium WebDriver and modifies the findElement method, triggering the Tree-comparing process and initiating self-healing if the NoSuchElement exception is caught.</p><p>Healenium’s backend is a server that uses a PostgreSQL database to execute interactions between the client and the database, which records old and new locator values as well as relevant information such as DOM page, method name, class name, screenshots, and so on.</p><p>There are also Maven and Gradle plugins that generate healing results reports and interact with the backend to obtain information about the healing elements.</p><p>Healenium also offers an <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a> plugin for updating the codebase with new location values.</p><h3>Demo Project</h3><h4>Pre-requisites:</h4><ul><li>Clone this GitHub project<br><a href="https://github.com/osandadeshan/healenium-demo">https://github.com/osandadeshan/healenium-demo</a></li><li>Download and install <a href="https://www.jetbrains.com/idea/">JetBrains IntelliJ IDEA</a></li><li>Download and install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a></li></ul><h4>Steps to follow:</h4><blockquote><strong>Starting the Healenium Backend</strong></blockquote><p>1. First, make sure Docker is up and running.</p><p>2. Open a Command Prompt or Terminal in the “<strong><em>healenium</em></strong>” directory and execute:</p><pre><strong>docker-compose up -d</strong></pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Kc0a9vrFpoZgcFxB.png" /><figcaption>Running the Healenium backend and database containers</figcaption></figure><p>3. Wait for docker to download the images and finish the setup.</p><p>4. Using the Docker Desktop UI, check that the Healenium backend and database containers are running.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tjRczPfagGZrknFR.png" /><figcaption>Checking that the Healenium backend and database containers are running</figcaption></figure><p>Or you can execute the below command to check that.</p><pre><strong>docker ps</strong></pre><blockquote><strong>Integrating Healenium in tests</strong></blockquote><p>It’s as simple as writing one line of code to integrate Healenium into your framework. Wrapping the WebDriver in the new SelfHealingDriver is all that’s required.</p><p>But, before that, you have to import,</p><pre><strong>import com.epam.healenium.SelfHealingDriver;</strong></pre><p>Example code:</p><pre>@BeforeMethod<br>public void before() {<br>    WebDriver delegate = new ChromeDriver(); // declare delegate<br>    driver = SelfHealingDriver.create(delegate); // create Self-healing driver<br>    driver.manage().window().maximize();<br>    driver.navigate().to(&quot;<a href="http://automationpractice.com/">http://automationpractice.com/</a>&quot;);<br>}</pre><blockquote><strong>Running the tests</strong></blockquote><p>Run the tests with the proper locators at least once using <strong><em>mvn clean test</em></strong>. After that, we make the following changes to “<strong><em>index.html</em></strong>” in the “<strong><em>src/main/resources/checkout</em></strong>” folder. (You can change any locators as you wish)</p><ul><li>id=“<strong>address</strong>” to id=“<strong>address1</strong>”</li><li>id=“<strong>cc-name</strong>” to id=“<strong>card-name</strong>”</li><li>id=“<strong>cc-number</strong>” to id=“<strong>card-number</strong>”</li><li>id=“<strong>cc-expiration</strong>” to id=“<strong>card-expiration</strong>”</li><li>id=“<strong>cc-cvv</strong>” to id=“<strong>card-cvv</strong>”</li><li>“<strong>Continue to checkout</strong>” to “<strong>Checkout</strong>”</li></ul><p>Normally, this would cause the locators to malfunction because they rely on exact matches. The test will pass because the self-healing driver is used, and it will heal the locators during the runtime.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*0RSdCMdKPO-mRw53.png" /><figcaption>Healing the locators during the runtime</figcaption></figure><p>The locator has been fixed, and the action has been appropriately done, as indicated by the red boxes.</p><p>Let’s say you want to fine-tune Healenium to include a score cap or recovery tries. In such a scenario, create a file called “<strong><em>healenium.properties</em></strong>” and place it in the test resources (“<strong><em>src/test/resources</em></strong>”) directory. The following is the file’s content:</p><pre><strong>recovery-tries = 1<br>score-cap = 0.5<br>heal-enabled = true<br>serverHost = localhost<br>serverPort = 7878</strong></pre><ul><li><strong>recovery-tries</strong> — The number of times the algorithm will try to discover a matched locator.</li><li><strong>score-cap</strong> — The minimum matching score required for the detected locator to be accepted (50% is represented by the number 0.5).</li><li><strong>heal-enabled</strong> — A toggle switch that turns healing on or off. True and false are the accepted values.</li><li><strong>serverHost </strong>— The URL of the Docker Container server that we established while setting up the backend.</li><li><strong>serverPort </strong>— The above-mentioned server’s port</li></ul><p>Run the tests using <strong><em>mvn clean test</em></strong> to generate a new report from the command line. A link to the report generated in the console will appear after a successful test run.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*OixXkYMHr3IqCFkk.png" /><figcaption>Running the tests</figcaption></figure><p>When you open the link in a browser, you’ll get a list of all the locators that have been fixed, along with screenshots of the page where the locators have been fixed. On the right side, there’s a switch to see if the locator has been properly resolved with the correct one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FRzgugtct9YmIZGd.png" /><figcaption>Healing Report</figcaption></figure><p>This boilerplate also includes an Allure report as an added feature. After running <strong><em>mvn clean test</em></strong>, run <strong><em>allure serve target/allure-results</em></strong> to generate the Allure report.</p><p>By installing the “<strong>Healenium</strong>” IDE plugin, you can enable the automatic locator updating feature into the IntelliJ IDEA. You can right-click on the locators you want to correct and select Healing Results. You’ll see a little window with a list of fixed locators to choose from, along with their corresponding scores.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/787/0*4RJl5Lbqt8Jh2DgI.png" /><figcaption>Healing the locators using “<strong>Healenium</strong>” IntelliJ IDEA plugin</figcaption></figure><p><strong>Happy Automation !!!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c25a96d0736d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/no-more-test-flakiness-from-selenium-locator-changes-c25a96d0736d">No more test flakiness from Selenium locator changes</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting up Automated Code Reviews]]></title>
            <link>https://medium.com/automationmaster/setting-up-automated-code-reviews-f8d370888cc4?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/f8d370888cc4</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[automated-code-review]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[code-review]]></category>
            <category><![CDATA[tools]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Sat, 16 Oct 2021 07:26:27 GMT</pubDate>
            <atom:updated>2021-10-22T08:29:02.681Z</atom:updated>
            <content:encoded><![CDATA[<h4>No more hardcore manual reviews ❌</h4><h3>Introduction</h3><h4>What is a Code Review?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*w-hJe2R5ScNrj3cs.png" /></figure><p>“<strong>Code Review</strong>, also known as Peer Code Review, is the act of consciously and systematically convening with one’s fellow programmers to check each other’s code for mistakes and has been repeatedly shown to accelerate and streamline the process of software development like few other practices can.” (From <a href="https://smartbear.com/learn/code-review/what-is-code-review/">SmartBear</a>).</p><h4>The Benefits of Code Reviews</h4><ul><li><strong>Improves code quality:</strong> Code reviews improve code quality by detecting issues before they snowball out of control and ensuring consistent standards. This leads to robust software that is built from components for seamless integration and functionality.</li><li><strong>Supports knowledge transfer:</strong> Source code that is constantly under review allows developers to learn more reliable techniques and best practices.</li><li><strong>Helps teams create better documentation: </strong>Code reviews also help teams to create better documentation, which makes it easier for developers to add features and upgrade existing ones in the future.</li><li><strong>Makes QA testing easier: </strong>Another benefit of maintaining consistent standards is a source code that is easier for specialists and testers to understand. In Quality Assurance (QA) testing, testers will not only have to check the quality of the code, but they will also have to identify issues leading to poor tests. This can lead to persistent, avoidable delays in development due to further testing and reworking.</li></ul><h4>Challenges in Code Reviews</h4><ul><li><strong>Code review happens too late in the development process:</strong> The work has already been done. To rework or to start again could be very time-consuming. The reviewers have a difficult choice: risk the current release while the work is redone, or let it through and suffer the additional technical debt.</li><li><strong>The Wait Time:</strong> This is the biggest issue in code reviews. When the author of the code opens a pull request and marks it ready for review, they need to wait until another person comes along and reviews it. This could be 1 hour after the pull request has been opened (if you are lucky) or it might take a few days or even weeks (if you are not lucky). The wait time is especially painful for us programmers because, after each wait time, the context is lost and needs to be rebuilt again. The longer the wait time the more difficult it is to remember how all the code changes fit together into the shippable feature.</li></ul><h4>Solution for the challenges in Code Reviews</h4><ul><li><strong>Automated code reviews:</strong> It will provide quick feedback for the commits you pushed to the repository.</li></ul><h3>How to integrate automated code reviews into your project?</h3><h4>Required Tool</h4><ul><li><em>Code Inspector GitHub Action</em></li></ul><h4>How does it work?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*cp6WJ-PMhfrT8CP9.png" /><figcaption>Workflow of the Code Inspector</figcaption></figure><h4>Steps to follow</h4><blockquote><strong>Step 1: Create a project in Code Inspector</strong></blockquote><ul><li>Sign up on <a href="https://www.code-inspector.com/">Code Inspector</a>.</li><li>Create a project.</li><li>Install Code Inspector for a repository or multiple repositories you like to have automated code reviews.</li></ul><blockquote><strong>Step 2: Get your Code Inspector API keys</strong></blockquote><ul><li>In your profile, generate API keys.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/483/0*nqJCBsjNHa3kEMFg.png" /><figcaption>Generating API keys</figcaption></figure><ul><li>Once you click on the button, the following window will appear.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/385/0*AziLn7Y-jA1Rx927.png" /><figcaption>Generated API keys</figcaption></figure><blockquote><strong>Step 3: Configure your Code Inspector API keys in your GitHub repository</strong></blockquote><ul><li>You need to add your Code Inspector API keys into GitHub.</li><li>On GitHub, go in your repository settings, click on the secret <em>Secrets</em> (on the right), and create a new secret.</li><li>Create a secret called <strong>CODE_INSPECTOR_ACCESS_KEY</strong> and set it to the value of the access key generated at the previous step.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1023/0*iSZx9q51FJBZgQLb.png" /><figcaption>Creating repository secret key for “<strong>CODE_INSPECTOR_ACCESS_KEY</strong>”</figcaption></figure><ul><li>Create another secret called <strong>CODE_INSPECTOR_SECRET_KEY</strong> and set it to the value of the secret key generated at the previous step.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*7FzznElkxQ7SZAcy.png" /><figcaption>Creating repository secret key for “<strong>CODE_INSPECTOR_SECRET_KEY</strong>”</figcaption></figure><ul><li>Once all secrets have been created, we should have the following secrets generated.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/769/0*1Z8x9ek8mxUGy_jh.png" /><figcaption>Repository secret keys for Code Inspector</figcaption></figure><blockquote><strong>Step 4: Configure the GitHub action</strong></blockquote><p>Create a file <strong>.github/workflows/main.yml</strong> and insert the following content.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bd1baed7c165894482350a600ac680bd/href">https://medium.com/media/bd1baed7c165894482350a600ac680bd/href</a></iframe><p>The following parameters <strong>should <em>NOT</em> be changed</strong>:</p><ul><li><strong>repo_token:</strong> This is how Code Inspector can access your repository</li><li><strong>code_inspector_access_key </strong>and <strong>code_inspector_access_key</strong>: This is how the action can communicate with the Code Inspector analysis engine.</li></ul><p>The following parameters<strong> <em>CAN </em>be changed</strong>:</p><ul><li><strong>min_quality_grade</strong>: The minimum grade your project should have. Valid values are: <strong>EXCELLENT</strong>, <strong>GOOD</strong>, <strong>NEUTRAL</strong>, <strong>WARNING</strong>, <strong>CRITICAL</strong></li><li><strong>min_quality_score</strong>: The minimum code quality scores your project should have. This is a value between <strong>0</strong> and <strong>100</strong>.</li><li><strong>max_defects_rate</strong>: The number of defects per line of code. For example, the value <strong>0.001</strong> means 1 defect per 1000 lines of code.</li><li><strong>max_complex_functions_rate</strong>: The rate of complex functions (E.g. functions with high cyclomatic complexity). For example, a value of <strong>0.5</strong> means that the code should not have more than 50% of functions with high complexity.</li><li><strong>max_long_functions_rate</strong>: The rate of long functions (E.g. functions that are too long to be correctly read by developers). For example, a value of <strong>0.4</strong> means that the code should not have more than 40% of long functions.</li><li><strong>project_name</strong>: The name of the project on <a href="https://www.code-inspector.com/">Code Inspector</a>. This argument is optional: if you set a project name, the analysis engine will use the preferences of this project. Leave blank for not using a project.</li><li><strong>max_timeout_sec</strong>: How many seconds the analysis should come back to you. Default is 600 secconds (10 minutes).</li></ul><blockquote><strong>Step 5: Enjoy the automated code reviews</strong></blockquote><ul><li>Push a new commit and the Code Inspector engine will check if the new code meets your criteria.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gjdVPHamhwzZB03YUoQOww.png" /><figcaption>Code Inspector Audits</figcaption></figure><ul><li>To visualize the details of the result, you can use the <a href="https://frontend.code-inspector.com/">frontend</a>, <a href="https://github.com/codeinspectorio/citool">command-line client</a>, or use directly <a href="https://doc.code-inspector.com/docs/api/">API</a>.</li><li>Code Inspector’s frontend dashboard for the project.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GJV68zGeRdgidxHH6i6D3w.png" /><figcaption>Code Inspector Frontend — Dashboard</figcaption></figure><ul><li>Code Inspector’s frontend analysis for the project.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hdU2VbNa6SiRVYLwmvgT-Q.png" /><figcaption>Code Inspector Frontend — Analysis</figcaption></figure><ul><li>Once we click on the “<strong><em>Violations</em></strong>” showing on a red color chip, we can see the violations on the code.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*H3MhD9zgcDyG-rbjxNB2hg.png" /><figcaption>Code Inspector Frontend — Violations</figcaption></figure><h3>Demo Project</h3><p>GitHub Repo: <a href="https://github.com/osandadeshan/code-quality-demo">https://github.com/osandadeshan/code-quality-demo</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f8d370888cc4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/setting-up-automated-code-reviews-f8d370888cc4">Setting up Automated Code Reviews</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Boilerplate projects for Junior Automation Engineers]]></title>
            <link>https://medium.com/automationmaster/boilerplate-projects-for-junior-automation-engineers-5fee9af98151?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/5fee9af98151</guid>
            <category><![CDATA[api-automation]]></category>
            <category><![CDATA[mobile-automation]]></category>
            <category><![CDATA[web-automation]]></category>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[boilerplate]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Thu, 07 Oct 2021 03:48:24 GMT</pubDate>
            <atom:updated>2021-10-07T03:47:31.949Z</atom:updated>
            <content:encoded><![CDATA[<h4>Automation Boilerplate Projects</h4><h3>What is a Boilerplate?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*URd_j_qm0CBxWMne.jpg" /></figure><p>In computer programming, <strong>boilerplate code</strong> or just <strong>boilerplate</strong> are sections of code that are repeated in multiple places with little to no variation. When using languages that are considered <strong><em>verbose</em></strong>, the programmer must write a lot of code to accomplish only minor functionality. Such code is called <strong><em>boilerplate</em></strong>.</p><p>The need for boilerplate can be reduced through high-level mechanisms such as <a href="https://en.wikipedia.org/wiki/Metaprogramming">metaprogramming</a> (which has the computer automatically write the needed boilerplate code or insert it at <a href="https://en.wikipedia.org/wiki/Compile_time">compile time</a>), <a href="https://en.wikipedia.org/wiki/Convention_over_configuration">convention over configuration</a> (which provides good default values, reducing the need to specify program details in every project) and <a href="https://en.wikipedia.org/wiki/Model-driven_engineering">model-driven engineering</a> (which uses models and model-to-code generators, eliminating the need for manual boilerplate code).</p><h3>Web UI Automation Boilerplates</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/0*wySHDz05e_dC8XxS.png" /></figure><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c56a416156db2092ca0d39de1d3fe223/href">https://medium.com/media/c56a416156db2092ca0d39de1d3fe223/href</a></iframe><h3>Mobile Automation Boilerplates</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*PZGxYMSb8cXpAGpl.png" /></figure><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3ce69976dcb947ad879d78ed4ce1da4b/href">https://medium.com/media/3ce69976dcb947ad879d78ed4ce1da4b/href</a></iframe><h3>API Automation Boilerplates</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*RMsAdxslxOxIWqrv.jpg" /></figure><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3e95e9b6b4d7eb4c59d654d9553d4fdb/href">https://medium.com/media/3e95e9b6b4d7eb4c59d654d9553d4fdb/href</a></iframe><h3>Reporting Boilerplates</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/0*hSYdrRb9oTR9EY9R.png" /></figure><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/452e8a87ae5d31e5d4b6ab4f94cbaff2/href">https://medium.com/media/452e8a87ae5d31e5d4b6ab4f94cbaff2/href</a></iframe><p><strong>Happy Automation !!!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5fee9af98151" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/boilerplate-projects-for-junior-automation-engineers-5fee9af98151">Boilerplate projects for Junior Automation Engineers</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Be a Leader Everyone Loves to Work With]]></title>
            <link>https://medium.com/automationmaster/how-to-be-a-good-leader-to-your-team-b84c1d6f1185?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/b84c1d6f1185</guid>
            <category><![CDATA[leadership-development]]></category>
            <category><![CDATA[leadership]]></category>
            <category><![CDATA[methodology]]></category>
            <category><![CDATA[leadership-skills]]></category>
            <category><![CDATA[leadership-coaching]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Sat, 19 Jun 2021 13:21:46 GMT</pubDate>
            <atom:updated>2021-06-21T12:41:53.733Z</atom:updated>
            <content:encoded><![CDATA[<h4>Leading the team towards the success</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*oVuhM3iCA8Rw_XdQ.jpg" /><figcaption>Leadership</figcaption></figure><p><strong>“The best leaders are the best learners. You have to believe that you (and others) can learn to lead, and that you can become a better leader tomorrow than you are today. Leaders are constant improvement fanatics, and learning is the master skill of leadership.” </strong>– Jim Kouzes, Co-author of The Leadership Challenge and The Student Leadership Challenge</p><p>If you think about an good organization, everyone regardless of their titles or positions is encouraged to <strong>act like a leader.</strong></p><p>Here, I am explaining about <strong>The Five Practices of Exemplary Leadership </strong>to improve your leadership skills.</p><h3>1. Model the Way</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/902/0*AyDZemWvfhBqXA40.jpg" /><figcaption>Model the Way</figcaption></figure><ul><li>Leaders should establish how the team members should be recognized or treated and goals pursued.</li><li>Leaders should be examples for the team members.</li><li>If you think that some task cannot be completed within a given period of time, then you should not give that task to a team member. If you did that, it would be a bad model for the team members and your image will be bad for them.</li></ul><h4>Important points for modeling the way</h4><ol><li>Titles are granted, but your behavior earns you respect.</li><li>Words and deeds must be consistent.</li><li>You must affirm the shared values of the group.</li></ol><h3>2. Inspire a Shared Vision</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/0*76Pwj2e4FLDtC7Vg.gif" /><figcaption>Inspire a Shared Vision</figcaption></figure><p>Leaders envision the future, see what the organization can become, and get others to see those possibilities.</p><h4>Important points for modeling the way</h4><ol><li>You have to inspire commitment, not command it.</li><li>Visions seen only by leaders are insufficient to create change in a company.</li></ol><h3>3. Challenge the Process</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*RgaMUHZrNrRMWckv.png" /><figcaption>Challenge the Process</figcaption></figure><p>Look for innovative ways to improve organizations and accept disappointments as learning opportunities.</p><h4>Important points for modeling the way</h4><ol><li>Leaders venture out; they don’t sit idly by.</li><li>Create a climate where good ideas are supported.</li></ol><h3>4. Enable Others to Act</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/0*nLLLRqy8sxq7Fwz1.jpg" /><figcaption>Enable Others to Act</figcaption></figure><p>Leaders foster collaboration and build spirited teams by actively involving others.</p><h4>Important points for modeling the way</h4><ol><li>Giving power to mentees grants them ownership, making them more capable.</li><li>Seeking opinions of others builds up their capabilities.</li></ol><h3>5. Encourage the Heart</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*XY9Op6X_04XrmOpy.jpg" /><figcaption>Encourage the Heart</figcaption></figure><p>To keep hope and determination alive, leaders recognize contributions that individuals make.</p><h4>Important points for modeling the way</h4><ol><li>Create a culture of celebrating values and victories.</li><li>Celebrations and rituals can carry a group through tough times.</li></ol><blockquote>“<strong>A leader is a dealer in hope.</strong>” <br>Napoleon Bonaparte</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b84c1d6f1185" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/how-to-be-a-good-leader-to-your-team-b84c1d6f1185">How to Be a Leader Everyone Loves to Work With</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RESTful API Development using Express.js]]></title>
            <link>https://medium.com/automationmaster/restful-api-development-using-express-js-58dc1261c7ba?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/58dc1261c7ba</guid>
            <category><![CDATA[api]]></category>
            <category><![CDATA[expressjs]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[rest-api]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Sun, 16 May 2021 18:05:19 GMT</pubDate>
            <atom:updated>2021-05-16T15:50:40.193Z</atom:updated>
            <content:encoded><![CDATA[<h4>API CRUD operation development using Express.js</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_5LmRsI5lOMi2NVLUB8lbQ.png" /><figcaption>Express.js</figcaption></figure><h3>Contents</h3><ol><li>What is REST?</li><li>What is Express.js?</li><li>Express.js Architecture</li><li>Advantages of Express.js</li><li>Tools/Technologies</li><li>Pre-requisites</li><li>Getting started</li><li>Setting up the server</li><li>Setting up the schema</li><li>Setting up the routes</li><li>Setting up the controller</li><li>Completing the server</li><li>Adding a middleware</li><li>Testing via Postman</li><li>Github source</li></ol><h3>1. What is REST?</h3><p>REST — Representational State Transfer</p><p><strong>REST</strong>, or Representational State Transfer, is an architectural style for providing standards between computer systems on the web, making it easier for systems to communicate with each other. REST-compliant systems, often called RESTful systems, are characterized by how they are stateless and separate the concerns of client and server. We will go into what these terms mean and why they are beneficial characteristics for services on the Web.</p><p>The REST architectural style describes six constraints that were originally communicated by Roy Fielding in his doctoral dissertation and defines the basis of RESTful-style as:</p><p>1. Uniform Interface</p><p>2. Stateless</p><p>3. Cacheable</p><p>4. Client-Server</p><p>5. Layered System</p><p>6. Code on Demand (optional)</p><p>RESTful services use HTTP requests to perform <strong>CRUD (Create, Read, Update, Delete) operations.</strong></p><h3>2. What is Express.js?</h3><p>Express is a fast, assertive, essential and moderate web framework of Node.js. You can assume express as a layer built on the top of the Node.js that helps manage a server and routes. It provides a robust set of features to develop web and mobile applications.</p><p>Let’s see some of the core features of Express framework:</p><p>· It can be used to design single-page, multi-page and hybrid web applications.</p><p>· It allows to setup middleware to respond to HTTP Requests.</p><p>· It defines a routing table which is used to perform different actions based on HTTP method and URL.</p><p>· It allows to dynamically render HTML Pages based on passing arguments to templates.</p><h3>3. Express.js Architecture</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*opB5yQEB-0cEReKU4aNWCQ.jpeg" /><figcaption>Express.js architecture</figcaption></figure><h3>4. Advantages of Express.js</h3><p>· Ultra-fast I/O</p><p>· Asynchronous and single threaded</p><p>· MVC like structure</p><p>· Robust API makes routing easy</p><h3>5. Tools/Technologies</h3><p>· Node.js</p><p>· MongoDB</p><p>· Text editor (Notepad++, Sublime, Atom, VSCode)</p><p>· Postman</p><h3>6. Pre-requisites</h3><p><strong>Node.js</strong> and <strong>MongoDB </strong>should be installed. If you haven’t installed them, you can install from the below URLs.</p><p><a href="https://nodejs.org/en/download/package-manager/">Node.js</a></p><p><a href="https://docs.mongodb.com/manual/installation/">MongoDB</a></p><h3>7. Getting started</h3><p>In this tutorial, I will guide you to develop RESTful APIs for CRUD operations using Mongoose and Express.js. Basically, you will be able to develop routes for <strong>GET</strong>, <strong>POST</strong>, <strong>PUT</strong> and <strong>DELETE</strong> HTTP methods.</p><p>Open your terminal and kindly follow the following steps.</p><ol><li>Create a folder for your project. Here I will name it as <strong>“expressjs-restful-apis-demo”</strong></li></ol><pre><strong><em>mkdir expressjs-restful-apis-demo</em></strong></pre><p><em>2.</em> Navigate to that folder</p><pre><strong><em>cd expressjs-restful-apis-demo</em></strong></pre><p>3. Create a <strong>“package.json”</strong> file — This package.json file provides the information of the project and its dependencies</p><pre><strong><em>npm init</em></strong></pre><p>4. Press Enter to complete the creation of <strong>package.json</strong></p><p>5. Add the below dependencies to your <strong>package.json</strong></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/772cbea384099629244b3d4007983e48/href">https://medium.com/media/772cbea384099629244b3d4007983e48/href</a></iframe><p>6. Update your <strong>package.json</strong> with the following</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7119eb173d49ed1f05929f2d2ac1b0ac/href">https://medium.com/media/7119eb173d49ed1f05929f2d2ac1b0ac/href</a></iframe><p>7. Create a file named “<strong>server.js”</strong> — In this file, we will be writing the protocols to create our server</p><p>8. Create a folder called <strong>“api”</strong></p><pre><strong><em>mkdir api</em></strong></pre><p>Inside this folder called <strong>api</strong>, create three separate folders called “<strong>models”</strong>, “<strong>routes”</strong>, and “<strong>controllers”</strong> by executing</p><pre><strong><em>mkdir api/controllers api/models api/routes</em></strong></pre><p>9. Create “<strong>tasksController.js”</strong> in the <strong>api/controllers</strong> folder, <strong>“tasksRoutes.js”</strong> in the <strong>api/routes</strong> folder, and <strong>“tasksModel.js”</strong> in the <strong>api/models</strong> folder</p><p>10. Our folder structure should look like this now</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/816/1*qVM_qdDNJ-97DMkZzj1e0g.png" /><figcaption>Package structure of Express.js</figcaption></figure><h3>8. Setting up the server</h3><p>1. Let’s install <strong>express </strong>and <strong>nodemon</strong>, express will be used to create the server while nodemon will help us to keep track of changes to our application by watching changed files and automatically restart the server</p><pre><strong><em>npm install — save-dev nodemon</em></strong></pre><pre><strong><em>npm install express –save</em></strong></pre><p>2. Then we can install <strong>express-healthcheck</strong>, which can be used to check the health of the server</p><pre><strong><em>npm install express-healthcheck</em></strong></pre><p>3. Open the <strong>server.js</strong> file and type/copy the code below into it</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b55843f9fc3146b573d01798f47cf467/href">https://medium.com/media/b55843f9fc3146b573d01798f47cf467/href</a></iframe><p>4. On your terminal, execute</p><pre><strong><em>npm start</em></strong></pre><p>This will start the server and then you will see</p><p><strong>RESTful API demo server started on: 3000</strong></p><h3>9. Setting up the schema</h3><p>First, we need to install mongoose. Mongoose is what we will use to interact with a MongoDB(Database) instance.</p><pre><strong><em>npm install mongoose –save</em></strong></pre><p>After installation, open the<strong> tasksModel.js</strong> file in your <strong>api/models </strong>folder and type the following code into the file and save.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/feed2c64b5b9a1bad5e122d2510e4a7b/href">https://medium.com/media/feed2c64b5b9a1bad5e122d2510e4a7b/href</a></iframe><p>From the code above, we are defining the set of attributes for our MongoDB collection. Simply this is the payload we need to use to create a task from the service.</p><p>As you can see, it the task collection(table) will contain a name: a string, a category: a string and the date it was created. It also contains task status which we have defined as pending — a default value for every task created.</p><h3>10. Setting up the routes</h3><p>Routing refers to determining how an application responds to a client request for a specific endpoint, which is a URI (or path) and a specific HTTP request method (<strong>GET</strong>, <strong>POST</strong>, and so on).</p><p>Each of our routes has different route handler functions, which are executed when the route is matched.</p><p>Below we have defined two basic routes(‘<strong>/tasks</strong>’, and ‘<strong>/tasks/taskId</strong>’) with different methods</p><p>‘<strong>/tasks</strong>’ has to methods(<strong>GET </strong>and <strong>POST</strong>), while ‘<strong>/tasks/taskId</strong>’ has <strong>GET</strong>, <strong>PUT </strong>and <strong>DELETE</strong>.</p><p>As you can see, we required the controller so each of the routes methods can call it’s respective handler function.</p><p>To do this, open the <strong>tasksRoutes.js</strong> file in the route folder and paste the code snippet below into.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a5e96c365e4c7ba12c195f574b6ba5a5/href">https://medium.com/media/a5e96c365e4c7ba12c195f574b6ba5a5/href</a></iframe><h3>11. Setting up the controller</h3><p>Open <strong>tasksController.js</strong> file with your text editor (VSCode, Sublime, Atom e.t.c) and let’s deep dive into coding.</p><p>In this controller, we would be writing 5 different functions namely: <strong>getAllTasks</strong>, <strong>createTask</strong>, <strong>getTaskById</strong>, <strong>editTaskById</strong>, <strong>deleteTaskById</strong>. We will export each of the functions for us to use in our routes.</p><p>Each of these functions uses different mongoose methods such as <strong>find</strong>, <strong><em>findById</em></strong>, <strong><em>findOneAndUpdate</em></strong>, <strong><em>save</em></strong> and <strong><em>remove</em></strong>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bd9bd5bfa289faa70dba188d7abb9247/href">https://medium.com/media/bd9bd5bfa289faa70dba188d7abb9247/href</a></iframe><h3>12. Completing the server</h3><p>Earlie, we had a minimal code for our server to be up and running in the <strong>server.js</strong> file.</p><p>In this section we will be connecting our handlers(controllers), database, the created models, body parser and the created routes together.</p><p>Open the <strong>server.js</strong> file created a while ago and follow the following steps to put everything together.</p><p>Essentially, you will be replacing the code in your <strong>server.js</strong> with the code snippet from this section</p><p>1. Connect your database by adding a url to the mongoose instance connection</p><p>2. Load the created model — task</p><p>3. Install bodyParser and use bodyParser Parse incoming request bodies in a middleware before your handlers, available under the <strong>req.body</strong> property.</p><p>It exposes various factories to create middlewares. All middlewares will populate the <strong>req.bodyproperty</strong> with the parsed body, or an empty object ({}) if there was no body to parse (or an error was returned)</p><p>4. Register the created routes in the server</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1a317af6d8edfb80fd89af529096dbd4/href">https://medium.com/media/1a317af6d8edfb80fd89af529096dbd4/href</a></iframe><p>5. Start MongoDB server</p><p>Open your terminal and run</p><pre><strong><em>mongod</em></strong></pre><p>6. Start Node server</p><p>Open your terminal and run</p><pre><strong><em>npm start</em></strong></pre><h3>13. Adding a middleware</h3><p>Having done all these, what happens if we entered a wrong route? say you entered <strong>‘http://localhost:3000/task</strong>&#39;, It responds with a message “<strong>Cannot GET /task</strong>”. Let’s add express middleware which could be used to return more interactive messages.</p><p>Middlewares basically intercepts incoming HTTP request and as such you can use them to perform several operations ranging from authentication to validations etc.</p><p>To do this, open your <strong>server.js</strong> file and paste the code snippet into it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/53049c5e6fda3c4163e0791702fa68d1/href">https://medium.com/media/53049c5e6fda3c4163e0791702fa68d1/href</a></iframe><p>The snippet above helps to redirect and respond whenever a wrong route is entered on the site.</p><h3>14. Testing via Postman</h3><p>Now that everything is now connected, let’s test each of the routes and the respective methods.</p><p>Open your postman and type:</p><p>1. <a href="http://localhost:3000/tasks"><strong>http://localhost:3000/tasks</strong></a> in the enter request URL section</p><p>2. Change the HTTP method to <strong>POST </strong>and select raw radio button</p><p>3. Then choose <strong>JSON (application/json)</strong></p><p>4. Enter the body as follows</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3f7f0e13b77d63a0e4d98fe38b62b2e4/href">https://medium.com/media/3f7f0e13b77d63a0e4d98fe38b62b2e4/href</a></iframe><p>5. Click on Send button</p><p>6. It will give the response as <strong>201 (Created)</strong></p><p><strong>POST Request</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hfwW7lMHIln3UBAHMGwV5A.png" /><figcaption>POST request</figcaption></figure><p>7. You can try other HTTP methods such as <strong>GET</strong>, <strong>PUT </strong>and <strong>DELETE </strong>as well using <a href="https://www.getpostman.com/collections/d9646cc382d69aca3111">this postman collection</a></p><p><strong>GET Request</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qk9mzavj3BQiR71zNKFSBw.png" /><figcaption>GET request</figcaption></figure><p><strong>PUT Request</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OTXUZ_LAS7YFs9pmQNEa5w.png" /><figcaption>PUT request</figcaption></figure><p><strong>DELETE Request</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ht2Yz5tMzCCzhk756QA-ng.png" /><figcaption>DELETE request</figcaption></figure><p><strong>Note: </strong>Health route can be verified using <strong>GET </strong><a href="http://localhost:3000/health"><strong>http://localhost:3000/health</strong></a></p><p><strong>Health Request</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a_i_mlNGlL2OgvsdyGsmpw.png" /><figcaption>Health request</figcaption></figure><h3>15. Github source</h3><p><a href="https://github.com/osandadeshan/expressjs-restful-apis-demo.git">osandadeshan/expressjs-restful-apis-demo</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=58dc1261c7ba" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/restful-api-development-using-express-js-58dc1261c7ba">RESTful API Development using Express.js</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting Google OAuth Access Token using Google APIs]]></title>
            <link>https://medium.com/automationmaster/getting-google-oauth-access-token-using-google-apis-18b2ba11a11a?source=rss----3f3ec55f5af9---4</link>
            <guid isPermaLink="false">https://medium.com/p/18b2ba11a11a</guid>
            <category><![CDATA[google-api]]></category>
            <category><![CDATA[access-token]]></category>
            <category><![CDATA[oauth]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[google-drive]]></category>
            <dc:creator><![CDATA[Osanda Deshan Nimalarathna]]></dc:creator>
            <pubDate>Sun, 16 May 2021 18:05:09 GMT</pubDate>
            <atom:updated>2021-05-16T16:15:52.838Z</atom:updated>
            <content:encoded><![CDATA[<h4>OAuth access token for Google Drive</h4><p>If you need to access your Google drive and read your contents through an API, you will need the Google OAuth access token associated with your google drive.</p><p>Here I have explained how to get that access token using Google APIs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7jfNGwuC8qwFePsANzY9-Q.jpeg" /></figure><ol><li>Go to this <a href="https://console.developers.google.com/projectcreate?previousPage=%2Fflows%2Fenableapi%3Fapiid%3Dappsactivity%26credential%3Dclient_key%26pli%3D1&amp;angularJsUrl=%2Fprojectcreate%3FpreviousPage%3D%252Fflows%252Fenableapi%253Fapiid%253Dappsactivity%2526credential%253Dclient_key%2526pli%253D1&amp;project=&amp;folder=&amp;organizationId=0">link</a>.</li><li>Login to your Google account.</li><li>Create a project using a project name and click on <strong><em>“CREATE”</em></strong> button.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/613/1*cOYNWGKyq_9bOyx-1K5Xpw.png" /><figcaption>Creating the project on your google account</figcaption></figure><p>4. Select the project you have created.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/586/1*ujNhgf0m-6X_dYzUxeEg7A.png" /><figcaption>Selecting the created project</figcaption></figure><p>5. Click on <strong><em>“Open”</em></strong> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8nMetxpy_Q3gR6S1FVGwOg.png" /><figcaption>Open the created project</figcaption></figure><p>6. To enable Google Drive API, click on Google Drive API and then click on <strong><em>“ENABLE”</em></strong> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tDK65Ga4QBPjI7a_wIS98w.png" /><figcaption>Enabling Google Drive API</figcaption></figure><p>7. Click on <em>“</em><strong><em>Create Credentials</em></strong><em>”</em> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*os8h0kwOmP7Cinl7-3TJJA.png" /><figcaption>Creating credentials</figcaption></figure><p>8. Choose <strong>“<em>Web browser (Javascript)”</em></strong> for the “<strong><em>Where will you be calling the API from?”</em></strong> dropdown.</p><p>9. Select <strong><em>“ User data” </em></strong>for the “<strong><em>What data will you be accessing?”.</em></strong></p><p>10. Click on <strong><em>“What credentials do I need?”</em></strong> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*b7AwB5raTGXn4y9Di_rXng.png" /><figcaption>Creating credentials for the project</figcaption></figure><p>11. Provide a redirect URL for the <strong><em>“Authorized redirect URIs”.<br></em>Ex: </strong><a href="https://www.maxsoftlk.com"><strong>https://www.maxsoftlk.com</strong></a></p><p>12. Click on <strong><em>“Create OAuth client ID”</em></strong> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/981/1*OE-rJUTyGuf20joeJIAqgQ.png" /><figcaption>Creating OAuth client ID</figcaption></figure><p>13. Provide a <strong>“<em>product name”.</em><br>Ex: Test1</strong></p><p>14. Click on <strong><em>“Continue”</em></strong> button.</p><p>15. Click on <strong><em>“Download”</em></strong> button to download this credential information in JSON format.</p><p>16. Click on <strong><em>“Done”</em></strong> button.</p><p>17. Change this URL according to the parameters given by you in the above steps.</p><p><a href="https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/drive&amp;response_type=code&amp;access_type=offline&amp;redirect_uri=https://questionaggrigator.prd-prsn.com&amp;client_id=1072307075112-fposi9hgskm5uud3239f6gbfpkf6dfpk.apps.googleusercontent.com">https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/drive&amp;response_type=code&amp;access_type=offline&amp;redirect_uri=<strong>redirect_url</strong>&amp;client_id=<strong>y</strong></a><strong>our_client_id</strong></p><p><strong>Note: Your Client ID can be found from the file you have downloaded in step 15.</strong></p><p>Ex: <a href="https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/drive&amp;response_type=code&amp;access_type=offline&amp;redirect_uri=https://questionaggrigator.prd-prsn.com&amp;client_id=1072307075112-fposi9hgskm5uud3239f6gbfpkf6dfpk.apps.googleusercontent.com">https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/drive&amp;response_type=code&amp;access_type=offline&amp;redirect_uri=</a><a href="https://www.maxsoftlk.com"><strong>https://www.maxsoftlk.com</strong></a><a href="https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/drive&amp;response_type=code&amp;access_type=offline&amp;redirect_uri=https://questionaggrigator.prd-prsn.com&amp;client_id=1072307075112-fposi9hgskm5uud3239f6gbfpkf6dfpk.apps.googleusercontent.com">&amp;client_id=<strong>1072307075112-fposi9hsdfghjkd3239f6gbfpkfasfpk.apps.googleusercontent.com</strong></a></p><p>18. Copy the above URL and paste it in an incognito mode of your browser.</p><p>19. Select the Google account which you have logged in previously.</p><p>20. If you are come-up with this screen, click on <strong><em>Advanced</em></strong> link and then click on <strong><em>go with unsafe mode</em></strong> link.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*touuyhh5mPFdSWgdIuxXqw.png" /><figcaption>Screen 1</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/759/1*49VtLrwu87jbWXLBl7UlKw.png" /><figcaption>Screen 2</figcaption></figure><p>21. Click on <strong><em>“Allow”</em></strong> button.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/560/1*y33QNhZiwUp3BUZTRaPApg.png" /><figcaption>Allowing the redirect uri to access your google drive</figcaption></figure><p>22. Copy the URL on the address bar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cGJ-NUoKZlp1yHmW5LwxSw.png" /><figcaption>Copying the url in the address bar of the browser</figcaption></figure><p>23. Paste it in the note pad and copy the <strong><em>code.</em></strong></p><p>Ex: <a href="https://www.maxsoftlk.com/?code=4/WwBf2eVqGtlxmBbPMGymGi18OJCj3JFMhXouWO1jrdnyWGqH4pcNbqIfxWQnTWA_AG-sX3nRqEUn_Z2x0WGHhQg&amp;scope=https://www.googleapis.com/auth/drive#">https://www.maxsoftlk.com/?code=</a><a href="https://www.maxsoftlk.com/?code=4/WwBf2eVqGtlxmBbPMGymGi18OJCj3JFMhXouWO1jrdnyWGqH4pcNbqIfxWQnTWA_AG-sX3nRqEUn_Z2x0WGHhQg&amp;scope=https://www.googleapis.com/auth/drive#"><strong>4/WwBf2eVqGtlxmWEGHGymGi18OJCj3JFMhXouWO1jrdnyWGqH4pcNoprdcFGwTWA_AG-sX3nRqEUn_Z2x0WGHhQg</strong></a><a href="https://www.maxsoftlk.com/?code=4/WwBf2eVqGtlxmBbPMGymGi18OJCj3JFMhXouWO1jrdnyWGqH4pcNbqIfxWQnTWA_AG-sX3nRqEUn_Z2x0WGHhQg&amp;scope=https://www.googleapis.com/auth/drive#">&amp;scope=https://www.googleapis.com/auth/drive#</a></p><p>The code is: <a href="https://www.maxsoftlk.com/?code=4/WwBf2eVqGtlxmBbPMGymGi18OJCj3JFMhXouWO1jrdnyWGqH4pcNbqIfxWQnTWA_AG-sX3nRqEUn_Z2x0WGHhQg&amp;scope=https://www.googleapis.com/auth/drive#"><strong>4/WwBf2eVqGtlxmWEGHGymGi18OJCj3JFMhXouWO1jrdnyWGqH4pcNoprdcFGwTWA_AG-sX3nRqEUn_Z2x0WGHhQg</strong></a></p><p>24. Now you can use these details to invoke the Google API and get the Google OAuth Access token for the Google Drive.</p><p>25. Open the <strong>Postman</strong></p><p>26. Create a new request with these details.<br>HTTP Method: <strong>POST<br></strong>Body Type: <strong>form-data</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/368/1*3PXLh48SFc5HxwCGx1oAsw.png" /><figcaption>Key-Value pairs for the form-data request</figcaption></figure><p>27. Click on <strong><em>“Send”</em></strong> button and get the access token.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Xy9cr3j5JtTAncOKbaauCg.png" /><figcaption>Invoking the request through postman</figcaption></figure><p><strong>Note: </strong><a href="https://www.getpostman.com/collections/ec9071ec7bb0773cf424"><strong>Postman Collection</strong></a></p><p><strong>Happy Programming !!!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=18b2ba11a11a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/automationmaster/getting-google-oauth-access-token-using-google-apis-18b2ba11a11a">Getting Google OAuth Access Token using Google APIs</a> was originally published in <a href="https://medium.com/automationmaster">Test Automation Master</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>