<?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: Pablo Calvo</title>
    <description>The latest articles on DEV Community by Pablo Calvo (@pjcalvo).</description>
    <link>https://dev.to/pjcalvo</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%2F302185%2Fc2110187-6003-4a81-a687-79d69d453c95.jpeg</url>
      <title>DEV Community: Pablo Calvo</title>
      <link>https://dev.to/pjcalvo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pjcalvo"/>
    <language>en</language>
    <item>
      <title>3 Successful Test Runs Is the Magic Number</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Tue, 30 Jul 2024 20:58:16 +0000</pubDate>
      <link>https://dev.to/pjcalvo/3-successful-test-runs-is-the-magic-number-264m</link>
      <guid>https://dev.to/pjcalvo/3-successful-test-runs-is-the-magic-number-264m</guid>
      <description>&lt;p&gt;&lt;strong&gt;(But Is It Really?)&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;As developers and testers, we've all been there: the excitement of seeing that green checkmark after a successful test run. It feels like a job well done, and the temptation to move on is strong. However, I’m here to tell you that one successful test run doesn't mean your work is finished. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Pitfall of One Successful Test Run&lt;/strong&gt;&lt;br&gt;
Relying on a single successful test run to validate your code is a risky practice. A myriad of issues can arise that only become apparent with repeated testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Unstable Environments: Minor fluctuations in your testing environment can cause a test to pass once but fail subsequently. This could be due to network instability, server loads, or other environmental factors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Changes: The test data might change, causing subsequent runs to fail. For instance, if your test relies on a particular data set, any alterations can lead to inconsistencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configuration Variations: Sometimes, different configurations or slight changes in the deployment settings can affect the test outcome. Ensuring your test runs successfully across multiple configurations is crucial.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Three Test Runs?&lt;/strong&gt;&lt;br&gt;
So, why do some advocate for three successful test runs? The idea is to introduce a buffer against the aforementioned issues. If your code and tests are robust enough to pass three consecutive times, it’s less likely that flukes or transient issues are at play.&lt;/p&gt;

&lt;p&gt;However, while three successful test runs can provide a higher degree of confidence, they should not be seen as a definitive measure of success. It's essential to recognize that the number three is somewhat arbitrary and not inherently magical. The key takeaway should be the principle behind it: consistency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The True Measure of Success: Consistent Stability&lt;/strong&gt;&lt;br&gt;
To ensure your code is genuinely ready, focus on the consistent and repeatable success of your tests. Here are some best practices to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Automated Testing Pipelines: Integrate your tests into automated pipelines that run tests multiple times across different environments and configurations. This helps catch issues that might only appear under specific conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuous Integration/Continuous Deployment (CI/CD): Implement CI/CD practices to ensure that tests are run frequently, ideally with every code change. This continuous feedback loop helps identify issues early.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comprehensive Test Coverage: Ensure your tests cover a wide range of scenarios, including edge cases and potential failure points. This reduces the likelihood of unforeseen issues cropping up in production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitoring and Alerts: Post-deployment monitoring can help catch issues that might not have been apparent during testing. Set up alerts for any anomalies or errors in the live environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In summary, don’t fall into the trap of believing in a magic number. Instead, aim for the consistent reliability of your tests to ensure your code is truly ready for prime time. Happy testing!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>playwright</category>
    </item>
    <item>
      <title>UI Test Automation Framework proposal with SeleniumBase!</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Tue, 08 Sep 2020 17:30:12 +0000</pubDate>
      <link>https://dev.to/pjcalvo/ui-test-automation-framework-proposal-4oh5</link>
      <guid>https://dev.to/pjcalvo/ui-test-automation-framework-proposal-4oh5</guid>
      <description>&lt;p&gt;Considering to use &lt;a href="https://seleniumbase.io/" rel="noopener noreferrer"&gt;Seleniumbase.io&lt;/a&gt; for my next web automation framework I decided to come up with an initial architecture diagram that illustrates the integration of the framework with the rest of components. Using &lt;code&gt;Python&lt;/code&gt; &lt;code&gt;Webdriver (SeleniumBase)&lt;/code&gt; and &lt;code&gt;pyTest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ptrhdl75k7zhwav4grz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ptrhdl75k7zhwav4grz.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case we will be using the &lt;a href="https://martinfowler.com/bliki/PageObject.html" rel="noopener noreferrer"&gt;PageObjectModel&lt;/a&gt; which is commonly used as a first approach for web testing frameworks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn101nmflufq4hkfzdkih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn101nmflufq4hkfzdkih.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
So let's elaborate a little bit further on each of the specific components: &lt;/p&gt;

&lt;h3&gt;
  
  
  SeleniumBase
&lt;/h3&gt;

&lt;p&gt;Will serve as our default WebDriver wrapper, which will be in charge of most of the UI interactions, assertions and eventually reporting and logging. &lt;/p&gt;

&lt;h3&gt;
  
  
  Utilities/WebDriverWrapper
&lt;/h3&gt;

&lt;p&gt;Will be used to wrap those custom UI interactions that are not supported by SeleniumBase, but still will be used across many of the pages and test cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  DataWrapper
&lt;/h3&gt;

&lt;p&gt;Will centralize the test generation and serving process for all the test cases and keywords (common flows), so that we ensure that no hardcoded data will be generated at different stages of the test process.&lt;br&gt;
The data could be anywhere from api services, json files or random generated data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keywords (common flows)
&lt;/h3&gt;

&lt;p&gt;Will contain user flows or actions that need interaction with at least 2 pages objects and that is commonly used across different tests. This will help us reduce code duplicity and eventually build a repository of keywords that could function as blocks to easy build new test cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Base Page and Page Objects
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;BasePage&lt;/code&gt; will initially just be added as a placeholder in case we require it. Notice that not all the pages inherit from it, as we want to avoid the jungle inheritance problem.&lt;/p&gt;

