<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shoeib Shargo</title>
    <description>The latest articles on DEV Community by Shoeib Shargo (@shoeib_shargo).</description>
    <link>https://dev.to/shoeib_shargo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F837036%2Fae1b07bc-6542-4d77-9e39-507d87046d05.jpeg</url>
      <title>DEV Community: Shoeib Shargo</title>
      <link>https://dev.to/shoeib_shargo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shoeib_shargo"/>
    <language>en</language>
    <item>
      <title>🔐 Understanding Web Application Firewall (WAF) for Software QA Engineers</title>
      <dc:creator>Shoeib Shargo</dc:creator>
      <pubDate>Mon, 21 Apr 2025 15:00:06 +0000</pubDate>
      <link>https://dev.to/shoeib_shargo/understanding-web-application-firewall-waf-for-qa-engineers-1826</link>
      <guid>https://dev.to/shoeib_shargo/understanding-web-application-firewall-waf-for-qa-engineers-1826</guid>
      <description>&lt;p&gt;As QA engineers, we’re often caught in the middle of development and security teams — balancing feature delivery with risk mitigation. But when it comes to Web Application Firewalls (WAFs), many of us have only a surface-level understanding, often leaving testing gaps and friction with DevSecOps.&lt;/p&gt;

&lt;p&gt;This article distills the key takeaways from a session I recently delivered internally, breaking down what WAFs are, how they work, and how QA can actively contribute to WAF integration and testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Is a WAF — And Why Should QA Care?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Web Application Firewall protects web applications from common threats like SQL Injection (SQLi), Cross-Site Scripting (XSS), and Broken Authentication. Unlike traditional firewalls that operate on lower network layers, a WAF works at Layer 7 (Application Layer) and analyzes HTTP/HTTPS traffic.&lt;/p&gt;

&lt;p&gt;🔁 It acts as a reverse proxy — inspecting, filtering, and deciding whether to forward traffic to your web servers.&lt;/p&gt;

&lt;p&gt;Think of it as a smart bouncer that not only checks IDs but also reads your body language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forward Proxy vs. Reverse Proxy — Quick Refresher&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;In the above diagram, the client’s real IP (192.168.1.10) is hidden. Server sees the proxy’s IP (203.0.113.5) instead of the client’s. It’s used for anonymity, security, and content filtering (e.g., VPN, corporate proxies).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zicomxs5s01ueibpk7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zicomxs5s01ueibpk7f.png" alt="Image description" width="800" height="170"&gt;&lt;/a&gt;&lt;br&gt;
Example of Reverse Proxy&lt;br&gt;
In the above diagram, server’s real IP (10.0.0.1) is hidden. Client only sees the reverse proxy’s IP (203.0.113.5). It’s used for security, load balancing, and caching (e.g., Nginx, Cloudflare, AWS ALB).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Differences&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Forward Proxy: Client-side (client requests go through the proxy before reaching the internet).&lt;br&gt;
Reverse Proxy: Server-side (client requests are directed to the proxy, which forwards them to backend servers).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Purpose of Web Application Firewall (WAF)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protects applications from common web threats like SQL Injection, Cross-Site Scripting (XSS), and Broken Authentication.&lt;/li&gt;
&lt;li&gt;Works at Layer 7 (Application Layer) of the OSI Model.&lt;/li&gt;
&lt;li&gt;Acts as a reverse proxy, shielding backend servers from direct exposure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How Does a WAF Work?&lt;/strong&gt;&lt;br&gt;
Traffic Inspection&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Looks into request headers, bodies, cookies, and URLs&lt;/li&gt;
&lt;li&gt;Detects anomalies and known malicious patterns&lt;/li&gt;
&lt;li&gt;Rule-Based Decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Signature-based (blocklists)&lt;br&gt;
Behavioral-based (AI/ML models for zero-day threats)&lt;br&gt;
Response Handling&lt;/p&gt;

&lt;p&gt;Blocks, challenges (CAPTCHA), or logs the request&lt;br&gt;
Sends alerts for attack attempts&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WAF in Action: Threat Examples&lt;/strong&gt;&lt;br&gt;
🛑 SQL Injection (SQLi)&lt;br&gt;
Payload:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /search?query=' OR 1=1 -- HTTP/1.1&lt;/code&gt;&lt;br&gt;
The WAF inspects the query string, recognizes the ' OR 1=1 injection pattern, and blocks it before it hits your database.&lt;/p&gt;

