<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://danny-briskin.github.io//feed.xml" rel="self" type="application/atom+xml" /><link href="https://danny-briskin.github.io//" rel="alternate" type="text/html" /><updated>2026-03-05T23:55:33+00:00</updated><id>https://danny-briskin.github.io//feed.xml</id><title type="html">Knowledge Vault</title><subtitle>Articles about Information Technologies by Danny Briskin</subtitle><entry><title type="html">Navigating BDD: Connecting Feature Files to Code with Ease</title><link href="https://danny-briskin.github.io//vscode/2026/03/04/navigating-bdd-connecting-feature-files-to-code-with-ease.html" rel="alternate" type="text/html" title="Navigating BDD: Connecting Feature Files to Code with Ease" /><published>2026-03-04T08:40:30+00:00</published><updated>2026-03-04T08:40:30+00:00</updated><id>https://danny-briskin.github.io//vscode/2026/03/04/navigating-bdd-connecting-feature-files-to-code-with-ease</id><content type="html" xml:base="https://danny-briskin.github.io//vscode/2026/03/04/navigating-bdd-connecting-feature-files-to-code-with-ease.html"><![CDATA[<p><img src="/images/testops_testautomation.jpg" alt="" /></p>

<p>Danny Briskin, Senior Software Developer in Test</p>

<h1 id="navigating-bdd-connecting-feature-files-to-code-with-ease">Navigating BDD: Connecting Feature Files to Code with Ease</h1>

<p>In the world of software testing, we use something called <strong>Gherkin</strong>. It is a way to write test steps in plain English (or many other languages) so that everyone can understand what the software is supposed to do.</p>

<p>But there is a catch: those plain English steps have to “talk” to real computer code. For a developer, finding which piece of code belongs to which step can be like hunting for a needle in a haystack. I built <strong>Gherkin Step Navigator</strong> to make that search disappear.</p>

<hr />

<h2 id="two-worlds-one-engine">Two Worlds, One Engine</h2>

<p>The primary hurdle in BDD (Behavior Driven Development) tooling is that human language is messy, while computer code is very strict.</p>

<h3 id="1-the-built-in-language-engine">1. The Built-In Language Engine</h3>
<p>Gherkin is unique because it is written in natural human languages. To make this extension truly universal, I didn’t just focus on English. The extension comes pre-loaded with a comprehensive list of keywords for over <strong>70 different languages</strong>.</p>

<p>Whether you are writing a test in English (<em>Given</em>), Spanish (<em>Dada</em>), or German (<em>Angenommen</em>), the extension already knows what those words mean. It uses this internal dictionary to instantly recognize a test step as soon as you type it, allowing the navigation to work across global teams without any extra setup.</p>

<h3 id="2-finding-the-code">2. Finding the Code</h3>
<p>On the other side, we have the programming code. Developers use special “tags” or “labels” to link code to a step. My extension is designed to recognize these labels across different languages. Whether you are using <strong>C#</strong>, <strong>Java</strong>, or <strong>Python</strong>, the engine knows how to find where the work happens.</p>

<hr />

<h2 id="deep-dive-the-three-pillars-of-the-architecture">Deep Dive: The Three Pillars of the Architecture</h2>

<p>The extension is built on three main systems that work together to make navigation feel like magic: the <strong>Indexer</strong>, the <strong>Cache</strong>, and the <strong>Matcher</strong>.</p>

<h3 id="1-the-background-indexer">1. The Background Indexer</h3>
<p>When you open a project, you don’t want to wait for minutes while the tool “loads.”</p>
<ul>
  <li><strong>Smart Scanning</strong>: The Indexer runs in the background. It quickly scans your project files for those special code labels.</li>
  <li><strong>Resource Awareness</strong>: It is smart enough to ignore “junk” folders like <code class="language-plaintext highlighter-rouge">node_modules</code> or build output folders. This prevents your computer from slowing down or getting “stuck” on thousands of irrelevant files.</li>
  <li><strong>Real-Time Updates</strong>: If you add a new step or change an old one, a “file watcher” tells the Indexer to update that specific file immediately. Your navigation map is always up to date.</li>
</ul>

<h3 id="2-the-in-memory-step-cache">2. The In-Memory Step Cache</h3>
<p>The Cache is where the “map” of your project lives.</p>
<ul>
  <li><strong>Instant Lookup</strong>: Instead of searching your hard drive every time you click a step, the extension looks at a fast list in your computer’s memory. This list says: <em>“This specific step name lives in this file, on this exact line.”</em></li>
  <li><strong>Efficiency</strong>: By storing this map in memory, the “Go to Definition” action becomes nearly instantaneous, even in massive projects with thousands of steps.</li>
</ul>

<h3 id="3-the-intelligent-matcher">3. The Intelligent Matcher</h3>
<p>Steps often have variables, like numbers or names (for example: “I have 5 items”, “User navigates to ‘Orders’ page”).</p>
<ul>
  <li><strong>Pattern Recognition</strong>: The Matcher is the brain that understands these patterns. It converts common human-readable patterns into a language the computer can search quickly.</li>
  <li><strong>Accuracy</strong>: It ensures that a step in your feature file finds the correct code, even if the code uses complex “regex” or placeholders.</li>
</ul>

<hr />

<h2 id="precision-navigation-no-more-drifting">Precision Navigation: No More “Drifting”</h2>

<p>One of the most annoying bugs in other tools is “navigation drift” - where you click a step, and the editor takes you to the right file, but the wrong line.</p>

<ul>
  <li><strong>Finding the Spot</strong>: Most tools just look for the line number. This extension calculates the exact position of the text, counting every character.</li>
  <li><strong>Handling Systems</strong>: Different computers save files in different ways (specifically how they handle “enters” or line breaks). This extension handles both Windows and Mac styles perfectly.</li>
  <li><strong>The Control-Click</strong>: Because of this precision, you can simply <strong>Control-Click</strong> (or press <strong>F12</strong>) on any step. The extension checks its map and takes you straight to the implementation.</li>
</ul>

<p><img src="/images/GherkinNavigator.gif" alt="Gherkin Navigator Demo" /></p>

<hr />

<h2 id="keeping-things-neat-formatting-and-colors">Keeping Things Neat: Formatting and Colors</h2>

<p>I also wanted to make sure the test files themselves stayed easy to read.</p>

<ul>
  <li><strong>Beautiful Tables</strong>: If you have a table of data, the extension automatically measures your columns and lines up all the pipes (<code class="language-plaintext highlighter-rouge">|</code>) so they look like a clean spreadsheet.</li>
  <li><strong>Great Colors</strong>: It uses colors to highlight variables, tags, and comments. This makes it easy to spot mistakes before you even run your tests.</li>
  <li><strong>Perfect Spacing</strong>: It fixes the indentation of your text (moving Scenarios and Steps to the right spots) so everything looks professional.</li>
</ul>

<p><img src="/images/GherkinFormatter.gif" alt="Gherkin Formatter Demo" /></p>

<p><strong>Gherkin Step Navigator</strong> is out now on the <a href="https://marketplace.visualstudio.com/items?itemName=DannyBriskin.gherkin-step-navigator">VS Code Marketplace</a>. If you want to help me improve it, come visit the <a href="https://github.com/danny-briskin/gherkin-step-navigator">GitHub project</a>.</p>]]></content><author><name></name></author><category term="vscode" /><category term="extension," /><category term="vscode," /><category term="gherkin," /><category term="bdd" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Efficient Test Discovery in Large Codebases: Combining LLMs with Classical ML Algorithms</title><link href="https://danny-briskin.github.io//llm/2025/09/23/combining-llm-traditional-ml.html" rel="alternate" type="text/html" title="Efficient Test Discovery in Large Codebases: Combining LLMs with Classical ML Algorithms" /><published>2025-09-23T08:40:30+00:00</published><updated>2025-09-23T08:40:30+00:00</updated><id>https://danny-briskin.github.io//llm/2025/09/23/combining-llm-traditional-ml</id><content type="html" xml:base="https://danny-briskin.github.io//llm/2025/09/23/combining-llm-traditional-ml.html"><![CDATA[<p><img src="/images/changelog.jpg" alt="" /></p>

<p>Danny Briskin, Quality Engineering Practice Manager</p>

<h2 id="the-challenge-limited-context-windows">The Challenge: Limited Context Windows</h2>
<p>Large Language Models (LLMs) such as GPT are reasoning-powerful, yet only have a limited context window (say, 8k-128k tokens depending on the model).</p>

<p>In <strong>test automation</strong> in real-world scenarios, this is a bottleneck. Suppose you have <strong>tens of thousands of automated tests</strong> in your codebase.</p>

<p>If a user says:</p>
<blockquote>
  <p><em>“I need to test the login area with multiple failed attempts”</em></p>
</blockquote>

<p>you want to return the most relevant tests that already exist. However, you can’t just hose the entire test suite into the LLM - there isn’t sufficient memory.</p>

<p>So, what do we do?
We combine <strong>classical ML/NLP algorithms</strong> with LLMs to <strong>pre-filter</strong> first and then <strong>reason</strong>.</p>

<h2 id="step-1--topic-detection">Step 1 – Topic Detection</h2>
<p>First, we extract the <strong>topic</strong> from the user’s query. This helps us match the query with the structure of existing tests.</p>