&lt;p&gt;The page objects will follow the pattern of providing locators and user actions that are available for specific pages. &lt;br&gt;
&lt;em&gt;Note: We could eventually split the locators under their our classes, but initially that would be an overhead.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Base Test Case and Test Cases
&lt;/h3&gt;

&lt;p&gt;Finally our test cases layer. At this point is where we are going to define our test cases structure based on actions or elements delivered by each of the PageObjects. Also at this layer only is where test assertions are executed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Of course there is a lot of room for changes with this approach and we will be seeking for feedback on improvements. But overall based on lessons learned and trying to follow a single responsibility principle we think this initial architecture will let us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start writing valuable test cases soon.&lt;/li&gt;
&lt;li&gt;Avoid over engineering issues.&lt;/li&gt;
&lt;li&gt;Heavily rely on SeleniumBase to drive most of the element interactions.&lt;/li&gt;
&lt;li&gt;Be mindful about classes responsibilities and code re-usabilities problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anything else we are missing. &lt;/p&gt;

</description>
      <category>webtesting</category>
      <category>seleniumbase</category>
      <category>testing</category>
    </item>
    <item>
      <title>Problems with webDriver JavaScript clicking!</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Wed, 02 Sep 2020 18:11:41 +0000</pubDate>
      <link>https://dev.to/pjcalvo/problems-with-webdriver-javascript-clicking-4hjb</link>
      <guid>https://dev.to/pjcalvo/problems-with-webdriver-javascript-clicking-4hjb</guid>
      <description>&lt;p&gt;&lt;strong&gt;"Let's click that element using javascript... so we could avoid issues like ElementNotInteractable, or AnotherElementWillReceiveTheClick."&lt;/strong&gt; &lt;em&gt;some automation engineer&lt;/em&gt; - 2020.&lt;/p&gt;

&lt;p&gt;I have heard this one many times, and to be honest there are edge cases where it is the only way to make that click work. But I personally try to use that approach as the latest source and here is why...&lt;/p&gt;

&lt;h2&gt;
  
  
  Story
&lt;/h2&gt;

&lt;p&gt;I got a message from a customer today saying: 'Hey marketing can't save new components on the page, they are saying that there is no save button'. &lt;/p&gt;

&lt;p&gt;So I opened my email to check the automated tests and they were all Passed. 100% success. At this point I started to think that it was most likely a glitch on the system. So I started testing manually and found out that the &lt;code&gt;save icon&lt;/code&gt; was indeed missing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmrlb4fso0umgvh7kbzhh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmrlb4fso0umgvh7kbzhh.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But then.. how did my tests didn't find this issue? This is a flow that all the tests go through, that means I should have had hundreds of failures.&lt;/p&gt;

&lt;p&gt;So I opened the code and.. found the problem right away...&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;seleniumHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clickElementJavaScriptExecutor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkIcon&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;clickElementJavaScriptExecutor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WebElement&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;JavascriptExecutor&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"arguments[0].click();"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This is one of those cases where the bug is 100% test case proof, or in different words I had useless tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation
&lt;/h2&gt;

&lt;p&gt;For this specific case, the element &lt;strong&gt;does exist&lt;/strong&gt; on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjzzmgqn4mcg49t05kzeo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjzzmgqn4mcg49t05kzeo.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it was hidden, not visible for normal customers and for javascript that doesn't mean anything. The script will just trigger the click event bind to it and move on as long as the element exist. &lt;/p&gt;

&lt;p&gt;And that caused the automated flows to complete with no error, the test cases never exposed a real customer issue. After changing the javascript clicking for a normal click, all the tests started failing as expected.  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="n"&gt;checkIcon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;WebDriver error. Failed Authoring: Component: phonenumber&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Failure Reason: org.openqa.selenium.ElementNotInteractableException: element not interactable&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  End notes
&lt;/h2&gt;

&lt;p&gt;Remember that every single action on the script should behave as a validation. Clicking, typing, selecting.. if one of those fail you got yourself a problem in the website this is a continues validation approach that would help your tests being more reliable and avoid false positives. (like in my story).&lt;/p&gt;

&lt;p&gt;So using javascript to click the elements is sometimes a good idea, for example when the normal webdriver clicking doesn't seem to work, or you have a very specific UI that place components on top of each other and the clicking area is very small.&lt;/p&gt;

&lt;p&gt;But we should try to avoid it, cause it is not a real user flow.&lt;/p&gt;

&lt;p&gt;Comments?&lt;/p&gt;

&lt;p&gt;cheers :) and remember to keep using webdriver&lt;br&gt;
&lt;a href="https://pjcalvo.github.com" rel="noopener noreferrer"&gt;https://pjcalvo.github.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if you liked the post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/YZqiD0Q" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdriver</category>
      <category>selenium</category>
      <category>issues</category>
    </item>
    <item>
      <title>WebDriver vs Chrome DevTools (Speed Test)</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Sat, 20 Jun 2020 20:18:44 +0000</pubDate>
      <link>https://dev.to/pjcalvo/webdriver-vs-chrome-devtools-speed-test-441h</link>
      <guid>https://dev.to/pjcalvo/webdriver-vs-chrome-devtools-speed-test-441h</guid>
      <description>&lt;p&gt;Today I got inspired by @FedericoToledo to write a test script that can automate a simple clicking game, but with a focus on executing as fast as possible. &lt;/p&gt;