&lt;p&gt;⚠️ Cross-Site Scripting (XSS)&lt;br&gt;
Payload:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /profile?name=&amp;lt;script&amp;gt;alert('XSS')&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;br&gt;
WAFs catch and block scripts embedded in inputs that could steal sessions or deface content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where QA Engineers Come In&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Functional Testing — Preventing False Positives&lt;/strong&gt;&lt;br&gt;
Ensure the WAF does not break valid user actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Form submissions with special characters (&amp;lt;, {}, etc.)&lt;/li&gt;
&lt;li&gt;OAuth or SSO login flows&lt;/li&gt;
&lt;li&gt;File uploads, multi-step forms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Security Testing — Catching False Negatives&lt;/strong&gt;&lt;br&gt;
Ensure the WAF does block real threats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simulate XSS and SQLi attacks&lt;/li&gt;
&lt;li&gt;Test API rate limiting&lt;/li&gt;
&lt;li&gt;Verify payload filters for headers and cookies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Performance Testing — Is the WAF Slowing Us Down?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare API response times before/after WAF&lt;/li&gt;
&lt;li&gt;Run load tests with concurrency and observe latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Regression Testing — After WAF Deployment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run automated suites to catch functionality breakage&lt;/li&gt;
&lt;li&gt;Validate all 3rd-party service integrations and APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
A WAF isn’t just a security checkbox — it affects the entire request lifecycle. As QA engineers, it’s on us to ensure it’s properly configured, doesn’t block real users, and effectively filters real threats. And if we want secure applications, we can’t leave that responsibility to just the security team — QA has a huge role to play.&lt;/p&gt;

&lt;p&gt;Let me know what kind of WAF testing you’re doing, or drop a comment if you’ve seen false positives break a release. Follow for more QA + DevSecOps insights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/shoeib-mahmud-shargo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/TheSRK/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sdet</category>
      <category>waf</category>
      <category>testing</category>
      <category>securitytesting</category>
    </item>
    <item>
      <title>Tackling Complex Microservice Issues: Insights for QA Engineers</title>
      <dc:creator>Shoeib Shargo</dc:creator>
      <pubDate>Sat, 19 Oct 2024 15:37:13 +0000</pubDate>
      <link>https://dev.to/shoeib_shargo/tackling-complex-software-issues-insights-for-qa-engineers-503g</link>
      <guid>https://dev.to/shoeib_shargo/tackling-complex-software-issues-insights-for-qa-engineers-503g</guid>
      <description>&lt;p&gt;At its core, software testing is about validating the data flow from point A to point B, ensuring that information moves seamlessly and accurately through every component of a system. In the complex landscape of modern software development, this fundamental principle becomes increasingly challenging to uphold. As applications grow in complexity—embracing asynchronous operations, microservices architectures, third-party integrations, and AI-driven features—the pathways that data takes become more intricate, and the potential for issues escalates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the CAP Theorem and Database ACID Compliance
&lt;/h2&gt;

&lt;p&gt;Before diving into specific issues, it's essential to grasp foundational concepts like the CAP theorem and ACID compliance of databases. The CAP theorem states that in any distributed data system, you can only guarantee two out of three properties: Consistency (C), Availability (A), and Partition Tolerance (P). Understanding which properties your system prioritizes is crucial for designing appropriate tests, especially in microservices architectures where services may prioritize different aspects.&lt;/p&gt;

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

&lt;p&gt;Additionally, knowing whether the databases used are ACID-compliant (Atomicity, Consistency, Isolation, Durability) is vital for certain parts of your application. However, not all databases need to be ACID-compliant. For example, databases like Redis and OpenSearch are not fully ACID-compliant, but they serve critical roles in caching, real-time analytics, and search functionalities where high performance and scalability are required.&lt;/p&gt;

&lt;p&gt;As QA engineers, it's important to understand the characteristics of these non-ACID-compliant databases and how they affect data consistency and reliability. We need to design test strategies that account for these trade-offs. This includes testing for eventual consistency, handling data synchronization issues, and ensuring that the system behaves predictably under various conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Race Condition and Cache Inconsistency Issues
&lt;/h2&gt;