<p>Options:</p>
<ul>
  <li><strong><a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf">TF-IDF, Term Frequency-Inverse Document Frequency</a> / keyword extraction</strong> – rapid keyword-based filtering.</li>
  <li><strong>Embeddings</strong> – deeper semantic understanding.</li>
  <li>a single query to a LLM can be used as well at this point</li>
</ul>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">sklearn.feature_extraction.text</span> <span class="kn">import</span> <span class="n">TfidfVectorizer</span>

<span class="n">queries</span> <span class="o">=</span> <span class="p">[</span><span class="s">"I need to test the login area with multiple failed attempts"</span><span class="p">]</span>
<span class="n">tests</span> <span class="o">=</span> <span class="p">[</span><span class="s">"test_login_success"</span><span class="p">,</span> <span class="s">"test_login_failed_attempts"</span><span class="p">,</span>
         <span class="s">"test_password_reset"</span><span class="p">,</span> <span class="s">"test_account_lockout"</span><span class="p">]</span>

<span class="n">vectorizer</span> <span class="o">=</span> <span class="n">TfidfVectorizer</span><span class="p">()</span>
<span class="n">X</span> <span class="o">=</span> <span class="n">vectorizer</span><span class="p">.</span><span class="n">fit_transform</span><span class="p">(</span><span class="n">tests</span> <span class="o">+</span> <span class="n">queries</span><span class="p">)</span>

<span class="c1"># Compute cosine similarity between query and tests
</span><span class="kn">from</span> <span class="nn">sklearn.metrics.pairwise</span> <span class="kn">import</span> <span class="n">cosine_similarity</span>
<span class="n">cos_sim</span> <span class="o">=</span> <span class="n">cosine_similarity</span><span class="p">(</span><span class="n">X</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">X</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="n">cos_sim</span><span class="p">)</span>
</code></pre></div></div>
<p>This gives us a first-pass ranking: <strong>“test_login_failed_attempts”</strong> is the winner.</p>

<h2 id="step-2--candidate-filtering">Step 2 – Candidate Filtering</h2>
<p>Knowing that the query subject is <em>“login/authentication”</em>, we filter the repository.</p>

<p>Algorithms:</p>
<ul>
  <li><strong>Cosine similarity on embeddings</strong> → top semantic match.</li>
  <li><strong>Longest Common Subsequence (LCS)</strong> → useful if test names follow conventions.</li>
  <li><strong>Edit Distance (Levenshtein)</strong> → identifies typos in queries/test names.</li>
</ul>

<p>Example with embeddings:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">sentence_transformers</span> <span class="kn">import</span> <span class="n">SentenceTransformer</span><span class="p">,</span> <span class="n">util</span>
<span class="kn">import</span> <span class="nn">torch</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">SentenceTransformer</span><span class="p">(</span><span class="s">'all-MiniLM-L6-v2'</span><span class="p">)</span>

<span class="n">query_embedding</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="s">"login failed attempts"</span><span class="p">,</span> <span class="n">convert_to_tensor</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">test_embeddings</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">encode</span><span class="p">(</span><span class="n">tests</span><span class="p">,</span> <span class="n">convert_to_tensor</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

<span class="c1"># Semantic similarity
</span><span class="n">cos_scores</span> <span class="o">=</span> <span class="n">util</span><span class="p">.</span><span class="n">pytorch_cos_sim</span><span class="p">(</span><span class="n">query_embedding</span><span class="p">,</span> <span class="n">test_embeddings</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">top_results</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">topk</span><span class="p">(</span><span class="n">cos_scores</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>

<span class="k">for</span> <span class="n">idx</span> <span class="ow">in</span> <span class="n">top_results</span><span class="p">.</span><span class="n">indices</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">tests</span><span class="p">[</span><span class="n">idx</span><span class="p">])</span>
</code></pre></div></div>

<p>Output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>test_login_failed_attempts
test_account_lockout
test_login_success
</code></pre></div></div>

<p>Now we have <strong>candidates</strong> instead of the entire repository.</p>

<h2 id="step-3--llm-refinement">Step 3 – LLM Refinement</h2>
<p>Finally, only run candidate tests through the LLM and allow it to <strong>refine, filter, and suggest enhancements</strong>:</p>

<p><strong>Prompt Example:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>User wants: "I need to test the login area with multiple failed attempts."

Candidate tests:
1. test_login_failed_attempts
2. test_account_lockout
3. test_login_success

Question: Which tests are most relevant? Suggest if any coverage is missing.
</code></pre></div></div>

<p><strong>LLM Output:</strong></p>
<ul>
  <li>Most relevant: <code class="language-plaintext highlighter-rouge">test_login_failed_attempts</code>, <code class="language-plaintext highlighter-rouge">test_account_lockout</code>.</li>
  <li>Less relevant: <code class="language-plaintext highlighter-rouge">test_login_success</code>.</li>
  <li>Missing: A test for captcha bypass after repeated failures.</li>
</ul>

<h2 id="the-hybrid-workflow">The Hybrid Workflow</h2>
<p>Putting it together:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+------------------+
|   User Query     |
+------------------+
         |
         v
+------------------+
| Topic Detection  |
| (TF-IDF, Embed.) |
+------------------+
         |
         v
+---------------------------+
| Candidate Filtering       |
| (Cosine, LCS, Edit Dist.) |
+---------------------------+
         |
         v
+------------------+
|  LLM Refinement  |
+------------------+
         |
         v
+---------------------------+
| Suggested Relevant Tests  |
+---------------------------+
</code></pre></div></div>

<p>This approach:</p>
<ul>
  <li>Maintains LLM inputs small.</li>
  <li>Ensures fast retrieval.</li>
  <li>Trades off on deterministic filtering and generative reasoning.</li>
</ul>

<h2 id="why-not-just-use-the-llm-alone">Why Not Just Use the LLM Alone?</h2>
<ul>
  <li><strong>Context window limit</strong> – you can’t put the whole test suite in.</li>
  <li><strong>Efficiency matters</strong> – looping all tests through the LLM is expensive.</li>
  <li><strong>Reliability</strong> – algorithms provide deterministic filtering before involving the LLM.</li>
</ul>

<h2 id="generalizing-beyond-tests">Generalizing Beyond Tests</h2>
<p>This pattern applies in many contexts:</p>
<ul>
  <li>Knowledge base search (support tickets, FAQs).</li>
  <li>Document retrieval (legal, medical, financial).</li>
  <li>Conversational memory management.</li>
</ul>

<h2 id="conclusion">Conclusion</h2>
<p>Traditional ML/NLP algorithms are still important - they make LLMs practical in real-world systems.</p>

<p>In our test automation example, cosine similarity and sequence matching help narrow down thousands of tests to a few. The LLM can then reason over these results.</p>

<p>The result:</p>
<ul>
  <li>Faster.</li>
  <li>Cheaper.</li>
  <li>More accurate.</li>
</ul>

<p>Hybrid systems that use traditional algorithms for retrieval and LLMs for reasoning are the future of agent design.</p>]]></content><author><name></name></author><category term="llm" /><category term="llm," /><category term="AI," /><category term="agents," /><category term="ML," /><category term="Algorithms" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">AI Makes Life Easier - Don’t Let It Make Your Brain Lazy</title><link href="https://danny-briskin.github.io//ai/2025/06/28/ai-makes-life-easier.html" rel="alternate" type="text/html" title="AI Makes Life Easier - Don’t Let It Make Your Brain Lazy" /><published>2025-06-28T11:40:30+00:00</published><updated>2025-06-28T11:40:30+00:00</updated><id>https://danny-briskin.github.io//ai/2025/06/28/ai-makes-life-easier</id><content type="html" xml:base="https://danny-briskin.github.io//ai/2025/06/28/ai-makes-life-easier.html"><![CDATA[<p><img src="/images/project_documentation.jpg" alt="" /></p>

<p>Danny Briskin, Quality Engineering Practice Manager</p>

<h2 id="how-i-got-into-ai-and-why-i-love-tech">How I Got Into AI (and Why I Love Tech)</h2>
<p>I’ve always considered myself an innovative person. My curiosity for new technology started over 25 years ago, during my university days. I was fascinated by neural networks—long before they became a trend. I explored them in pet projects and stayed engaged with their evolution.</p>

<p>So when modern AI tools like ChatGPT, DeepSeek, and Gemini became available, I started using them right away. Because of knowledge and experience in IT, I could clearly see their strengths and weaknesses. These tools helped me achieve better results and often made my work faster and easier.</p>

<h2 id="an-interesting-ai-coding-experience">An Interesting AI Coding Experience</h2>
<p>Recently, I was working on a project that required writing code in a technology I hadn’t used for more than five years. Instead of refreshing my memory, I decided to ask one of the popular LLMs to generate this code for me. It created more than 400 lines of good, working code, and I was fully satisfied. I merged the code into the project and moved on.</p>

<p>Then, the next day, I had to implement a similar task. At first, I began coding it myself, out of habit. Then I realized - wait, why not use AI again? So I did, and once again, the results were great.</p>

<p>But then, on another day, I found myself writing code manually again - without thinking. Why? The AI-generated code had been good. It worked. So why didn’t I turn to AI first this time?</p>

<p>I began to reflect. The action felt unconscious, like my brain just defaulted to the old way of working. Was it habit? Resistance to change? Or something deeper?</p>

<h2 id="a-scientific-research-about-over-relying-on-ai">A Scientific Research About Over Relying on AI</h2>
<p>Curious about why I kept returning to manual work despite AI’s success, I began looking into research about how AI affects our thinking. That’s when I came across a fascinating study: <a href="https://arxiv.org/abs/2506.08872">Your Brain on ChatGPT: Accumulation of Cognitive Debt when Using an AI Assistant for Essay Writing Task</a>.
The research explored how using AI for writing tasks affects our memory and brain activity. It introduced the term “cognitive debt” - a side effect of letting AI do the hard thinking for us. Here’s what it means:</p>
<ul>
  <li>Your brain skips a workout. When AI does the thinking, your brain doesn’t engage or strengthen the skills needed for memory and reasoning.</li>
  <li>Learning doesn’t stick. People who used AI couldn’t recall what they “wrote” because their brain didn’t actively process the material.</li>
  <li>Long-term skill decline. Relying on AI too much may weaken problem-solving abilities over time.</li>
</ul>

<h2 id="my-personal-realization">My Personal Realization</h2>
<p>This is exactly what happened to me. I’ve always kept my brain active, learning constantly. But suddenly, AI was doing all the work. Naturally, my brain resisted - trying to stay in shape by forcing me to think instead of relying on automation.</p>

<p>Out of curiosity, I re-checked the AI-generated code. It still worked fine, but on closer review, I spotted inefficiencies and logic I would’ve handled differently. Why didn’t I see them earlier? Most likely, because I was mentally “checked out.” The job seemed “done,” so I didn’t dig deeper.</p>

<p>When AI writes an email for me, I review it carefully. But when it generates 400 lines of working code, it’s harder to stay vigilant - especially when the output already looks good.</p>

<h2 id="what-should-we-do">What Should We Do?</h2>
<p>Should we stop using AI? No. The genie is out of the bottle. But we should treat AI like a junior team member - useful, knowledgeable, fast, but still learning. Just like we guide junior developers, we must review and improve what AI creates.</p>

<p>We need to stay critical, think actively, and not outsource our brain. If we blindly accept AI outputs, we risk losing the very skills that made us effective in the first place.</p>

<h2 id="conclusion-stay-human">Conclusion: Stay Human</h2>
<p>AI is a powerful tool - but not a replacement for thinking. Don’t stop using your brain. Don’t stop reviewing, questioning, learning. Let AI help, but don’t let it make you passive.</p>

<p>Don’t stop thinking. Train your brain. Be human.</p>]]></content><author><name></name></author><category term="ai" /><category term="ai,llm,Artificial" /><category term="Intelligence," /><category term="brain" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">AI’s Next Lesson: Who Will Teach the Teacher?</title><link href="https://danny-briskin.github.io//ai/2025/06/01/ai-next-lesson-who-will-teach-the-teacher.html" rel="alternate" type="text/html" title="AI’s Next Lesson: Who Will Teach the Teacher?" /><published>2025-06-01T11:40:30+00:00</published><updated>2025-06-01T11:40:30+00:00</updated><id>https://danny-briskin.github.io//ai/2025/06/01/ai-next-lesson-who-will-teach-the-teacher</id><content type="html" xml:base="https://danny-briskin.github.io//ai/2025/06/01/ai-next-lesson-who-will-teach-the-teacher.html"><![CDATA[<p><img src="/images/flexibility_aspectj.jpg" alt="" /></p>