&lt;blockquote data-lang="en"&gt;
&lt;p&gt;Test automation challenge: Can you prepare a test script to win the game 1to50 automatically as fast as possible &lt;a href="https://t.co/H6mwYxeKko" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/H6mwYxeKko" rel="noopener noreferrer"&gt;https://t.co/H6mwYxeKko&lt;/a&gt;???&lt;br&gt;&lt;br&gt;My solution with Ruby and Selenium took 4.9secs to win the game and I know it can be faster &lt;a href="https://t.co/HadbaCzien" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/HadbaCzien" rel="noopener noreferrer"&gt;https://t.co/HadbaCzien&lt;/a&gt; &lt;a href="https://t.co/UrPDh2bfGX" rel="noopener noreferrer"&gt;pic.twitter.com/UrPDh2bfGX&lt;/a&gt;&lt;/p&gt;— Federico Toledo (@fltoledo) &lt;a href="https://twitter.com/fltoledo/status/1248789014632476672?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;April 11, 2020&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;This challenge inspired me to use my favorite Web Testing Framework &lt;a href="https://webdriver.io/" rel="noopener noreferrer"&gt;webdriverIO&lt;/a&gt; which support devtools and webdriver protocols out of the box with minor setup. So I decided to try and see which protocol executes faster.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's explain the details first.
&lt;/h2&gt;

&lt;p&gt;In order to make your tests scripts execute actions against a website your test framework has to use an interface which is actually capable of controlling the browser.&lt;/p&gt;

&lt;p&gt;There are 2 very popular interfaces that allow this to happen. Selenium uses &lt;strong&gt;WebDriver&lt;/strong&gt; (w3c protocol) which is today the most popular one, but there is also &lt;strong&gt;Chrome DevTools&lt;/strong&gt; which is native to chromium browsers and has some benefits and disadvantages over its competitor. In this post we are focusing on speed, and &lt;em&gt;spoiler alert&lt;/em&gt; our winner is devTools.&lt;/p&gt;
&lt;h3&gt;
  
  
  Here is why!
&lt;/h3&gt;

&lt;p&gt;WebDriver sits in the middle between the test scripts and the browser, and uses http traffic to communicate back and forth which can cause a small latency between the execution and the response from the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz9vv3vqm71k2mlqb766l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz9vv3vqm71k2mlqb766l.png" alt="Alt Text" width="756" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand Chrome DevTools allows direct control over the browser by using the &lt;a href="https://developers.google.com/web/tools/puppeteer" rel="noopener noreferrer"&gt;Puppeteer library&lt;/a&gt; which is finally used by WebDriverio. This means direct execution and less latency on the results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwq817yh0nrgu8m38gjdb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwq817yh0nrgu8m38gjdb.png" alt="Alt Text" width="518" height="161"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's do the test
&lt;/h2&gt;

&lt;p&gt;The idea of the game is to click the numbers from 1 to 50 as fast as possible, but only the next 25 are displayed. So if you click 1 then the 26 appear. &lt;a href="https://zzzscore.com/1to50/en/?ts=1592666149743" rel="noopener noreferrer"&gt;Game&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created a simple script using WebdriverIO on standalone mode so that it runs fast without all the test framework setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;remote&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webdriverio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;logLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;browserName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://zzzscore.com/1to50/en/?ts=1592666149743&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// navigate&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;//div[text()="@id"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//click each number&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.level&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`The result is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// await browser.deleteSession()&lt;/span&gt;
&lt;span class="p"&gt;})().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default webdriverIO v6 will try to connect using devtools protocol unless a explicit WebDriver path is specified.&lt;/p&gt;

&lt;p&gt;To run the script just type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &amp;lt;scriptname&amp;gt;.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  DevTools Result
&lt;/h4&gt;

&lt;p&gt;The first thing you will notice when executing the script is this line of log: &lt;code&gt;INFO webdriverio: Initiate new session using the devtools protocol&lt;/code&gt; which indicates the used protocol.&lt;/p&gt;

&lt;p&gt;And you get this result: &lt;em&gt;&lt;strong&gt;The result is 0.85&lt;/strong&gt;&lt;/em&gt;. Which is really fast, it meant at least 50 browser interactions in less than a second.&lt;/p&gt;

&lt;h4&gt;
  
  
  WebDriver Result
&lt;/h4&gt;

&lt;p&gt;In order to execute the same script on WebDriver protocol, we need to specify the &lt;code&gt;hostname&lt;/code&gt; and &lt;code&gt;path&lt;/code&gt; when creating the remote object. We also require a running webdriver server. I recommend installing the &lt;a href="https://www.npmjs.com/package/webdriver-manager" rel="noopener noreferrer"&gt;webdriver-manager&lt;/a&gt; package which simplifies the process a lot.&lt;/p&gt;

&lt;p&gt;Once the webdriver is running (by default it runs on localhost:444/wd/hub), then just update the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;logLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;browserName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/wd/hub&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again you will notice that the webdriver protocol is specified: &lt;code&gt;INFO webdriverio: Initiate new session using the webdriver protocol&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And the result is: &lt;em&gt;&lt;strong&gt;The result is 3.801&lt;/strong&gt;&lt;/em&gt; which is impressing but it is 4 times slower than the first execution and places webdriver in the second place.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;We can easily conclude that devtools protocol execute scripts way faster which makes me consider it as a scripting and scraping tool. But there is no free lunch, webdriver is now days the most used interface and the one that has support for cross browser testing, devices and even desktop apps.&lt;/p&gt;

&lt;p&gt;I will also mention the great capability of webdriverIO to support both protocols out of the box. It really matters a lot when a tool allows you the power of choice.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Credits for the protocols images to webdriverio website&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Emulate GeoLocation for Automated Testing with webdriverIO</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Fri, 01 May 2020 02:57:02 +0000</pubDate>
      <link>https://dev.to/coffeestain/emulate-geolocation-for-automated-testing-with-webdriverio-5e2e</link>
      <guid>https://dev.to/coffeestain/emulate-geolocation-for-automated-testing-with-webdriverio-5e2e</guid>
      <description>&lt;p&gt;As exposed on the &lt;a href="https://dev.to/coffeestain/emulate-geolocation-for-manual-testing-311k"&gt;first post&lt;/a&gt; of this series, Google Chrome DevTools includes a series of features that facilitate the emulation of customers &lt;em&gt;Experience Profiles&lt;/em&gt;, such as device, resolution and of course geographical location.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chrome DevTools Protocol