&lt;p&gt;Modern applications often rely on asynchronous operations, particularly in microservices architectures. While this design enhances scalability and performance, it introduces challenges in ensuring that dependent operations occur in the correct sequence. Race conditions can occur when multiple operations attempt to access or modify shared data simultaneously without proper synchronization, leading to unpredictable outcomes. Additionally, caching mechanisms, while improving performance, can introduce data consistency issues across distributed systems if not managed carefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Asynchronous Operations: The asynchronous nature of microservices means services operate independently, and communication between them is often event-driven. Ensuring that events are processed in the correct order without causing delays or bottlenecks requires careful design.&lt;br&gt;
Caching Mechanisms: Implementing distributed caching systems like Redis, can improve response times but keeping cached data synchronized across multiple instances or services adds complexity, particularly when data changes frequently.&lt;br&gt;
Race Conditions: When two or more operations compete to access or modify shared data, the lack of proper synchronization can lead to unpredictable outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;QA Thoughts and Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Comprehensive Test Scenarios: Develop test cases that cover asynchronous operations and potential timing issues. Simulate scenarios where operations overlap to detect race conditions.&lt;br&gt;
Eventual Consistency Testing: Validate how the system behaves when data is not immediately consistent. Ensure the application can handle slight delays in data propagation without adverse effects.&lt;br&gt;
Monitoring and Logging: Request developers to implement detailed logging around the Service and data updates to trace the sequence of events. This aids in identifying the exact point of failure when issues arise.&lt;br&gt;
Collaboration with Developers: Work closely with the development team to understand the data flow and dependencies. This collaboration can lead to more effective testing strategies that target potential weak points.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Issues from Integration with Third-Party APIs and Services
&lt;/h2&gt;

&lt;p&gt;Always remember users do not care whether the intended data is coming from Third-party API or not. Problems arising from integrations with external services, such as APIs not handling edge cases, rate limits causing failures, or changes in third-party platforms affecting functionality should be handled gracefully within the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;External Dependencies: Relying on third-party services introduces variables outside the team's control, including API changes, downtime, or inconsistent responses.&lt;br&gt;
Error Handling: Ensuring that the application gracefully handles errors from external services requires robust exception management and fallback mechanisms.&lt;br&gt;
Data Mapping and Validation: Differences in data formats or unexpected data from external services can lead to failures if not properly validated and sanitized.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;QA Thoughts and Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mock Services: Utilize mock servers to simulate third-party APIs, allowing testing of various responses, including errors and unexpected data.&lt;br&gt;
Contract Testing: Upon discussing with developers if possible implement contract tests to ensure that the integration points adhere to expected interfaces and data formats. Tools like Pact can be your friend here.&lt;br&gt;
Resilience Testing: Test how the application behaves under failure conditions, such as timeouts, rate limits, or invalid responses from external services.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Data Consistency and Synchronization Issue in Distributed Systems
&lt;/h2&gt;

&lt;p&gt;Data inconsistencies can emerge when there are synchronization challenges among various services or databases. Such issues may lead to unreliable data representations and hinder effective decision-making for the user. In distributed systems, ensuring data consistency across multiple services and databases is complex, especially when using a combination of ACID-compliant and non-ACID-compliant databases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Distributed Systems: In microservices architectures, data is often distributed across multiple services, each with its own database or storage mechanism. Coordinating data changes across these services is complex.&lt;br&gt;
Eventual Consistency Models: Some systems rely on eventual consistency, where data updates propagate over time rather than instantaneously, leading to temporary discrepancies. This is especially relevant when using non-ACID-compliant databases like OpenSearch.&lt;br&gt;
Concurrency Issues: Simultaneous operations on the same data can result in conflicts or overwriting of information.&lt;br&gt;
RDS Reader-Writer Delay: In relational databases like Amazon RDS using a read replica setup, there can be replication lag between the writer (primary) and the reader (replica). This delay means that data written to the primary database may not be immediately available on the replica database, leading to temporary inconsistencies in read operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;QA Thoughts and Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Data Consistency Tests: Design tests that verify data integrity across all modules and services after operations that modify data. This includes checking that data is consistent between primary and replica databases.&lt;br&gt;
Latency Simulation: Introduce artificial delays in data propagation during testing to observe how the system handles stale data.&lt;br&gt;
Conflict Resolution Strategies: Know the implemented logic from Developers to ensure that the Service has clear rules for resolving data conflicts, such as last-write-wins or merge strategies, and that these are tested thoroughly.&lt;br&gt;
End-to-End Testing: Conduct end-to-end tests that cover complete user flows across multiple services. This helps identify issues that may not be apparent when testing services in isolation.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Performance Bottleneck Issues and Resource Management
&lt;/h2&gt;