<p>Danny Briskin, Quality Engineering Practice Manager</p>

<h2 id="introduction">Introduction</h2>

<p>As more people use Artificial Intelligence (AI) tools, fewer people are using services run by humans. A very noticeable example is the Stack Overflow website, which is much less active now. This trend poses significant challenges for the continued improvement of AI and its ability to provide dependable results. It also jeopardizes easy access to accurate technical knowledge..</p>

<h2 id="from-human-communities-to-ai-assistants">From Human Communities to AI Assistants</h2>

<p>To understand this shift, we first examine why Stack Overflow’s use has decreased. Traditionally, Stack Overflow operated as a key online community where users could ask IT-related questions and usually receive quick responses from professionals. Many experts willingly shared their knowledge there at no cost, often motivated by a system of reputation points earned through helpful contributions. While users needed to register and follow strict posting rules, the quality of answers often justified the effort.</p>

<p>Today, Large Language Models (LLMs) like ChatGPT offer a simpler way to get answers immediately. Users can refine questions with more details and explore different answers interactively, a flexibility not present on Stack Overflow. Furthermore, Stack Overflow users sometimes faced negative feedback from the community for questions perceived as low-quality or redundant. LLMs, in contrast, offer a judgment-free interaction.</p>

<p>AI tools have become effective aids for software engineers. For instance, LLMs can help generate initial drafts for requirements, user stories, system architecture, or test cases. Additionally, many code assistance tools integrated into Integrated Development Environments (IDEs) use AI to increase developer productivity. Overall, LLMs provide quick and often dependable support when needed.</p>

<h2 id="ais-reliance-on-human-generated-knowledge">AI’s Reliance on Human-Generated Knowledge</h2>

<p>This situation raises an important question regarding potential downsides. The effectiveness of LLMs, especially in software engineering, is not accidental. These AI models achieved their current capabilities by being trained on extensive and specific datasets. A significant portion of this training data, particularly for coding tasks, was sourced directly from platforms like Stack Overflow and the vast amounts of human-written code available in public repositories.</p>

<h2 id="potential-effects-of-a-shrinking-knowledge-base">Potential Effects of a Shrinking Knowledge Base</h2>
<p>If the trend of declining use of human-to-human Q&amp;A websites continues, the publicly available collection of expert knowledge, essential for AI training, will significantly decrease. While human expertise will still exist, its accessibility for training AI models will be reduced.</p>

<p>The absence of active platforms like Stack Overflow could hinder the ability of future AI versions to learn about new developments. Some might suggest that LLMs can infer new information from existing data. However, LLMs are designed to generate responses even with limited input. If fresh, relevant data is scarce, an LLM might produce an answer based on its older training. Such an answer could sound convincing but be inaccurate or inapplicable, a phenomenon known as “hallucination,” which is difficult to prevent.</p>

<p>LLMs do not possess human-like understanding; they identify patterns and combine information from their training data, which can sometimes produce seemingly new outputs. However, their capacity to provide correct and useful information for new or fast-changing technical areas depends heavily on up-to-date and varied training data. Without new information reflecting current developments, LLM-generated solutions might be based on old or incomplete patterns.</p>

<p>Consider a scenario where a new version of a widely used programming language or database system is released. Without ongoing human discussion and problem-solving on public platforms, AI models might provide information relevant only to older versions or, worse, generate incorrect solutions. This raises the question of how AI will learn about these new technologies.</p>

<p>Furthermore, while popular technologies like Java or Python have large communities generating vast amounts of data, niche tools have much smaller user bases and fewer experts. The challenge of training AI effectively for these less common technologies becomes even greater with a reduced flow of new human-generated knowledge.</p>

<h2 id="difficulties-in-keeping-ai-knowledge-current">Difficulties in Keeping AI Knowledge Current</h2>

<p>Recently, efforts have begun to update existing LLMs by fine-tuning them with new information and identifying outdated content. However, this process is complex. While it’s possible to correct general knowledge gaps, such as updating an LLM with the name of a newly elected official, addressing highly specialized technical subjects is much harder. It raises questions about the number of experts required to continually update AI across all fields of knowledge.</p>

<p>The community environment of platforms like Stack Overflow encouraged knowledge sharing. Motivations such as helping others and gaining recognition led to the organic creation of a large, high-quality dataset. This type of motivation is less apparent in human-AI interactions. Engineers may use AI for their own tasks, but widespread, voluntary contributions to train or correct AI systems are unlikely without clear rewards or a sense of community. This loss of intrinsic motivation weakens a key method for obtaining ongoing training data.</p>

<p>Furthermore, some LLM developers believe that most easily available public text data suitable for training has already been used. To improve AI further, it might be necessary to use real-time information or private data sources. For specific areas like software engineering, obtaining up-to-date, practical knowledge could mean analyzing private code, internal documents, or even developer activities. Such methods present major challenges regarding cost, privacy, ethics, and scalability, potentially making LLM usage too expensive.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The ease of using AI tools like LLMs is unintentionally causing a decrease in the use of important human-based knowledge platforms like Stack Overflow. This situation presents a key problem: the AI systems that depend on these platforms are also contributing to the loss of their essential data sources. This can lead to serious outcomes, such as AI development slowing down, more instances of incorrect or fabricated AI-generated information, specific problems keeping AI updated on new and specialized technologies, and major challenges in finding practical, ethical, and affordable ways to refresh AI knowledge.</p>

<p>This highlights the ongoing importance of promoting mutual support among software engineers, as their collective expertise will remain an indispensable resource. While AI offers unprecedented capabilities, the dynamic, evolving nature of human problem-solving and collaborative learning remains unique. Ensuring that this human element continues to thrive and contribute to the collective knowledge pool will be essential for navigating the complexities of future technological development.</p>]]></content><author><name></name></author><category term="ai" /><category term="ai," /><category term="llm,Knowledge" /><category term="Management,Stack" /><category term="Overflow,Artificial" /><category term="Intelligence" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Coding assistance AI tools</title><link href="https://danny-briskin.github.io//ai,/copilot,/coding/assistance/2024/07/24/coding-assistance-ai-tools.html" rel="alternate" type="text/html" title="Coding assistance AI tools" /><published>2024-07-24T11:40:30+00:00</published><updated>2024-07-24T11:40:30+00:00</updated><id>https://danny-briskin.github.io//ai,/copilot,/coding/assistance/2024/07/24/coding-assistance-ai-tools</id><content type="html" xml:base="https://danny-briskin.github.io//ai,/copilot,/coding/assistance/2024/07/24/coding-assistance-ai-tools.html"><![CDATA[<p><img src="/images/project_documentation.jpg" alt="" /></p>