&lt;/h3&gt;

&lt;p&gt;Once you have mastered the art of manual testing and want to try some automated geoLocation testing, you don't have to look away looking for new tools. &lt;strong&gt;Chrome DevTools&lt;/strong&gt; exposes its own Protocol which allow us to programmatically use the same tools you know how to use on the browser.&lt;/p&gt;

&lt;h1&gt;
  
  
  Let's automate it
&lt;/h1&gt;

&lt;p&gt;For the sake of this post I decided to use &lt;a href="https://webdriver.io/docs/gettingstarted.html" rel="noopener noreferrer"&gt;webdriverIO&lt;/a&gt; to write the automated tests. The reason to use this framework is that there is 3rd party service already developed and ready to use that allow us to seamlessly use the devTools without adding any extra code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup the project
&lt;/h3&gt;

&lt;p&gt;We need to create a folder, and start a nodeJs project. (Recommended: Make sure the latest version of node is installed).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;webdriverio-test &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;webdriverio-test
&lt;span class="nv"&gt;$ &lt;/span&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Install webdriverIO CLI
&lt;/h3&gt;

&lt;p&gt;Now let's install the wdio CLI, which provide us with tools to setup and run a basic webdriverIO project.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;npm i &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @wdio/cli


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Generate a wdio config file and create a test folder
&lt;/h3&gt;

&lt;p&gt;WebdriverIO uses a configuration file (by default &lt;code&gt;wdio.conf.js&lt;/code&gt;) that contains the basic information of the framework. Once the basic setup is complete webdriverIO will look into &lt;code&gt;./test/specs&lt;/code&gt; for the test files.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;npx wdio config &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ./test/specs


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Setup devTools service
&lt;/h3&gt;

&lt;p&gt;So far we have only setup the basic default configuration for webdriverIO. On this next step we are going to include the devTools service which will provide us with capabilities to interact with the devTools protocol.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @wdio/devtools-service &lt;span class="nt"&gt;--save-dev&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then go to the &lt;code&gt;wdio.conf.js&lt;/code&gt; and add the &lt;code&gt;devtools&lt;/code&gt; service to the webdriverIO configuration.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// Services take over a specific job you don't want to take care of. They enhance&lt;/span&gt;
&lt;span class="c1"&gt;// your test setup with almost no effort. Unlike plugins, they don't add new&lt;/span&gt;
&lt;span class="c1"&gt;// commands. Instead, they hook themselves up into the test process.&lt;/span&gt;
    &lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;devtools&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;..&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;This is basically all the necessary configuration&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Now let's create some tests
&lt;/h3&gt;

&lt;p&gt;Create a file under the folder above and name it &lt;code&gt;geo.js&lt;/code&gt;. This file will contain our testing code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Scenario&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;https://www.where-am-i.net/&lt;/code&gt; and verify the current position.&lt;/li&gt;
&lt;li&gt;Use devTools to emulate a different location. (London)&lt;/li&gt;
&lt;li&gt;Verify that the user is on a new location.&lt;/li&gt;
&lt;li&gt;Again use devTools to emulate another location. (Tokyo)&lt;/li&gt;
&lt;li&gt;Repeat the verification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Read the comments in the code&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check Location overriding the timezone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check the default timezone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.where-am-i.net/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// this validation depends on where are you!&lt;/span&gt;
        &lt;span class="c1"&gt;// assert.equal(location.getText(), '19.075984, 72.877656')&lt;/span&gt;
        &lt;span class="c1"&gt;// you might get 0.0 and 0.0 the first time because of authorization issues&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check that London should be the timezone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// change to london location and timezone&lt;/span&gt;
        &lt;span class="c1"&gt;// cdp is the command to use a devTools command&lt;/span&gt;
        &lt;span class="c1"&gt;// we are going to use the Emulation module&lt;/span&gt;
        &lt;span class="c1"&gt;// and the setGeoLocationOverride method&lt;/span&gt;
        &lt;span class="c1"&gt;// info: https://chromedevtools.github.io/devtools-protocol/tot/Emulation/&lt;/span&gt;
        &lt;span class="c1"&gt;// note: the location and timezone must match or you will get an unavailable position    &lt;/span&gt;

        &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cdp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Emulation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setGeolocationOverride&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;51.507351&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.127758&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;accuracy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cdp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Emulation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setTimezoneOverride&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;timezoneId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Europe/London&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;       
        &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// wait so you can notice the map changing&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#location&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// get a location reference for validation&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;51.507351, -0.127758&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check that Tokyo should be the timezone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// change to lo Tokyo location and timezone&lt;/span&gt;
        &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cdp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Emulation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setGeolocationOverride&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;35.689487&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;139.691706&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;accuracy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cdp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Emulation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setTimezoneOverride&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;timezoneId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Asia/Tokyo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pause&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// wait so you can notice the map changing&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#location&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;35.689487, 139.691706&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Let's execute the tests
&lt;/h3&gt;

&lt;p&gt;Just run on your terminal&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;npx wdio wdio.conf.js


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd4a91y9d2y3tunzb5gws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd4a91y9d2y3tunzb5gws.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like that!! We just emulated different user locations with no need to add complicated code or any extra tooling. &lt;/p&gt;

&lt;h3&gt;
  
  
  Now what?
&lt;/h3&gt;