&lt;p&gt;Performance issues, such as slow searches, delays in execution, and timeouts during bulk operations, can significantly impact user satisfaction. They may lead to loss of revenue or customers if not addressed promptly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Resource Intensive Operations: Bulk data processing and complex queries can strain system resources, leading to slow performance or crashes. This is particularly relevant when using databases like OpenSearch for search functionalities.&lt;br&gt;
Inefficient Algorithms: Suboptimal code can significantly impact performance, especially as data volumes grow. Algorithms with poor time complexity can become bottlenecks.&lt;br&gt;
Scalability Limitations: Systems not designed with scalability in mind may perform well under low load but degrade rapidly as usage increases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;QA Thoughts and Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Performance Testing: Implement load testing on business-critical Services to evaluate how the system performs under various levels of stress and identify bottlenecks.&lt;br&gt;
Regression Analysis: Ensure that new code changes do not negatively impact performance by comparing metrics before and after changes. Incorporate performance benchmarks into the CI/CD pipeline.&lt;br&gt;
Scalability Planning: Invoke a discussion with DevOps and Developers to encourage architectural designs that support horizontal scaling and efficient resource utilization. Validate that the system can scale out to handle increased loads without degradation.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Issues in AI Enabled Features
&lt;/h2&gt;

&lt;p&gt;A newly added concern for QA is the behavior of AI-implemented features. Issues like irrelevant responses from AI or their inability to process certain inputs reveal problems that can cause user frustration and diminish trust in AI capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical Complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Input Validation: The system may lack robust input validation, allowing malformed data to be processed by the AI model.&lt;br&gt;
Model Limitations: The AI model may not be fine-tuned to handle or filter out irrelevant inputs effectively.&lt;br&gt;
Resource Constraints: The AI processing pipeline may suffer from timeouts due to insufficient computational resources allocated for processing complex inputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;QA Thoughts and Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI Testing Strategies: Develop specialized testing methods for AI features, including validation datasets that cover a wide range of inputs i.e. challenging or malicious data to evaluate how the AI system handles unexpected inputs.&lt;br&gt;
User Simulation: Use real-world data to simulate how users interact with AI features, identifying gaps in understanding or performance.&lt;br&gt;
Ensure Clear Error Handling: When the AI system cannot process an input or is unsure of a response, make sure it handles the situation gracefully.&lt;/p&gt;

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

&lt;p&gt;Addressing complex software issues requires a deep understanding of technical challenges and a proactive approach to testing. As QA engineers, we must develop comprehensive testing strategies that consider the intricacies of modern applications. By collaborating closely with development teams, implementing robust testing methodologies, and focusing on both technical and user experience aspects, we can help build more reliable, efficient, and user-friendly software systems.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>qa</category>
      <category>microservices</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Standard Naming Convention for UI Elements to Use with Selenium Locators</title>
      <dc:creator>Shoeib Shargo</dc:creator>
      <pubDate>Fri, 16 Sep 2022 21:42:57 +0000</pubDate>
      <link>https://dev.to/shoeib_shargo/standard-naming-convention-for-ui-elements-to-use-with-selenium-locators-3490</link>
      <guid>https://dev.to/shoeib_shargo/standard-naming-convention-for-ui-elements-to-use-with-selenium-locators-3490</guid>
      <description>&lt;p&gt;How many of you have put your Thinking Cap on before writing a variable name? Well, I certainly do more often than not. If you are working on a big project, sometimes it becomes a tedious job to correct variable naming conventions among the team members.&lt;/p&gt;