<p>Danny Briskin, Quality Engineering Practice Manager</p>

<h1 id="introduction">Introduction</h1>
<p>In recent years, artificial intelligence has significantly transformed the landscape of software development by introducing AI coding assistance tools. These tools, powered by advanced machine learning algorithms, are designed to enhance productivity, reduce errors, and streamline the coding process. By providing real-time suggestions, auto-completions, and intelligent code analysis, AI coding assistants are revolutionizing the way developers write and optimize code. This technology not only accelerates the development process but also empowers developers to focus on more complex problem-solving tasks, thereby fostering innovation and efficiency.</p>

<p>Are those tools really that powerful? Let’s figure that out.</p>

<h1 id="ai-powered-tools">AI powered tools</h1>
<p>There are plenty of tools that can be used for software development process, starting from idea generation to code generation and testing. Here is a list of most know ones</p>
<ul>
  <li>Tools for ideas generation and concepts creation
    <ul>
      <li><a href="https://openai.com/chatgpt/">ChatGPT</a> - A universal assistant for generating ideas and answering complex queries.</li>
      <li><a href="https://gluecharm.com/">GlueCharm</a> - A tool for creating user stories and managing product backlogs.</li>
      <li><a href="https://www.frase.io/">Frase</a> - A content generation tool for websites.</li>
      <li><a href="https://miro.com/product-overview/">Miro</a> - A collaborative platform for product diagramming and prototyping.</li>
    </ul>
  </li>
  <li>Tools for coding assistance
    <ul>
      <li><a href="https://copilot.microsoft.com/">Copilot</a> - A code completion tool that suggests and generates code based on the context.</li>
      <li><a href="https://openai.com/chatgpt/">ChatGPT models</a> - Can assist in code generation and problem-solving.</li>
      <li><a href="https://gemini.google.com/">Gemini</a> - A text-generation tool.</li>
      <li><a href="https://cloud.google.com/vertex-ai">Vertex AI</a> - A tool for both text and code generation.</li>
      <li><a href="https://cloud.google.com/products/gemini/code-assist">Gemini Code Assist</a> - A text-generation tool for writing and generating code.</li>
      <li><a href="https://github.com/meta-llama/llama/tree/main">Llama</a> - Meta’s code and text generation model.</li>
      <li><a href="https://www.tabnine.com/">Tabnine</a> - A robust AI code completion tool.</li>
      <li><a href="https://codeium.com">Codeium</a> - Specializes in both code and text generation, offering advanced capabilities for developers.</li>
    </ul>
  </li>
</ul>

<p>In this article I will concentrate on coding assistance tools only.</p>

<h1 id="the-pros-of-ai-tools-in-software-development">The Pros of AI tools in Software Development</h1>
<p>AI coding tools have become an integral part of modern software development, offering a range of benefits:</p>
<ul>
  <li><strong>Affordability:</strong> Many AI tools provide a trial period or even a free tier, making them accessible to a broad audience.</li>
  <li><strong>Deployment Options:</strong> These tools often offer cloud-based solutions and on-premises versions to suit different organizational needs.</li>
  <li><strong>Ease of Use:</strong> Installation is typically straightforward, and most tools support plugins for popular IDEs, integrating seamlessly into existing workflows.</li>
  <li><strong>Contextual Suggestions:</strong> AI tools utilize predefined models and analyze your codebase, including comments, to provide context-aware suggestions.</li>
  <li><strong>Security:</strong> Many AI tools are tested for security compliance and have relevant certifications.</li>
  <li><strong>Specific Use Cases:</strong>
    <ul>
      <li><strong>Function Documentation and Comments:</strong> AI tools are extremely useful for generating javadoc, docstrings, and in-code comments, especially for non-native English speakers.</li>
      <li><strong>Code Predictions:</strong> These tools are helpful for generating boilerplate code, but when the code is supposed to be more sophisticated, the usefulness of tools becomes questionable.</li>
      <li><strong>Code Completion:</strong> AI code completion is useful but built-in IDE code completion tools are currently more robust and faster.</li>
      <li><strong>Git Commit Messages:</strong> Some tools can analyze changes between commits, though they are not very robust.</li>
      <li><strong>Code Explanation:</strong> AI tools provide valuable insights for junior developers, making them very useful in this aspect.</li>
      <li><strong>Unit Test Generation:</strong> Many tools excel in generating unit tests, offering great value there.</li>
      <li><strong>Code refactoring:</strong> - tools are quite useful, but developer needs to put a lot of efforts to explain what is needed and the result is questionable in most cases.</li>
    </ul>
  </li>
</ul>

<h1 id="code-assistance-with-codeium--use-case-analysis">Code Assistance with Codeium – Use Case Analysis</h1>
<p>AI-powered tools such as Codeium have proven to be effective in generating code documentation, ranging from simple to more complex cases. For example, the following Python docstring was entirely generated by AI:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="s">"""
Sends a REST request to the specified endpoint with the given parameters.

Args:
    endpoint_qualifier (str): The identifier of the endpoint to send the request to.
    endpoints_config (dict[str, Any]): A dictionary containing the configuration for all endpoints.
    replacements (dict[str, dict[str, str]], optional): A dictionary containing replacements for the payload and path of the request.
    **kwargs: Additional keyword arguments to customize the request.

Returns:
    Response: The response object containing the result of the request.

Raises:
    ValueError: If the endpoint qualifier is invalid or if the authentication or method is not specified for the endpoint.
    Exception: If the expected status code does not match the actual status code of the response.
"""</span></code></pre></figure>

<p>On the other hand, this docstring output</p>

<p><img src="/images/code_assistance_01.jpg" alt="generate docstring" />
although highly useful, still requires human review for completeness and correctness. 
<img src="/images/code_assistance_02.jpg" alt="generate docstring result" /></p>

<h2 id="code-predictions">Code Predictions</h2>
<p>The code prediction functionality provided by Codeium is generally effective, particularly in scenarios involving repetitive tasks like REST requests. It efficiently predicts the next line of code and offers useful suggestions.</p>

<p><img src="/images/code_assistance_03.jpg" alt="code prediction 1" />
<img src="/images/code_assistance_04.jpg" alt="code prediction 2" />
<img src="/images/code_assistance_05.jpg" alt="code prediction 3" />
However, if a prediction is declined and replaced with a manually written line, the tool will still attempt to predict the same suggestion for the subsequent line, which can become frustrating for the developer after multiple occurrences.</p>

<h2 id="code-completion">Code Completion</h2>
<p>The code completion feature is closely related to prediction but often lacks in variability and performance. For example, when a user starts typing, the AI might suggest a single line of completion, as seen in the grey suggestion below:
<img src="/images/code_assistance_06.jpg" alt="code completion" />
In contrast, the built-in IDE completion tools typically provide multiple options, as seen in the popup shown in the same scenario. This demonstrates that while AI completion can be helpful, its current implementation still trails behind traditional code completion systems in terms of flexibility and speed.</p>

<h2 id="git-commit-messages">Git Commit Messages</h2>
<p>The AI’s ability to assist in generating Git commit messages is somewhat limited. When analyzing changes in the deletion of a class field (a mapped database table column, for instance), the suggested commit messages remain very basic, often failing to capture the full context of the modification. For example:
<img src="/images/code_assistance_07.jpg" alt="git changes" />
When the change was in deletion of class field (mapped database table column): 
<img src="/images/code_assistance_08.jpg" alt="git changes" />
The proposed commit messages were (white was human printed, grey – AI propositions):
<img src="/images/code_assistance_09.jpg" alt="git changes" />
Or:
<img src="/images/code_assistance_10.jpg" alt="git changes" />
As seen in this instance, the AI lacks the sophistication to create insightful commit messages and typically defaults to generic or overly simplistic suggestions.</p>

<h2 id="code-explanation">Code Explanation</h2>
<p>This feature is highly beneficial, especially for junior engineers. The AI can identify third-party libraries and explaining their methods based on a minimal codebase. This ability to provide clear, context-aware explanations can significantly accelerate the learning curve for less experienced developers.
<img src="/images/code_assistance_11.jpg" alt="code explanation" /></p>

<h2 id="unit-test-generation">Unit Test Generation</h2>
<p>AI-generated unit tests can be valuable as a starting point. The AI is proficient in producing a list of possible test cases and generating basic code structures for unit tests. For instance, when provided with a function to test, the AI was able to suggest relevant tests and create functional test cases.
<img src="/images/code_assistance_12.jpg" alt="unit tests" />
<img src="/images/code_assistance_13.jpg" alt="unit tests" />
<img src="/images/code_assistance_14.jpg" alt="unit tests" /></p>

<p>While the tests may need further customization and refinement, the foundation provided by AI can save developers time and ensure greater test coverage.</p>

<h2 id="code-refactoring">Code Refactoring</h2>
<p>For simple and well-understood scenarios, AI-driven code refactoring works efficiently. For instance, when refactoring the following function:
<img src="/images/code_assistance_15.jpg" alt="refactoring" />
A refactoring with:
<img src="/images/code_assistance_16.jpg" alt="refactoring" />
resulted in the next code:
<img src="/images/code_assistance_17.jpg" alt="refactoring" /></p>