&lt;p&gt;Well... this example is just a very small fragment of what you actually accomplish with devTools and webdriverIO. I encourage you to explore more the documentation for both tools and discover new capabilities that can be added to your tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://webdriver.io/docs/devtools-service.html" rel="noopener noreferrer"&gt;webdriverIO devTools Service&lt;/a&gt;&lt;br&gt;
&lt;a href="https://chromedevtools.github.io/devtools-protocol/tot/Emulation/" rel="noopener noreferrer"&gt;Chrome devTools Protocol&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Share your thoughts and comments on how you test geolocation within your automated tests!!&lt;/p&gt;

</description>
      <category>webdriverio</category>
      <category>testing</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Emulate GeoLocation for Manual Testing</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Wed, 29 Apr 2020 15:54:46 +0000</pubDate>
      <link>https://dev.to/coffeestain/emulate-geolocation-for-manual-testing-311k</link>
      <guid>https://dev.to/coffeestain/emulate-geolocation-for-manual-testing-311k</guid>
      <description>&lt;p&gt;In a web world so advanced that it is almost mandatory to deliver unique &lt;em&gt;User Experiences&lt;/em&gt; to unique &lt;em&gt;Customers Profiles&lt;/em&gt;, we need to make sure our system behaves as expected under different circumstances, and those circumstances can be as simple as where my customers are located.&lt;/p&gt;

&lt;h2&gt;
  
  
  Old fashion way!
&lt;/h2&gt;

&lt;p&gt;One simple solution to test &lt;strong&gt;Geo Location&lt;/strong&gt; features is to use a VPN provider that can easily transport us to almost anywhere in the world. But as simple as the solution looks like, it also comes with several constraints: Limited to the VPN provider location points and really hard to programmatically change location on demand, and of course the monetary cost of acquiring the service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Google Chrome DevTools to the rescue!
&lt;/h2&gt;

&lt;p&gt;With google chrome devTools we can easily override the user current geo location and test our unique experience. Check it out &lt;a href="https://developers.google.com/web/tools/chrome-devtools/device-mode/geolocation" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's try it out!
&lt;/h3&gt;

&lt;p&gt;Once you have followed the previous link and know how to change the default location, let's make some tests around it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;a href="https://www.where-am-i.net/" rel="noopener noreferrer"&gt;https://www.where-am-i.net/&lt;/a&gt;, and check the current location.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhlwuoes9xg3w6ln4e64t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhlwuoes9xg3w6ln4e64t.png" alt="Alt Text" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let's override the default user location to Berlin, and notice how the map is automatically updated.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxfgrl1iq1sbgj6todadx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxfgrl1iq1sbgj6todadx.png" alt="Alt Text" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let's try one more time with a different city.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbe9ls4qjglteos5wvflt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbe9ls4qjglteos5wvflt.png" alt="Alt Text" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So far pretty awesome...&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom locations
&lt;/h3&gt;

&lt;p&gt;We can basically add anywhere in the world as a custom location, as long as the timezone matches with the geo coordinates. &lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://cloud.google.com/dataprep/docs/html/Supported-Time-Zone-Values_66194188" rel="noopener noreferrer"&gt;this list&lt;/a&gt; to find the supported timezones.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First find a timezone you want to test. i.e. &lt;code&gt;America/Costa_Rica&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Go to google maps and find the right coordinates. ..&lt;a href="http://www.google.com/maps/place/San+Jos%C3%A9,+Costa+Rica/@**9.9314988,-84.0951252**,14.76z/data" rel="noopener noreferrer"&gt;www.google.com/maps/place/San+Jos%C3%A9,+Costa+Rica/@**9.9314988,-84.0951252**,14.76z/data&lt;/a&gt;..&lt;/li&gt;
&lt;li&gt;Then add everything as a new custom location on the Sensors window.
&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqtjwt4a0f4r7czeuj9uk.png" alt="Alt Text" width="605" height="479"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As simple as that you can know use your custom timezone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb599xivrore3wv1rriaw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb599xivrore3wv1rriaw.png" alt="Alt Text" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Geo Location testing just turned very simple with Google Chrome and devTools. Of course you are limited to this specific browser and I think that the use of a VPN will be a solid complement for this kind of testing.&lt;/p&gt;

&lt;p&gt;Comment below what you do to test geo location features on your website, and any comments you have!!&lt;/p&gt;

</description>
      <category>devtools</category>
      <category>webdriverio</category>
      <category>testing</category>
    </item>
    <item>
      <title>Worst fear working on a Test Automation Project</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Wed, 15 Apr 2020 22:00:34 +0000</pubDate>
      <link>https://dev.to/pjcalvo/worst-fear-working-on-a-test-automation-project-3hkm</link>
      <guid>https://dev.to/pjcalvo/worst-fear-working-on-a-test-automation-project-3hkm</guid>
      <description>&lt;p&gt;Every coding project has embedded risks and challenges that are constantly transforming and requires proper care.  I have worked in test automation projects for more than 10 years now, I have used many languages, worked in multiple domains, being part of big and small teams and there is one thing that still scares me from time to time.&lt;/p&gt;

&lt;p&gt;And it is &lt;strong&gt;To build something useless&lt;/strong&gt; ...&lt;/p&gt;

&lt;p&gt;You can smell that something is wrong when you have been developing your test framework for over six months and it is not providing any value to the product. &lt;/p&gt;

&lt;h2&gt;
  
  
  Purposes of test automation
&lt;/h2&gt;

&lt;p&gt;I truly believe that a test automation framework serves 2 main purposes: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Provides trust and confidence that &lt;strong&gt;if no bugs are found&lt;/strong&gt; and &lt;strong&gt;no alerts have been triggered&lt;/strong&gt; your system is continuously working expected even when new features have been added. This means that your framework serves as a confidence tool and if there have been month without a single test case reporting failure then you can always feel safe.&lt;/li&gt;
&lt;li&gt;Provides details when &lt;strong&gt;something goes wrong&lt;/strong&gt; so that you and your team can analyze and take decisions based on the reporting failures. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Challenges of test automation
&lt;/h2&gt;