&lt;p&gt;In the case of UI test automation using Selenium or any other tools for that matter, it becomes way more complex to decide variable names for different web elements. Enough with the talks, let’s show you the chart that will help you distinguish between varieties of web elements and keep your sanity in check.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;+----------+----------------------------+--------+-----------------+&lt;br&gt;
| Category |      UI/Control type       | Prefix |     Example     |&lt;br&gt;
+----------+----------------------------+--------+-----------------+&lt;br&gt;
| Basic    | Button                     | btn    | btnExit         |&lt;br&gt;
| Basic    | Check box                  | chk    | chkReadOnly     |&lt;br&gt;
| Basic    | Combo box                  | cbo    | cboEnglish      |&lt;br&gt;
| Basic    | Common dialog              | dlg    | dlgFileOpen     |&lt;br&gt;
| Basic    | Date picker                | dtp    | dtpPublished    |&lt;br&gt;
| Basic    | Dropdown List / Select tag | ddl    | ddlCountry      |&lt;br&gt;
| Basic    | Form                       | frm    | frmEntry        |&lt;br&gt;
| Basic    | Frame                      | fra    | fraLanguage     |&lt;br&gt;
| Basic    | Image                      | img    | imgIcon         |&lt;br&gt;
| Basic    | Label                      | lbl    | lblHelpMessage  |&lt;br&gt;
| Basic    | Links/Anchor Tags          | lnk    | lnkForgotPwd    |&lt;br&gt;
| Basic    | List box                   | lst    | lstPolicyCodes  |&lt;br&gt;
| Basic    | Menu                       | mnu    | mnuFileOpen     |&lt;br&gt;
| Basic    | Radio button / group       | rdo    | rdoGender       |&lt;br&gt;
| Basic    | RichTextBox                | rtf    | rtfReport       |&lt;br&gt;
| Basic    | Table                      | tbl    | tblCustomer     |&lt;br&gt;
| Basic    | TabStrip                   | tab    | tabOptions      |&lt;br&gt;
| Basic    | Text Area                  | txa    | txaDescription  |&lt;br&gt;
| Basic    | Text box                   | txt    | txtLastName     |&lt;br&gt;
| Complex  | Chevron                    | chv    | chvProtocol     |&lt;br&gt;
| Complex  | Data grid                  | dgd    | dgdTitles       |&lt;br&gt;
| Complex  | Data list                  | dbl    | dblPublisher    |&lt;br&gt;
| Complex  | Directory list box         | dir    | dirSource       |&lt;br&gt;
| Complex  | Drive list box             | drv    | drvTarget       |&lt;br&gt;
| Complex  | File list box              | fil    | filSource       |&lt;br&gt;
| Complex  | Panel/Fieldset             | pnl    | pnlGroup        |&lt;br&gt;
| Complex  | ProgressBar                | prg    | prgLoadFile     |&lt;br&gt;
| Complex  | Slider                     | sld    | sldScale        |&lt;br&gt;
| Complex  | Spinner                    | spn    | spnPages        |&lt;br&gt;
| Complex  | StatusBar                  | sta    | staDateTime     |&lt;br&gt;
| Complex  | Timer                      | tmr    | tmrAlarm        |&lt;br&gt;
| Complex  | Toolbar                    | tlb    | tlbActions      |&lt;br&gt;
| Complex  | TreeView                   | tre    | treOrganization |&lt;br&gt;
+----------+----------------------------+--------+-----------------+&lt;/code&gt;&lt;br&gt;
In addition to the above naming convention, you can use &lt;code&gt;loc_&lt;/code&gt; as a prefix for locators. For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@FindBy(xpath = ".//*[@id='lastName']")&lt;br&gt;
public WebElement loc_txtLastName;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading so far. Hope it was helpful in some way. Nonetheless, I will keep writing such articles based on my experience and knowledge that may help you with the day-to-day tasks as a Test Automation Engineer. Stay connected!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>selenium</category>
      <category>qualityassurance</category>
      <category>testautomation</category>
    </item>
    <item>
      <title>Selenium Parallel testing using Java ThreadLocal and TestNG</title>
      <dc:creator>Shoeib Shargo</dc:creator>
      <pubDate>Fri, 10 Jun 2022 13:22:41 +0000</pubDate>
      <link>https://dev.to/shoeib_shargo/selenium-parallel-testing-using-java-threadlocal-and-testng-19cg</link>
      <guid>https://dev.to/shoeib_shargo/selenium-parallel-testing-using-java-threadlocal-and-testng-19cg</guid>
      <description>&lt;p&gt;No, writing just &lt;code&gt;parallel = “tests”&lt;/code&gt; &amp;amp; &lt;code&gt;thread-count =”2"&lt;/code&gt; in your testng.xml file won’t cut it. That is because Selenium WebDriver is not thread-safe by default. In a non-multithreaded environment, we keep our WebDriver reference static to make it thread-safe. But the problem occurs when we try to achieve parallel execution of our tests within the framework. Every thread you create to parallelize your tests tries to overwrite the WebDriver reference since there could be only one instance of static WebDriver reference.&lt;/p&gt;