<p>A parameter was added, the correct field name was found in the <strong>Account_Balancing</strong> class, correct function (<strong>where</strong>) was called.</p>

<p>But, if the scenario becomes a bit more complicated, the AI fails. Trying to add a JWT token type authentication functionality along with existing Bearer Authentication: 
<img src="/images/code_assistance_18.jpg" alt="refactoring" />
<img src="/images/code_assistance_20.jpg" alt="refactoring" />
Here you are (that’s right, it’s a carbon copy):
<img src="/images/code_assistance_19.jpg" alt="refactoring" /></p>

<p>Even a straightforward call to an existing function from the same file results in complete guesswork from the AI, as if it is uncertain about the expected outcome. For example:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">Using the rest_request function from this file, send a POST request to http://localhost:8080/endpoint with payload {"onekey": "twovalue"}</code></pre></figure>

<p><img src="/images/code_assistance_21.jpg" alt="refactoring" />
The answer was:
<img src="/images/code_assistance_22.jpg" alt="refactoring" />
The function to call looks like this:
<img src="/images/code_assistance_23.jpg" alt="refactoring" />
As a result, the parameter usage was entirely incorrect.</p>

<h1 id="the-cons-of-ai-tool-in-software-development">The Cons of AI Tool in Software Development</h1>
<p>Despite their advantages, AI coding tools have several drawbacks:</p>
<ul>
  <li><strong>Immature Plugins:</strong> IDE plugins need improvement to become more reliable and feature-rich.</li>
  <li><strong>Performance Issues:</strong> The analysis required for suggestions can lead to slow performance, affecting productivity.</li>
  <li><strong>Data Privacy Concerns:</strong> There is a risk of data leaks, as user inputs may be used for model training by AI tool creators.</li>
  <li><strong>Lack of Open Source:</strong> Most tools are not open source, raising concerns about potential information leaks, even with on-premises solutions.</li>
  <li><strong>Training Data Quality:</strong> The models are trained on unknown datasets, which may not adhere to best coding practices.</li>
  <li><strong>Limited of Precise Tuning:</strong> While AI tools can use your codebase for suggestions, they often focus on the current file rather than the entire project.</li>
  <li><strong>Annoying Behavior:</strong> AI tools may repeatedly suggest inappropriate code, causing frustration for developers.</li>
</ul>

<h1 id="general-concerns--about-ai-tools">General Concerns  about AI Tools</h1>
<ul>
  <li><strong>Reliability:</strong> AI tools can generate erroneous or unsafe code, necessitating thorough code reviews and undermining the idea of “machine-generated code.” Complex tools may create code that contradicts programming principles.</li>
  <li><strong>Code Support and Maintenance:</strong> AI-generated code may be overcomplicated, use uncommon approaches, and vary among developers, making collaboration challenging. Refactoring AI-generated code can be difficult.</li>
  <li><strong>Continuous Improvement:</strong> AI is trained (at least it is supposed to be) on syntactically correct elements, but this doesn’t guarantee optimal performance or maintainability.</li>
  <li><strong>Loss of Control:</strong> Developers may feel disconnected from the code, leading to their skill deterioration and reduced ability to handle AI errors.</li>
  <li><strong>Habits and Motor Memory:</strong> Experienced developers rely on motor memory for IDE tasks. AI tools disrupt this pattern, requiring more time for decision-making and returning to the original workflow.</li>
  <li><strong>Incorrect Suggestions:</strong> AI tools may offer irrelevant code suggestions that appear correct, leading to potential coding errors that regular code completion tools would avoid.</li>
</ul>

<h1 id="summary">Summary</h1>

<p>AI coding assistance tools have revolutionized software development by enhancing productivity, reducing errors, and streamlining the coding process. These tools provide real-time suggestions and intelligent code analysis, allowing developers to focus on complex problem-solving. AI tools offer several benefits, including affordability, ease of use, and contextual suggestions. They excel in specific tasks such as function documentation, code predictions, unit test generation, and code explanations, particularly aiding non-native English speakers and junior developers.</p>

<p>However, AI tools also have significant drawbacks. They can suffer from performance issues, data privacy concerns, and immature plugins. Most tools are not open source, leading to potential information leaks and questionable training data quality. AI-generated code can be unreliable, challenging to maintain, and often requires thorough review. Developers may feel a loss of control over their code, and AI tools can disrupt their workflow and motor memory. Additionally, incorrect suggestions can lead to coding errors that are not easily caught.</p>

<p>Overall, while AI tools can greatly assist in software development, their limitations and risks must be carefully managed to ensure reliable and efficient coding practices.</p>]]></content><author><name></name></author><category term="ai," /><category term="copilot," /><category term="coding" /><category term="assistance" /><category term="ai," /><category term="copilot," /><category term="coding" /><category term="assistance" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Test Automation Scope: Balancing Value and Complexity with End-to-End Testing</title><link href="https://danny-briskin.github.io//testing/2023/08/16/balancing-value-and-complexity-e2e-testing.html" rel="alternate" type="text/html" title="Test Automation Scope: Balancing Value and Complexity with End-to-End Testing" /><published>2023-08-16T11:40:30+00:00</published><updated>2023-08-16T11:40:30+00:00</updated><id>https://danny-briskin.github.io//testing/2023/08/16/balancing-value-and-complexity-e2e-testing</id><content type="html" xml:base="https://danny-briskin.github.io//testing/2023/08/16/balancing-value-and-complexity-e2e-testing.html"><![CDATA[<p><img src="/images/hidden_images.jpg" alt="" /></p>

<p>Danny Briskin, Quality Engineering Practice Manager</p>

<h1 id="introduction">Introduction</h1>
<p>In the realm of Quality Assurance (QA) automation, a recurring conundrum faces Software Development Engineer in Test professionals, at the outset of each project or even within Agile Sprints: What is the optimal scope of automation? The notion of “automating everything” is a common refrain from clients; however, deciphering the precise scope of “everything” remains a challenge. The process involves a judicious selection of test types for automation, a task that extends beyond mere mechanical execution..</p>

<h1 id="strategies-for-determining-automation-scope">Strategies for Determining Automation Scope</h1>
<h2 id="the-simple-and-direct-approach-full-automation">The Simple and Direct Approach: Full Automation</h2>
<p>This method entails mirroring manual test cases by automating each one. This approach provides comprehensive coverage, a favored aspect by project managers, and establishes a one-to-one mapping between manual and automated test suites, enhancing reporting capabilities.
Of course there is a drawback. This strategy often yields an extensive automation test suite, often marred by redundant or partially redundant test cases, leading to increased complexity and inefficiencies. Many TCs will not add any new value to the automation process and testing in general. For example, if there is a login feature in the system and you need to test a positive scenario. Obviously, you can automate it as a separate scenario but why? I’ll bet 95% of your TC require a successful login, so this part will definitely be tested elsewhere.</p>

<h2 id="the-value-based-approach-strategic-automation">The Value-Based Approach: Strategic Automation</h2>
<p>This approach involves assessing potential future costs for the development, maintenance, and execution of automated test cases. A critical metric known as the “test value” emerges from the ratio of manual testing expenses to automated testing costs. The “test value” evolves over time, becoming more favorable as development concludes and maintenance diminishes. Less intricate tests, reminiscent of component tests, can swiftly acquire value, while complex End-to-End (E2E) tests may never accrue substantial value due to elevated development and maintenance overheads.
Does it mean that one should automate only high-valued tests and never automate E2E tests?
Notably, E2E tests remain compelling for their demonstrative potential, aiding in securing top management support for test automation endeavors.</p>

<h1 id="enhancing-test-value-through-combination-unifying-test-cases">Enhancing Test Value through Combination: Unifying Test Cases</h1>
<p>Elevating the value of a test case can be achieved by amalgamating smaller tests. For instance, merging a login action with subsequent steps and logout functionality within the same test can optimize development, maintenance, and execution time, while manual effort increases.
This approach has drawbacks:</p>
<ul>
  <li>Challenges arise in tracking these combined test cases within the original manual test suite, as complex one-to-many, many-to-one, or many-to-many relationships between automation and manual tests may arise.</li>
  <li>This approach, while increasing value, also introduces complexities by transitioning from component-level tests to broader E2E scenarios. That also increases execution time.</li>
</ul>

<h1 id="end-to-end-e2e-tests-evaluating-the-spectrum">End-to-End (E2E) Tests: Evaluating the Spectrum</h1>
<p><em>End-to-End (E2E) testing is a software testing technique that verifies the functionality and performance of an entire software application from start to finish by simulating real-world user scenarios.</em>
Reasons to <strong>NOT</strong> doing E2E Automation:</p>
<ul>
  <li>E2E tests demand advanced software development skills, making implementation and maintenance daunting.</li>
  <li>E2E testing hinges on application state, necessitating meticulous setup and control of test data.</li>
  <li>The comprehensive environment required for E2E tests often involves shared components, adding intricacies.</li>
  <li>E2E tests encompass numerous serial steps and typically require considerable execution time.</li>
  <li>Execution involves interaction with user interfaces, exacerbating complexity.</li>
  <li>E2E tests are frequently executed later within Continuous Integration/Continuous Deployment (CI/CD) pipelines.</li>
</ul>

<p>The Singular Justification for E2E Automation: Customer-Centric Assurance</p>
<ul>
  <li>E2E tests stand as the sole category of automated tests that offer a genuine demonstration of application functionality aligned with user expectations.</li>