&lt;p&gt;There are many challenges that we can face when working on a test automation project. The tests could be flaky and report false negatives that will consume time on debugging misleading scenarios. You can also skip important validations and report false positives which will lead into production issues and the possibility of customer facing problems. You can also be limited by technology, stack and setup and have an incomplete framework that only tests partial scenarios which takes us to the previous challenge.&lt;/p&gt;

&lt;p&gt;But there is one challenge that I fear the most, and is to build a test automation framework that ends up being useless. Imagine yourself working for three month building the framework structure, you spend time adapting some cool reporting tools, and you use the very best SOLID programming principles to feel confidence about your code, but... nobody is benefiting from the tests. And here are some of the reasons why this could happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They take too long to execute.&lt;/li&gt;
&lt;li&gt;They are flaky.&lt;/li&gt;
&lt;li&gt;There is no CI/CD process within your project.&lt;/li&gt;
&lt;li&gt;There is no confidence in the tests.&lt;/li&gt;
&lt;li&gt;They are complex to execute on different environments.&lt;/li&gt;
&lt;li&gt;There are unrealistic expectations about the testing framework.&lt;/li&gt;
&lt;li&gt;Nobody know about them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to do?
&lt;/h2&gt;

&lt;p&gt;When you find yourself working on a test framework that is not providing a lot of value to anybody what you need to do is to remember that it is a &lt;strong&gt;software project&lt;/strong&gt;. So what? What I mean is that your project should have stakeholders, should have requirements and a defined release lifecycle.&lt;/p&gt;

&lt;p&gt;What you need to do is simple. Just raise your hand to the QA Team, the Dev team, the Product Owners, Project Managers and any other stakeholder that you think should be involved with the project. Ask the right questions and push for guidance, maybe the Product Owner just need one or two critical scenarios tested, while developers might not even understand the benefits of what you are working on and don't get me started with the amount of time testers don't know they could save.&lt;/p&gt;

&lt;p&gt;Don't be afraid to start over, it is always better to have &lt;strong&gt;ten initial stable test cases&lt;/strong&gt; and add coverage slowly, &lt;strong&gt;than pushing 100 flaky tests&lt;/strong&gt; which means a lot of debugging and all the trust compromised. &lt;/p&gt;

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

&lt;p&gt;It is easy to lost motivation when the work you are doing is not being appreciated, so make your self valuable and be smart about it.&lt;/p&gt;

&lt;p&gt;follow me if you need help implementing your test automation.&lt;br&gt;
&lt;a href="https://coffeestainio.github.com" rel="noopener noreferrer"&gt;https://coffeestainio.github.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/YZqiD0Q" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>process</category>
      <category>testing</category>
    </item>
    <item>
      <title>Selenium vs The World Faster Clicker</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Wed, 29 Jan 2020 16:29:53 +0000</pubDate>
      <link>https://dev.to/pjcalvo/selenium-vs-the-world-faster-clicker-3o6d</link>
      <guid>https://dev.to/pjcalvo/selenium-vs-the-world-faster-clicker-3o6d</guid>
      <description>&lt;p&gt;Today I discovered that a guy named &lt;strong&gt;Jordan Hum&lt;/strong&gt; is the world record holder for more clicks in 5 seconds, with an amazing score of &lt;strong&gt;14 CPS&lt;/strong&gt; which means 70 clicks in 5 seconds.&lt;/p&gt;

&lt;p&gt;So I went to &lt;a href="https://clickspeedtest.com/5-seconds.html" rel="noopener noreferrer"&gt;Click Speed Test&lt;/a&gt; and tried my self.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You’re a Rabbit! Well.. You Clicked with the speed of 9.6 CPS (Clicks per seconds). You made 48 Clicks in 5 Seconds&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating the clicks
&lt;/h2&gt;

&lt;p&gt;I wondered how fast can selenium click? I am pretty sure that a selenium script can beat this guy, but let's make sure finding some results.&lt;/p&gt;

&lt;p&gt;This is the script:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DRIVER_LOCATION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#driver
&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://clickspeedtest.com/5-seconds.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Start clicking&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;clicker&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# click for ever
&lt;/span&gt;  &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
  &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# until it breaks
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Time is over&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# results are slow
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_element_by_css_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.times&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Result: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;I executed it several times to find an average value, but basically this was the result:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Start clicking
Time is over
Result: 190 Clicks &lt;span class="k"&gt;in &lt;/span&gt;5 Seconds


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Let's learn something
&lt;/h2&gt;

&lt;p&gt;I wanted to have a lesson learned out of this post, so I tried and tested  a couple of myths:&lt;/p&gt;

&lt;p&gt;1) The original script finds the element once and then uses that instance to execute the clicks. But what if we try to find the element inside the loop and click it right away &lt;code&gt;driver.find_element_by_id('clicker').click()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The outcome is way slower clicking as expected: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Result: 137 Clicks &lt;span class="k"&gt;in &lt;/span&gt;5 Seconds


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

&lt;/div&gt;

&lt;p&gt;2) We can try to use a javascript executor to performs the clicks instead of the click method:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;javaScript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document.getElementById(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;clicker&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;).click();&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;javaScript&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;My lesson learned for this case is that using javascript not always trigger the click event.&lt;/p&gt;

&lt;p&gt;3) What if I use a headless browser? Does it makes any difference:&lt;/p&gt;

&lt;p&gt;The result is (surprisingly for me) an average faster clicking:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