&lt;p&gt;To overcome this problem, we can take help from the ThreadLocal class of Java. Wondering what is ThreadLocal? Simply put, it enables you to create a generic/ThreadLocal type of object which can only be read and written (via its get and set method) by the same thread, so if two threads are trying to read and write a ThreadLocal object concurrently, one thread would not see the modification of the ThreadLocal object done by the other thread, thus, making the thread-local object thread-safe.&lt;/p&gt;

&lt;p&gt;A typical thread-local object is made private static and its class provides initialValue, get, set, and remove methods. The following example shows how you can create a Generic type thread-local object&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//create a generic thread-local object
private static ThreadLocal&amp;lt;String&amp;gt; myThreadLocal = new ThreadLocal&amp;lt;String&amp;gt;();
//set thread-local value
myThreadLocal.set("Hello ThreadLocal");
//get thread-local value
String threadLocalValue = myThreadLocal.get();
//remove thread-local value for the current thread
myTheadlocal.remove();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have seen how ThreadLocal works, we can go ahead and implement it in our Selenium framework making the WebDriver session exclusive to each thread. Let’s create a BroweserManager class to manage the WebDriver instance alongside browsers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class BrowserManager {

   public static WebDriver doBrowserSetup(String browserName){
        WebDriver driver = null;
        if (browserName.equalsIgnoreCase("chrome")){
            //steup chrome browser
            WebDriverManager.chromedriver().setup();
            //Add options for --headed or --headless browserlaunch
            ChromeOptions chromeOptions = new ChromeOptions();
            chromeOptions.addArguments("-headed");

            //initialize driver for chrome
            driver = new ChromeDriver(chromeOptions);
            //maximize window
            driver.manage().window().maximize();
            //add implicit timeout
            driver.manage()
           .timeouts()
           .implicitlyWait(Duration.ofSeconds(30));
        }
   return driver;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, it’s time to create our BaseTest class where we will create a static WebDriver as a ThreadLocal and then set the driver to its object which will be accessible throughout the test with the help of our BrowserManager class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class BaseTest {
    protected static 
    ThreadLocal&amp;lt;WebDriver&amp;gt; threadLocalDriver = new ThreadLocal&amp;lt;&amp;gt;();
    @BeforeTest
    public void Setup(){
        WebDriver driver = BrowserManager.doBrowserSetup("chrome");
        //set driver
        threadLocalDriver.set(driver);
        System.out.println("Before Test Thread ID: "+Thread.currentThread().getId());
        //get URL
        getDriver().get("https://www.linkedin.com/");
    }
    //get thread-safe driver
    public static WebDriver getDriver(){
        return threadLocalDriver.get();
    }
    @AfterTest
    public void tearDown(){
        getDriver().quit();
        System.out.println("After Test Thread ID: "+Thread.currentThread().getId());
        threadLocalDriver.remove();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside @BeforeTest annotation we have called the doBrowserSetup() static method from BrowserManager class which returned a WebDriver reference with ChromeDriver initialized in it. Then we have set the returned WebDriver reference in our threadLocalDriver object. Now that our threadLocalDriver is set making the WebDriver reference thread-safe, we can use it across the test framework.&lt;/p&gt;

&lt;p&gt;Inside @AfterTest annotation we are just quitting the browser and then removing the current thread’s value (in this case, returned WebDriver reference from the doBrowserSetup() static method) from the threadLocalDriver object.&lt;/p&gt;

&lt;p&gt;Let’s complete the test by adding Test classes to it. We will run a login test on a demo site.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package Pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class Login {
    WebDriver driver;
    @FindBy(className = "login")
    WebElement linkLogin;
    @FindBy(id = "email")
    WebElement txtEmail;
    @FindBy(id = "passwd")
    WebElement txtPassword;
    @FindBy(id = "SubmitLogin")
    WebElement btnSignIn;
    @FindBy(xpath = "//span[contains(text(),'viva test')]")
    WebElement lblUserName;
    @FindBy(xpath = "//li[contains(text(),'Invalid email address.')]")
    WebElement lblInvalidEmail;
    @FindBy(xpath = "//li[contains(text(),'Authentication failed.')]")
    WebElement lblInvalidPassword;
    public Login(WebDriver driver){
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }
    //valid email and valid password
    public String doLogin(String email, String password){
        linkLogin.click();
        txtEmail.sendKeys(email);
        txtPassword.sendKeys(password);
        btnSignIn.click();
        return lblUserName.getText();
    }
    //Invalid email
    public String loginWithInvalidPassword(String email, String password){
        linkLogin.click();
        txtEmail.sendKeys(email);
        txtPassword.sendKeys(password);
        btnSignIn.click();
        return lblInvalidPassword.getText();
    }

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

&lt;/div&gt;



&lt;p&gt;Now create a test class based on the above class i.e. LoginTestRunner1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package TestRunners;
import Base.BaseTest;
import Pages.Login;
import Utils.BrowserManager;
import Utils.JsonReader;
import org.json.simple.parser.ParseException;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.IOException;
public class LoginTestRunner1 extends BaseTest {
    @Test
    public void loginTest() throws IOException, ParseException {
        getDriver().get("http://automationpractice.com/");
        Login login = new Login(getDriver());
        //valid email and valid password
        String user = login.doLogin("test_viva@test.com", "123456");
        Assert.assertEquals(user, "viva test");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create LoginTestRunner2 class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package TestRunners;
import Base.BaseTest;
import Pages.Login;
import Utils.BrowserManager;
import Utils.JsonReader;
import org.json.simple.parser.ParseException;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.IOException;
public class LoginTestRunner2 extends BaseTest {
    @Test
    public void loginWithInvalidEmailTest() throws IOException, ParseException {

        getDriver().get("http://automationpractice.com/");
        Login login = new Login(getDriver());
        //invalid email and valid pass
        String lblInvalidEmail = login.loginWithInvalidEmail("viva@te.com", "123456");
        Assert.assertEquals(lblInvalidEmail, "Authentication failed.");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let’s write parallel = “tests” &amp;amp; thread-count =”2" in our testng.xml file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"&amp;gt; 
&amp;lt;suite  verbose="1" name="E-commerce portal automation" parallel="tests" thread-count="2" &amp;gt;
    &amp;lt;test name="invalid email login" &amp;gt;
        &amp;lt;classes&amp;gt;
            &amp;lt;class name="TestRunners.LoginTestRunner2"/&amp;gt;
        &amp;lt;/classes&amp;gt;
    &amp;lt;/test&amp;gt;
    &amp;lt;test name="valid email login"&amp;gt;
        &amp;lt;classes&amp;gt;
            &amp;lt;class name="TestRunners.LoginTestRunner"/&amp;gt;
        &amp;lt;/classes&amp;gt;
    &amp;lt;/test&amp;gt;
&amp;lt;/suite&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the test. See the output below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before Test Thread ID: 15
Before Test Thread ID: 14
After Test Thread ID: 15
After Test Thread ID: 14
BUILD SUCCESSFUL in 48s
4 actionable tasks: 4 executed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. WebDriver instance has now become thread-safe, and as the output shows it is being used by the multiple threads concurrently thanks to ThreadLocal class.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>selenium</category>
      <category>java</category>
      <category>testng</category>
    </item>
    <item>
      <title>Don't just learn it, do it!</title>
      <dc:creator>Shoeib Shargo</dc:creator>
      <pubDate>Sat, 26 Mar 2022 07:27:29 +0000</pubDate>
      <link>https://dev.to/shoeib_shargo/dont-just-learn-it-do-it-5egk</link>
      <guid>https://dev.to/shoeib_shargo/dont-just-learn-it-do-it-5egk</guid>
      <description>&lt;p&gt;As they say, you can't learn to swim by just watching. I guess it stands true for building a Test automation framework from the scratch as well. No matter how many tutorials you watch online until you get your hands dirty with codes, you have not truly grasped it.&lt;/p&gt;

&lt;p&gt;In my current company, I have been assigned to a project to build an automation framework from the scratch in addition to being vigilant manually. Rookie myself was drowned in watching tutorials which led me nowhere because I was still fearful of making mistakes.&lt;/p&gt;

&lt;p&gt;Once I let go of that feeling and started writing codes, everything came naturally to me. Well of course all the knowledge that I had gathered watching tutorials and reading blog articles was helping me subconsciously.&lt;/p&gt;

&lt;p&gt;Yes, I have made mistakes along the way but to err is human. The more you progress in building an automation framework, the more you know what should you do to make your tests less flaky. I will share the key things that you should keep in mind while building an automation framework soon.&lt;/p&gt;

&lt;p&gt;The lesson being; don't just learn it, do it.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>testing</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