</ul>

<h1 id="balancing-the-automation-spectrum-making-informed-choices">Balancing the Automation Spectrum: Making Informed Choices</h1>
<p>Decision Framework:</p>
<ul>
  <li>There is no universal solution, whether embracing or eschewing E2E automation. E2E tests possess undeniable value, yet their challenges are evident.</li>
  <li>Total exclusion of E2E tests leaves the testing suite incomplete, potentially missing critical issues only detectable through comprehensive holistic testing.</li>
</ul>

<p>Guideline from Experience: A Balanced Approach
Based on empirical observations, a pragmatic guideline suggests allocating no more than 10% of the total number of tests and execution time to E2E tests for optimal results in most projects</p>

<h1 id="summary">Summary</h1>

<p>In the world of QA automation, deciding what to automate is a perpetual challenge. The “automate everything” mantra requires strategic assessment, with options like comprehensive replication of manual tests or evaluating value-based automation. End-to-End (E2E) tests, though impactful, introduce complexities such as maintenance, execution time, and UI interactions. Balancing E2E and other testing approaches is pivotal for efficient and effective test automation strategies.</p>]]></content><author><name></name></author><category term="testing" /><category term="testing," /><category term="e2e," /><category term="automation" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Effective Management of Graphical Documentation with Version Control</title><link href="https://danny-briskin.github.io//testing,/documentation/2023/08/02/effective-management-of-graphical-documentation-with-version-control.html" rel="alternate" type="text/html" title="Effective Management of Graphical Documentation with Version Control" /><published>2023-08-02T18:40:30+00:00</published><updated>2023-08-02T18:40:30+00:00</updated><id>https://danny-briskin.github.io//testing,/documentation/2023/08/02/effective-management-of-graphical-documentation-with-version-control</id><content type="html" xml:base="https://danny-briskin.github.io//testing,/documentation/2023/08/02/effective-management-of-graphical-documentation-with-version-control.html"><![CDATA[<p><img src="/images/tn_component-diagram-test-framework.jpg" alt="" />
Danny Briskin, Quality Engineering Practice Manager</p>

<h1 id="the-issue">The issue</h1>
<p>Maintaining comprehensive documentation in a project is a fundamental concept. For more insights, refer to this <a href="https://danny-briskin.github.io/documentation/2021/08/05/project-documentation.html">article</a>. However, the majority of documents become difficult to manage over time. As the project progresses, outdated documents must be discarded and replaced with new versions. The primary issue lies in the diversity of document formats. From the worst-case scenario of password-protected PDFs to various MS Word formats and even Markdown pages, managing versioning becomes either unviable or severely limited for such documents, particularly for graphical elements.</p>

<h1 id="implementing-version-control">Implementing Version Control</h1>
<p>Being software developers, we are well aware of version control, specifically Git. A clever approach is to leverage Git for managing documents as well. Surprisingly, even binary formats like PDFs and Word documents can be tracked using Git-like versioning. By adopting Markdown for documentation, one can fully exploit Git’s functionality, including branching, merging, and history with changes attributed to specific usernames. This solution can be implemented by maintaining these documents in a separate Git repository, preferably, or even within the codebase if the number of documents is manageable.</p>

<h1 id="the-role-of-visuals">The Role of Visuals</h1>
<p>Text often falls short in conveying complex information, and thus, we frequently rely on images to provide additional clarity to our content. Technical diagrams are an integral part of project documentation and must be effectively stored and versioned.</p>

<h1 id="pictures-as-text">Pictures as Text</h1>
<p>One viable solution is utilizing the <a href="https://en.wikipedia.org/wiki/SVG">SVG</a> format, which offers a text-based (XML) structure, allowing for easy depiction of various images. However, most project documentation comprises technical diagrams like UML diagrams, and switching to SVG might not be the most practical approach. Another solution is to use <a href="https://en.wikipedia.org/wiki/LaTeX">LaTeX</a> for document creation, but the learning curve and tools needed for the solution is an overkill for simple diagrams. Instead, we recommend using PlantUML, a simple tool that generates UML diagrams from formatted text documents. PlantUML utilizes SVG conversion in the background, ensuring seamless integration without additional complexities.</p>

<h1 id="introducing-plantuml">Introducing PlantUML</h1>
<p><a href="https://plantuml.com/">PlantUML</a> is a powerful tool that enables the creation of various UML diagrams along with non-UML diagrams, offering the flexibility to depict virtually anything effortlessly. From Component diagrams with JSON to Gantt diagrams and MindMaps, PlantUML proves to be a versatile solution with a minimal learning curve.
They use SVG conversion in background, so there is no extra magic.</p>

<h1 id="example-test-framework-component-diagram">Example: Test Framework Component Diagram</h1>
<p>Let’s take the example of creating a component diagram for a test automation framework. You can access the complete file <a href="/files/test_framework.puml">here</a>.</p>

<p>We can define several components within a package as follows:</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml">package "Virtual Machine environment" {
  component [Jenkins CI] as jenkins
  component [Java Development Kit (ver. 17)] as java17
  component [Email Server] as local_email
  database "Redis in-memory DB" as redis
}</code></pre></figure>

<p>We can also create packages inside other packages, and define connections between components:</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml">package "Test Automation Framework" {
  component [Cucumber] as cucumber
  package "Page objects" {
      component [Page Object 1]</code></pre></figure>

<p>This allows us to define bilateral and unilateral connections between components and packages, resulting in a comprehensive component diagram for the test automation framework.</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml">  junit <span class="err">&lt;</span>-&gt; cucumber
  junit <span class="err">&lt;</span>-&gt; spring

  "Test Automation Framework" -d.&gt; "System under test":tests</code></pre></figure>

<p>Look what we have as a result:
<img src="/images/component-diagram-test-framework.jpg" alt="" /></p>

<p>#Example: Usecase Diagram
Another application of PlantUML is in creating usecase diagrams, such as the invoicing process diagram. You can download the complete file <a href="/files/invoice_processing.puml">here</a> .</p>

<p>PlantUML allows the definition of complex elements, like loops and comments, to provide detailed context within the diagram.</p>

<p>Here’s an excerpt from the usecase diagram:</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml">group Initialization. Cleaning possible errors.
:Try to run **faTransactionServiceTrial** request;
if (faTransactionServiceTrial failed?) then (<span class="nt">&lt;color:red&gt;</span>yes)
  :<span class="nt">&lt;color:red&gt;</span>Delete<span class="nt">&lt;/color&gt;</span> **P** status records from rdr_sxm_feed;
else (<span class="nt">&lt;color:green&gt;</span>no)
endif
end group</code></pre></figure>

<p>A loop and a comment</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml">while (For ID in Invoice file)
  :Send **RDR** request with respect of 29th day;
  note right
    If Today is less 29th of the month
    RDR will be sent with Today's date,
    otherwise - it will be 1st day of the **next** month
    ====
    see <span class="nt">&lt;b&gt;</span>Utilities#getNon29thDate()<span class="nt">&lt;/b&gt;</span>
  end note
endwhile</code></pre></figure>

<p>Here is the result
<img src="/images/use-case-diagram-invoicing-process.jpg" alt="" /></p>

<h1 id="summary">Summary</h1>
<p>By embracing PlantUML and incorporating version control practices, we can effectively manage graphical documentation, ensuring a clear and streamlined approach to project documentation.</p>]]></content><author><name></name></author><category term="testing," /><category term="documentation" /><category term="testing," /><category term="documentation," /><category term="uml," /><category term="plantuml" /><summary type="html"><![CDATA[Danny Briskin, Quality Engineering Practice Manager]]></summary></entry><entry><title type="html">Importance of logging in test automation development</title><link href="https://danny-briskin.github.io//testing/2023/06/07/logging.html" rel="alternate" type="text/html" title="Importance of logging in test automation development" /><published>2023-06-07T18:40:30+00:00</published><updated>2023-06-07T18:40:30+00:00</updated><id>https://danny-briskin.github.io//testing/2023/06/07/logging</id><content type="html" xml:base="https://danny-briskin.github.io//testing/2023/06/07/logging.html"><![CDATA[<p><img src="/images/api_documentation.jpg" alt="" />
Danny Briskin, Quality Engineering Practice Manager</p>

<h1 id="the-issue">The issue</h1>
<p>In the realm of software applications, failures are inevitable. The causes of these failures may vary, ranging from issues in the code, logic, data, or the environment itself. However, it is essential to identify the root cause of each failure. While not all failures necessitate immediate changes, distinguishing between different types of failures is crucial.</p>

<p>Typically, developers employ concise error messages that provide potential reasons for the failure. Even in the event of a Blue Screen of Death (<a href="https://en.wikipedia.org/wiki/Blue_screen_of_death">BSoD</a>) occurrence, there is valuable information to consider. However, these messages, at best, only provide insights into the current failure situation. They cannot transport us back in time to reveal the state of the system prior to the crash.</p>

<h1 id="logging-in-test-automation-really-why">Logging? In test automation? Really, why?</h1>
<p>While logs have been in existence since the advent of modern computers, it is peculiar that software developers, particularly those specializing in test automation (SDETs), frequently neglect the implementation of proper logging practices within their applications. In my experience, the products developed by SDETs, which encompass test automation frameworks, exhibit a higher likelihood of encountering failures compared to “regular” applications. When confronted with a failure, the common approach is to repeat the test scenario in the hopes of identifying the underlying cause. Although this may prove effective for small test cases that require minimal preparatory work, it becomes considerably more challenging to identify failures in comprehensive End-to-End tests. Consider the scenario where the test suite is executed overnight. How can one accurately reproduce the state of the system during that specific nightly run?</p>