Result: 216 Clicks &lt;span class="k"&gt;in &lt;/span&gt;5 Seconds


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

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;I executed this a lot of times and in some cases it went down to ~170, but average was mostly faster.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Maybe clicking this fast is not a normal use case for selenium, but knowing how to improve tests speed and performance can save some minutes of execution on large amounts of tests.&lt;/p&gt;

&lt;p&gt;Suggestions on what else we can test?&lt;/p&gt;

&lt;p&gt;cheers :) and remember to keep clicking&lt;br&gt;
&lt;a href="https://pjcalvo.github.com" rel="noopener noreferrer"&gt;https://pjcalvo.github.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if you liked the post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/YZqiD0Q" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-orange.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>testing</category>
    </item>
    <item>
      <title>Revamp Selenium: The power of explicit waits</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Fri, 17 Jan 2020 21:15:26 +0000</pubDate>
      <link>https://dev.to/pjcalvo/revamp-selenium-the-power-of-explicit-waits-1934</link>
      <guid>https://dev.to/pjcalvo/revamp-selenium-the-power-of-explicit-waits-1934</guid>
      <description>&lt;p&gt;Working and testing with selenium will make us very patient developers. We fill our code with a lot of waits all the time, and is natural and is expected.&lt;/p&gt;

&lt;p&gt;UI tests have a huge dependency on the external conditions of the system under tests, for example: rendering speed, internet connection and third party tools. Also sometimes we need to wait because it is how our system behaves and &lt;strong&gt;we need certain conditions to be full-filled&lt;/strong&gt; before our test can continue executing. &lt;/p&gt;

&lt;p&gt;Selenium provides 2 ways of waiting (+1 non selenium):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implicit wait: Just gives the driver a range of time to &lt;strong&gt;find an element&lt;/strong&gt; before breaking the execution. In natural words: &lt;em&gt;Hey selenium if you can't find my elements in 30 seconds please finish the test&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Explicit wait: Verifies for conditions of an element to be full-filled before breaking or continue with the execution. &lt;em&gt;Hey selenium, find my element but also make sure it has  as a class before continue&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Sleep: We sleep our execution thread, then proceed. &lt;em&gt;Hey selenium STOP . . . . . . . . please proceed.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Talking about explicit waits
&lt;/h2&gt;

&lt;p&gt;Imagine we are working with a &lt;a href="https://gcalvocr.github.io/countdown-app/" rel="noopener noreferrer"&gt;countdown website&lt;/a&gt;, and we want to make sure that the counter actually works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DRIVER_LOCATION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#driver
&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://gcalvocr.github.io/countdown-app/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**** Click on 1 Minute ****&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;btn-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# click on start 1 minute countdown
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we could be tempted to add a 60 seconds hardcoded wait and they verify that the counter is 00:00.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;countdown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00:00&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Yes, the counter worked&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Nop, The expected value is not correct&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the solution works as a charm.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9DvIZZ40--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/12dc5jx437ourpl781o6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9DvIZZ40--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/12dc5jx437ourpl781o6.png" alt="Worked" width="800" height="95"&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt;, what if it fails? We would have to wait 1 entire minute to figure it out. What if our element is not visible anymore after 2 seconds? What if the counter never started?&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's create a custom explicit wait
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;element_has_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;#python contructor
&lt;/span&gt;    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# find the element
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# if the element text is expected return true
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The code above extends the WebDriverWait class to add a new custom wait. This custom wait is going to verify constantly for the condition &lt;code&gt;if element.text == self.text&lt;/code&gt;, but will break the test only when the given time is burned down and the condition was not met. &lt;/p&gt;

&lt;p&gt;So the new code could look like this using the &lt;strong&gt;WebDriverWait&lt;/strong&gt; and the custom &lt;strong&gt;element_has_text&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**** Click on 1 Minute ****&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_element_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;btn-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# click on start 1 minute countdown
&lt;/span&gt;
&lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebDriverWait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# wait max 10 seconds
&lt;/span&gt;&lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;countdown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# element locator
&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;until&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element_has_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00:55&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**** Timer has 55 seconds left ****&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;until&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element_has_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00:45&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**** Timer has 45 seconds left ****&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;until&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element_has_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00:35&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**** Timer has 35 seconds left ****&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# this will break now
&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;until&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;element_has_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00:10&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the outcome is a sweet &lt;em&gt;selenium.common.exceptions.TimeoutException&lt;/em&gt; that we can handle as part of the test failure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4QmieH7X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ks661f3xsn2txxcq46mo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4QmieH7X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ks661f3xsn2txxcq46mo.png" alt="Sweet expection" width="800" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is quite more powerful, because it is also looking for the element and verifying a property. In case the element can't be found the test will also break.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Selenium has several built in explicit waits that will adapt to most of the use cases, so there might no be need for you to create a custom one. But I wanted to go a step further and demonstrate that you can easily extend your testing framework and adapt to your system under test.&lt;/p&gt;

&lt;p&gt;Note: I don't want to say that &lt;code&gt;sleep.wait()&lt;/code&gt; is the devil and that you should try to avoid it, but yes it is the devil and you should avoid it as much as possible.&lt;/p&gt;

&lt;p&gt;Please take your time to read the selenium documentation and reach out if you need any advice on how to improve your code quality and tests speed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://selenium-python.readthedocs.io/waits.html#implicit-waits" rel="noopener noreferrer"&gt;Waits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comments?&lt;/p&gt;