<h1 id="log-everything">Log everything!</h1>
<p>Implement comprehensive logging in your application from the early stages of development. Ensure that log messages are included at significant points in the code, particularly where failures are anticipated. Additionally, incorporate logging at key junctures in the logic branching. If your testing involves a visual component, such as desktop, mobile, or web tests, consider adding relevant screenshots to the logs. Although organizing this sort of logging can be challenging, you can automate many aspects of the process. For further insights on AOP logging and how it can help streamline logging efforts, refer to this informative <a href="https://danny-briskin.github.io/java/2020/08/01/how-to-get-rid-of-boilerplate-code-adding-more-flexibility-with-aop.html">article</a>.</p>

<h1 id="-but-be-flexible-">… but be flexible …</h1>
<p>In contemporary logging frameworks, there is an abundance of configuration options available to establish logging setups. This allows for precise control over what information is logged, when it is logged, how it is logged, and where it is logged. It is essential to ensure that the logs encompass all the necessary information required for investigating potential failures. You can customize and define your own mnemonic codes to represent specific events, facilitating easier interpretation. For instance:</p>

<figure class="highlight"><pre><code class="language-log" data-lang="log">[oo] Starting scenario [Scenario Name]
[&gt;&gt;] ClassName::methodName1(parameterValue1, (ParameterClass2))
[&lt;&lt;] ClassName::methodName1()
[&gt;&gt;] ClassName::methodName2(parameterValue3)
[* ] custom log message
[o&lt;] [returnedValue] &lt;== ClassName::methodName2()
[&gt;&lt;] Finishing scenario [Scenario Name]</code></pre></figure>

<p>In the above example, “[oo]” indicates the starting point, “[&gt;&lt;]” - the exit point, “[»]” and “[«]” - method calls and exits respectively, while “[o&lt;]” is for returned value.</p>

<p>Additionally, it is imperative to include the precise date and time of each log event. The log level can also be of great assistance. For instance:</p>

<figure class="highlight"><pre><code class="language-log" data-lang="log">D 20230221050902892    </code></pre></figure>

<p>In this example, “D” denotes the log level “DEBUG”, which was used, and the date is formatted as “YYYYMMDDHH24MISSZZZ”. This format proves highly useful when sorting dates in a natural manner.</p>

<h1 id="-do-level-it-up-">… do level it up …</h1>
<p>Furthermore, it is essential to consider log levels, as they play a crucial role in logging. Implementing a dynamic logging level system allows the code to generate different outputs in the logs based on the specified level. For instance:</p>

<figure class="highlight"><pre><code class="language-log" data-lang="log">TRACE 20230221050902892 [&gt;&gt;] ClassName::methodName1(parameterValue1, (ParameterClass2))
DEBUG 20230221050902892 [o&lt;] [returnedValue] &lt;== ClassName::methodName2() 
WARN  20230221050902892 [* ] [returnedValue] is negative!
ERROR 20230221050402892 [* ] [returnedValue] does not meet given criteria (-3&lt;=value&lt;=0)!
CRIT  20230221050902892 [* ] browser quits unexpectedly</code></pre></figure>

<p>If the current log level is set to WARN, only messages with WARN level or higher (ERROR and CRIT) will be displayed.</p>

<p>It is also considered good practice to pass thelog level from outside the application.</p>

<h1 id="-because-it-is-expensive-sometimes">… because it is expensive sometimes.</h1>
<p>Once logging is integrated into the application, the process does not end there. It is crucial to actively monitor the logs over a specified period. Questions such as the number of lines generated per day and the continued necessity of this information after a certain period (e.g., a week or a month) need to be addressed. Additionally, it is vital to evaluate the performance impact of logging. For example, introducing logging at a specific point may result in a doubling of execution time and the generation of an additional 100MB of logs per day.</p>

<p>Most logging frameworks offer features to automatically archive or manage old logs, and it is important not to overlook these capabilities.</p>

<h1 id="summary">Summary</h1>
<p>Incorporating logs into your initial architecture can offer significant benefits by saving substantial time and effort required to investigate the underlying cause of test framework failures. Establishing an efficient log setup can prove invaluable, ensuring comprehensive visibility into the test execution process. By making logs an integral component of your framework design, you can enhance troubleshooting capabilities and reap the favorable outcomes that result from this proactive approach.</p>]]></content><author><name></name></author><category term="testing" /><category term="testing," /><category term="logging" /><summary type="html"><![CDATA[Danny Briskin, Quality Engineering Practice Manager]]></summary></entry><entry><title type="html">Test Automation in multiple versions and environments</title><link href="https://danny-briskin.github.io//testing/2023/06/02/testing-in-multi-environment.html" rel="alternate" type="text/html" title="Test Automation in multiple versions and environments" /><published>2023-06-02T18:40:30+00:00</published><updated>2023-06-02T18:40:30+00:00</updated><id>https://danny-briskin.github.io//testing/2023/06/02/testing-in-multi-environment</id><content type="html" xml:base="https://danny-briskin.github.io//testing/2023/06/02/testing-in-multi-environment.html"><![CDATA[<p><img src="/images/rest_hypermedia.jpg" alt="" />
Danny Briskin, Quality Engineering Practice Manager</p>

<h1 id="the-issue">The issue</h1>
<p>In software development projects, it is customary to establish various distinct environments to cater to different objectives. Typically, these include a designated development environment (DEV), a dedicated testing environment (QA), and naturally, a production environment (PROD). In more intricate scenarios, additional configurations such as User Acceptance Testing (UAT) and System Integration Testing (SIT) environments are incorporated. Moreover, in cases where a significant volume of testing activities is involved, it is common to have multiple instances of each environment type</p>

<p>In a normal deployment workflow, new versions of software are deployed in the order:</p>
<ol>
  <li>DEV</li>
  <li>SIT</li>
  <li>QA</li>
  <li>UAT</li>
  <li>PROD</li>
</ol>

<p>Consequently, the PROD environment may have version 1.3 in PROD, while UAT has 1.4, QA - 1.5, SIT - 1.6 and DEV 2.0. Although the version numbers may vary, and there can be more significant gaps between them, the underlying concept remains consistent. Once testing is conducted in a given environment, its respective version is promoted to the subsequent environment, replacing the current version with a “ready-to-deploy” update from the lower environment.</p>

<p>However, certain challenges can arise within this scheme. Testers, including end users, may identify defects within any environment, prompting developers to swiftly generate a hotfix that should be tested as well. Nonetheless, difficulties emerge when ongoing testing is underway in an environment that cannot be interrupted to facilitate the deployment of an updated version. Consequently, the fixed version will need to be deployed to another environment. If there is a scarcity of available environments, a chaotic situation can ensue.</p>

<p>For instance: Suppose a defect is found in the PROD (ver. 1.3), prompting the development team to create version 1.3.1 with the necessary bug fix. But there is an ongoing testing of ver. 1.4 in SIT, so let’s deploy it in QA. Meanwhile, another defect was found in UAT (ver. 1.4), leading the developers to produce version 1.4.1 (that does not include bugfix from 1.3.). The updated version is then deployed back to the UAT environment. At this point, the QA team completes their testing and prepares to deploy version 1.3.1 to UAT, but encounters the ongoing testing of version 1.4.1. Consequently, the SIT environment becomes available, and the decision is made to deploy version 1.3.1 there temporarily, mimicking a UAT environment.</p>

<p>Now we have:</p>
<ol>
  <li>DEV (ver. 2.0)</li>
  <li>SIT (ver. 1.4.1)</li>
  <li>QA (ver. 1.3.1)</li>
  <li>UAT (ver. 1.4)</li>
  <li>PROD (ver. 1.3)</li>
</ol>

<h1 id="wearing-a-test-automation-hat">Wearing a test automation hat</h1>
<p>Let’s consider the scenario where you possess a test automation framework with a comprehensive regression suite, prepared for deployment at any given moment. Being a proficient software developer [in test], you are well-versed in utilizing version control systems. Naturally, you maintain a master (main) branch, housing the most up-to-date codebase. When making modifications to the framework, you adhere to version control guidelines by creating a branch, committing changes, pushing them to the repository, and ultimately generating a Pull Request to merge the changes into the master branch.
Well done!</p>

<p>Assuming that you have made the necessary adjustments to your tests to align with the new bug fixes introduced in versions 1.3.1 and 1.4.1, you are now prepared to execute the regression suite. Typically, the regression suite is executed against the QA environment (1.3.1). Therefore, let’s clone the latest version from the master branch and execute it. However, a predicament arises concerning the changes made for versions 1.4.1, as they have not been incorporated yet. Consequently, a temporary solution entails temporarily removing those specific changes from our automation framework. Voila! It works as expected!</p>

<p>Nevertheless, we now encounter a situation where testing needs to be conducted on a version deployed in the SIT environment (1.4.1). Consequently, we must reintegrate those previously removed changes back into our framework. Naturally, if we aim to uphold version control practices, all these application and rollback processes should be executed through the Branch-Commit-Push-PullRequest sequence.</p>

<p>Consider the complexity that may arise when dealing with additional environments or larger version gaps, as the number of changes between versions increases.</p>