&lt;p&gt;cheers :) and remember to keep waiting&lt;br&gt;
&lt;a href="https://pjcalvo.github.com" rel="noopener noreferrer"&gt;https://pjcalvo.github.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if you liked the post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/YZqiD0Q" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Oibfu3K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>python</category>
      <category>testing</category>
    </item>
    <item>
      <title>Discussion: Challenges that we face as dev test engineers!?</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Mon, 13 Jan 2020 20:29:55 +0000</pubDate>
      <link>https://dev.to/pjcalvo/challenges-that-we-face-as-dev-test-engineers-50k9</link>
      <guid>https://dev.to/pjcalvo/challenges-that-we-face-as-dev-test-engineers-50k9</guid>
      <description>&lt;p&gt;I have worked on projects where the customer literally requests: &lt;strong&gt;We need to automate the testing for this project!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Basically, this makes me ask:&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you exactly need me to do?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What are the goals?&lt;/li&gt;
&lt;li&gt;What are the priorities?&lt;/li&gt;
&lt;li&gt;How do you need me to integrate with the SLC!?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What other challenges do you face as test engineers?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>automation</category>
      <category>testing</category>
    </item>
    <item>
      <title>Playing around with selenium and openCV!</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Thu, 09 Jan 2020 18:30:39 +0000</pubDate>
      <link>https://dev.to/coffeestain/playing-around-with-selenium-and-opencv-27h</link>
      <guid>https://dev.to/coffeestain/playing-around-with-selenium-and-opencv-27h</guid>
      <description>&lt;p&gt;&lt;strong&gt;Selenium library is frequently used for UI test automation, and was probably designed for it, but selenium itself is not a testing library and people quite often forget about it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Selenium is basically a set of tools that allow your code to interact with a browser and the html elements on it.&lt;/p&gt;

&lt;p&gt;The selenium WebDriver architecture consists in four main parts: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The language bindings (libraries for each language).&lt;/li&gt;
&lt;li&gt;The selenium server.&lt;/li&gt;
&lt;li&gt;The browser drivers.&lt;/li&gt;
&lt;li&gt;Finally, the drivers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Playing around with selenium
&lt;/h2&gt;

&lt;p&gt;Selenium is not a super fast processing tool, maybe because of the many components that need to get involved. I made a fun experiment to see how slow is really selenium by reducing the external dependencies at its minimum. &lt;/p&gt;

&lt;p&gt;For the experiment I will basically be executing clicks against a system in which no internet call is executed on each click and also there is no html rendering happening (or this does not matter that much).&lt;/p&gt;

&lt;h3&gt;
  
  
  Python and open CV
&lt;/h3&gt;

&lt;p&gt;I used python along with openCV to open an image and generate a list of pixel points for each black pixel on the given image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_coordenates&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Generating coordinates based on image&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;image2.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;black_coordinates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;#initial position
&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
            &lt;span class="c1"&gt;# find the black pixels
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
                &lt;span class="n"&gt;black_coordinates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Writing coordinates to file&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# generate a coordinates file
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COORDINATES_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;black_coordinates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Finish generating coordinates file&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Selenium and a website like &lt;code&gt;MS Paint&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Then I use selenium to go to a website like MSPaint (&lt;a href="https://kleki.com/" rel="noopener noreferrer"&gt;https://kleki.com/&lt;/a&gt;) and generate an image based on the black pixels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DRIVER_LOCATION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COORDINATES_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DRAW_WEBSITE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_element_by_css_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# move to starting point
&lt;/span&gt;            &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ActionChains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move_by_offset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STARTING_COORDINATES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;STARTING_COORDINATES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="c1"&gt;# start painting
&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Creating move actions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;xy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# split the x and y
&lt;/span&gt;                &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])}&lt;/span&gt; 
                &lt;span class="n"&gt;move_to&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                           &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

                &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move_by_offset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="c1"&gt;# finish painting
&lt;/span&gt;            &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The process
&lt;/h3&gt;

&lt;p&gt;A couple of minutes:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iBGggKZt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/y7r0qxd9fbs7glllffto.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iBGggKZt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/y7r0qxd9fbs7glllffto.png" alt="a couple of minutes" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;20 minutes:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7zn9vR1n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/sy7z9565pj9kl0t0t1wl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7zn9vR1n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/sy7z9565pj9kl0t0t1wl.png" alt="15 minutes" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;50 minutes:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wAd68Odt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/yk96h8mx0s3kjhqbcd4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wAd68Odt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/yk96h8mx0s3kjhqbcd4w.png" alt="50 minutes" width="800" height="715"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The results
&lt;/h3&gt;

&lt;p&gt;The coordinates file ended up with &lt;strong&gt;3777 rows&lt;/strong&gt;. Because for each row selenium executes 2 actions: &lt;code&gt;move_by_offset&lt;/code&gt; and &lt;code&gt;click&lt;/code&gt; it means &lt;strong&gt;7554 selenium actions&lt;/strong&gt; were be executed.&lt;/p&gt;

&lt;p&gt;The test took &lt;strong&gt;54.67 minute&lt;/strong&gt; to complete which means around &lt;strong&gt;3280 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And if we do the math: &lt;em&gt;7554 / 3280 = ~2.30&lt;/em&gt;. It means that 2.30 selenium actions were executed per second.&lt;/p&gt;

&lt;p&gt;Quite fun... &lt;/p&gt;

&lt;p&gt;Comments?&lt;/p&gt;

&lt;p&gt;cheers :) and remember to keep using selenium&lt;br&gt;
&lt;a href="https://pjcalvo.github.com" rel="noopener noreferrer"&gt;https://pjcalvo.github.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if you liked the post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/YZqiD0Q" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Oibfu3K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>python</category>
      <category>automation</category>
    </item>
    <item>
      <title>How slow is selenium?</title>
      <dc:creator>Pablo Calvo</dc:creator>
      <pubDate>Wed, 08 Jan 2020 19:42:28 +0000</pubDate>
      <link>https://dev.to/coffeestain/how-slow-are-selenium-tests-ob0</link>
      <guid>https://dev.to/coffeestain/how-slow-are-selenium-tests-ob0</guid>
      <description></description>
    </item>
  </channel>
</rss>