<h1 id="how-to-ease-this-pain">How to ease this pain</h1>
<p>Let’s implement a solution that involves maintaining multiple master branches within the repository. While technically only one master branch can exist, we can adapt our branching policy to accommodate this approach. We can establish an agreement where the initial master branch solely contains the latest code. For each version of the system under test, particularly major versions, we will create corresponding branches named “branch-X.Y.Z,” where X.Y.Z represents the version number.</p>

<p>Whenever a change is made for a specific version, such as 1.3.1, that is not the latest version (assuming the latest version in the given example is 2.0.0), we will push that change to the following branches:</p>
<ol>
  <li>The “branch-1.3.1” branch.</li>
  <li>All higher version branches where the change is currently applicable or will be applicable in the future. However, the decision to apply the change to the “branch-1.4.1” branch, for instance, will depend on specific considerations. Nevertheless, the change will be applied to the “branch-1.5,” “branch-2.0,” and “master” branches.</li>
</ol>

<p>Implementing the second aspect of this approach is complex and necessitates comprehensive knowledge of the system under test, the automation framework, and the project’s processes. However, investing time and effort into implementing this solution is highly beneficial.</p>

<p>By adopting this approach, you will have the flexibility to launch any regression suite against any version of the system under test in any environment, either individually or simultaneously across multiple versions.</p>

<h1 id="summary">Summary</h1>
<p>Implementing and maintaining multiple master branches in version control can be a challenging undertaking. It requires significant time and effort, and may initially appear impractical. Therefore, I do not recommend adopting this approach right from the outset. It is crucial to first understand the types and quantity of environments involved, determine which environments are necessary for running automation tests, comprehend the versioning structure in the project, and become familiar with the existing deployment guidelines and workflows. Only if the landscape proves to be exceptionally complex should you consider transitioning to the recommended multi master-branch scheme. Otherwise, it is advisable to keep the version control approach simple and straightforward.</p>]]></content><author><name></name></author><category term="testing" /><category term="testing," /><category term="git," /><category term="branches," /><category term="environments," /><category term="versions" /><summary type="html"><![CDATA[Danny Briskin, Quality Engineering Practice Manager]]></summary></entry><entry><title type="html">Testing web pages with Shadow DOM</title><link href="https://danny-briskin.github.io//web/testing/2023/04/26/testing-shadow-roots.html" rel="alternate" type="text/html" title="Testing web pages with Shadow DOM" /><published>2023-04-26T18:40:30+00:00</published><updated>2023-04-26T18:40:30+00:00</updated><id>https://danny-briskin.github.io//web/testing/2023/04/26/testing-shadow-roots</id><content type="html" xml:base="https://danny-briskin.github.io//web/testing/2023/04/26/testing-shadow-roots.html"><![CDATA[<p><img src="/images/performance_testing.jpg" alt="" />
Danny Briskin, Quality Engineering Practice Manager</p>

<h1 id="the-issue">The issue</h1>
<p>According to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM">MDN</a>, Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree - this shadow DOM tree starts with a shadow root, underneath which you can attach any element, in the same way as the normal DOM.
It looks like a contemporary implementation of old-time frames. The only difference is that frames HTML code is a fully qualified HTML, with all required html tags, while shadow DOM can start with literally anything.
What does it mean for QA automation engineers? 
First, <a href="https://www.w3schools.com/xml/xpath_syntax.asp">XPath</a> stops working in developer tools section of your browser. It definitely adds a headache when you are trying to find an element you need. What’s more, there can be no visible connection between elements. For example, you can find a combo box that is implemented with “div” elements like:</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"select-wrapper"</span> <span class="na">id=</span><span class="s">"my-shadow-root"</span><span class="nt">&gt;&lt;/div&gt;</span></code></pre></figure>

<p><img src="/images/shadow_root_02.png" alt="" /></p>

<p>There are no items in it but when you click on it, some items are populated. Sure, there was a JavaScript and CSS code that produced options in runtime. Some developers are skilled enough and make it appears inside a pseudo-select tag (i.e. top level div), like this:</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"select-wrapper"</span> <span class="na">id=</span><span class="s">"my-shadow-root"</span><span class="nt">&gt;</span>
  #shadow-root  
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"select"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"select__trigger"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;span&gt;</span>Option 1<span class="nt">&lt;/span&gt;</span>
          <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"arrow"</span><span class="nt">&gt;&lt;/div&gt;</span>
        <span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"custom-options"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"custom-option selected"</span><span class="nt">&gt;</span>Option 1<span class="nt">&lt;/span&gt;</span>
          <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"custom-option"</span><span class="nt">&gt;</span>Option 2<span class="nt">&lt;/span&gt;</span>
          <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"custom-option"</span><span class="nt">&gt;</span>Option 3<span class="nt">&lt;/span&gt;</span>
        <span class="nt">&lt;/div&gt;</span>
      <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;/div&gt;</span></code></pre></figure>

<p><img src="/images/shadow_root_03.png" alt="" />
You can see it visually, but it’s not easy to search for it programmatically.</p>

<h1 id="xpath-fails">XPath fails</h1>
<p>Let’s try to search for it using XPath:</p>

<figure class="highlight"><pre><code class="language-xpath" data-lang="xpath"><span class="o">//</span><span class="nt">span</span><span class="p">[</span><span class="na">@class</span><span class="o">=</span><span class="s">'custom-option'</span><span class="p">]</span></code></pre></figure>

<p>Oops, nothing… 
<img src="/images/shadow_root_04.png" alt="" />
But it is there! XPath will not work inside a #shadow-root. What a pity…
<img src="/images/shadow_root_01.png" alt="" /></p>

<h1 id="selenium-can-do-it">Selenium can do it!</h1>
<p>Well, pure JavaScript can do it too, and Selenium uses those methods to achieve the same goal.
Let’s first replicate that unsuccessful search with Selenium:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">webDriver</span><span class="o">.</span><span class="na">findElement</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">xpath</span><span class="o">(</span><span class="s">"//span[@class='custom-option']"</span><span class="o">))</span></code></pre></figure>

<p>The same result, no wonder
<img src="/images/shadow_root_05.png" alt="" /></p>

<p>Let’s locate the div that contains that shadow-root element:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">webDriver</span><span class="o">.</span><span class="na">findElement</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">id</span><span class="o">(</span><span class="s">"my-shadow-root"</span><span class="o">))</span></code></pre></figure>

<p>At least that one is found.
<img src="/images/shadow_root_06.png" alt="" /></p>

<p>There is a method, called “getShadowRoot()” that can give you a root element of the inner structure:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">webDriver</span><span class="o">.</span><span class="na">findElement</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">id</span><span class="o">(</span><span class="s">"my-shadow-root"</span><span class="o">))).</span><span class="na">getShadowRoot</span><span class="o">()</span></code></pre></figure>

<p>Now, we can continue our search as we are “inside” a regular WebElement.</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">webDriver</span><span class="o">.</span><span class="na">findElement</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">id</span><span class="o">(</span><span class="s">"my-shadow-root"</span><span class="o">)).</span><span class="na">getShadowRoot</span><span class="o">().</span><span class="na">findElements</span><span class="o">(</span><span class="nc">By</span><span class="o">.</span><span class="na">className</span><span class="o">(</span><span class="s">"custom-option"</span><span class="o">))</span></code></pre></figure>

<p><img src="/images/shadow_root_07.png" alt="" /></p>

<p>Pay attention, that not all “By.*” methods work fine inside a shadow-root. For the moment (Apr’2023), only by class and by id are working.</p>

<h1 id="a-worst-case-scenario">A worst-case scenario</h1>
<p>There are a lot of modern-day frameworks like Angular and ReactJS, that require a few developers’ work and can do a lot of magic by itself. So, it is quite possible, if you collaborate with a regular developer with a powerful framework in hands, you will see still empty combo box and this code somewhere in the DOM:</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"select-wrapper"</span> <span class="na">id=</span><span class="s">"my-shadow-root"</span><span class="nt">&gt;&lt;/div&gt;</span>

........

<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"zxc123po"</span><span class="nt">&gt;</span>
  #shadow-root  
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"select"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"select__trigger"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;span&gt;</span>Option 1<span class="nt">&lt;/span&gt;</span>
          <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"arrow"</span><span class="nt">&gt;&lt;/div&gt;</span>
        <span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"custom-options"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"custom-option selected"</span><span class="nt">&gt;</span>Option 1<span class="nt">&lt;/span&gt;</span>
          <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"custom-option"</span><span class="nt">&gt;</span>Option 2<span class="nt">&lt;/span&gt;</span>
          <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"custom-option"</span><span class="nt">&gt;</span>Option 3<span class="nt">&lt;/span&gt;</span>
        <span class="nt">&lt;/div&gt;</span>
      <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;/div&gt;</span></code></pre></figure>

<p>How to link those options with your combo box element (keeping in mind that class name is a random, obfuscated string)? The answer is: “Sorry, but no way”. 
The only way is to explain to the developer what Software Testability is, and ask them to change their code in order to have a reliable way of identifiying elements.</p>

<h1 id="summary">Summary</h1>
<p>I believe that sometime, all browsers will have a support of XPath looking inside shadow DOMs and Selenium will introduce a more natural way of working with those elements.</p>]]></content><author><name></name></author><category term="web" /><category term="testing" /><category term="xpath," /><category term="testing," /><category term="web" /><category term="ui," /><category term="shadow" /><category term="dom" /><summary type="html"><![CDATA[Danny Briskin, Quality Engineering Practice Manager]]></summary></entry></feed>