<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Boni García on Medium]]></title>
        <description><![CDATA[Stories by Boni García on Medium]]></description>
        <link>https://medium.com/@boni.gg?source=rss-7118f7714c53------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*_TbY9dIa4wSFbGd9K6Cn2A.jpeg</url>
            <title>Stories by Boni García on Medium</title>
            <link>https://medium.com/@boni.gg?source=rss-7118f7714c53------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 08 Apr 2026 22:49:55 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@boni.gg/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[WebDriver BiDi: The Future of Browser Automation is Now]]></title>
            <link>https://medium.com/@boni.gg/webdriver-bidi-the-future-of-browser-automation-is-now-1ca0d5ee74dd?source=rss-7118f7714c53------2</link>
            <guid isPermaLink="false">https://medium.com/p/1ca0d5ee74dd</guid>
            <dc:creator><![CDATA[Boni García]]></dc:creator>
            <pubDate>Tue, 21 Oct 2025 06:20:01 GMT</pubDate>
            <atom:updated>2025-10-23T20:25:56.684Z</atom:updated>
            <content:encoded><![CDATA[<p>This story summarizes a talk given by <a href="https://bonigarcia.dev/">Boni García</a> at the <a href="https://www.dstb.dk/konferencer/2025">Quality Beacon conference</a> in Copenhagen, Denmark, on October 21, 2025. You can find the original slides of this talk <a href="https://bonigarcia.dev/slides/2025-Quality_Beacon-WebDriver_BiDi_The_Future_of_Browser_Automation_is_Now-v1.pdf">here</a>.</p><h3>What Is Browser Automation?</h3><p>Browser automation means controlling a web browser using code — for testing, scraping, or performing repetitive tasks. Traditionally, tools like <strong>Selenium WebDriver</strong>, <strong>Puppeteer</strong>, <strong>Cypress</strong>, and <strong>Playwright</strong> have powered this automation revolution. But each came with different architectures, protocols, and quirks. WebDriver BiDi is changing that.</p><h3>Enter WebDriver BiDi</h3><p><strong>WebDriver BiDi</strong> (short for <em>bidirectional</em>) is a <strong>W3C standard-in-progress</strong> that enables <strong>two-way, real-time communication</strong> between your automation scripts and the browser. Unlike the classic WebDriver (which used HTTP in a request-response model), BiDi uses <strong>WebSockets</strong>, allowing the browser to <em>push</em> events like network requests, console logs, or JavaScript exceptions directly to your code. In short:</p><ul><li>🦾 More reliable automation</li><li>🌐 Better browser control</li><li>📃 One unified standard</li><li>⚒ Simpler tooling and fewer hacks</li></ul><p>Specification: <a href="https://www.w3.org/TR/webdriver-bidi/">https://www.w3.org/TR/webdriver-bidi/</a></p><h4>Why WebDriver BiDi Matters</h4><p>Traditional <strong>W3C WebDriver</strong> works like a walkie-talkie: your code sends a command, waits, and gets a response. WebDriver BiDi, on the other hand, is like a <strong>live conversation</strong> — both sides talk freely. That means we can now:</p><ul><li>Capture console logs in real time.</li><li>Intercept and modify network traffic.</li><li>React instantly to browser events.</li><li>Simulate complex user input with more precision.</li></ul><p>BiDi merges two worlds:</p><ul><li>The <strong>reliability</strong> of the W3C WebDriver protocol.</li><li>The <strong>real-time capabilities</strong> of the Chrome DevTools Protocol (CDP).</li></ul><h3>Selenium</h3><p>Selenium WebDriver (often known as simply <strong>Selenium</strong>) is a multilanguage <strong>browser automation library</strong>. Selenium’s architecture is based on the <a href="https://www.w3.org/TR/webdriver2/">W3C WebDriver</a> standard, which defines a protocol for browser communication using JSON over HTTP.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/684/1*nctuDTfhL1FsBnnLEQ2KCw.png" /></figure><p>Selenium has supported BiDi since version 4, with high-level APIs coming in Selenium 5. This way, currently both WebDriver and BiDi-based automation are possible with Selenium:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YbSUSs-KPexSj91JhVsZdA.png" /></figure><p>For enabling BiDi in Selenium you need:</p><pre>@BeforeEach<br>void setup() {<br>    ChromeOptions options = new ChromeOptions();<br>    options.enableBiDi();<br>    driver = new ChromeDriver(options);<br>}</pre><p>Once enabled, you can interact with BiDi modules like BrowsingContext, Input, Network, and Log.</p><p><em>Example: Browsing Context</em></p><pre>@Test<br>void testBrowsing() {<br>    BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle());<br>    context.navigate(&quot;https://bonigarcia.dev/selenium-webdriver-java/&quot;);<br>    String screenshot = context.captureScreenshot(); // Base64 image<br>    assertThat(screenshot).isNotBlank();<br>}</pre><p><em>Example: Listening to Logs</em></p><pre>@Test<br>void testLog() {<br>    List&lt;GenericLogEntry&gt; logs = new ArrayList&lt;&gt;();<br>    try (LogInspector inspector = new LogInspector(driver)) {<br>        inspector.onConsoleEntry(logs::add);<br>    }<br>    driver.get(&quot;https://bonigarcia.dev/selenium-webdriver-java/console-logs.html&quot;);<br>    new WebDriverWait(driver, Duration.ofSeconds(5)).until(_ -&gt; logs.size() &gt; 3);<br>    logs.forEach(log -&gt; System.out.println(log.getText()));<br>}</pre><h3>Puppeteer</h3><p><strong>Puppeteer</strong> is a Node.js <strong>browser automation library</strong> created and maintained by the Chrome DevTools team at Google since 2017. <strong>Puppeteer </strong>was initially tied to CDP, now supports BiDi for Firefox as of v23 — and uses it as the <strong>default protocol</strong> from Puppeteer 24 onward.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*o4sOWK32HTYR0zYznz-cZA.png" /></figure><p><em>Example: Hello World with Puppeteer and BiDi</em></p><pre>const puppeteer = require(&#39;puppeteer&#39;);<br><br>describe(&#39;Hello World with Puppeteer and BiDi&#39;, () =&gt; {<br>  let browser, page;<br><br>  beforeAll(async () =&gt; {<br>    browser = await puppeteer.launch({<br>      browser: &#39;firefox&#39;,<br>      protocol: &#39;webDriverBiDi&#39;,<br>    });<br>    page = await browser.newPage();<br>  });<br><br>  it(&#39;checks page title&#39;, async () =&gt; {<br>    await page.goto(&#39;https://bonigarcia.dev/selenium-webdriver-java/&#39;);<br>    const title = await page.title();<br>    expect(title).toContain(&#39;Selenium WebDriver&#39;);<br>  });<br><br>  afterAll(async () =&gt; await browser.close());<br>});java</pre><h3>Cypress</h3><p><strong>Cypress</strong> is a JavaScript <strong>end-to-end automated testing framework</strong> created as a company in 2014 to provide a seamless experience for automated web testing.</p><p>As of <strong>Cypress 14.1.0 (February 2025)</strong>, Firefox automation runs over WebDriver BiDi by default. By <strong>Cypress 15 (August 2025)</strong>, CDP support in Firefox was completely dropped.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cghH-IsWXFfXuwgONvWEiQ.png" /></figure><p>Users don’t need to change a thing — BiDi runs transparently beneath the familiar Cypress API:</p><pre>describe(&#39;Hello World Cypress&#39;, () =&gt; {<br>  it(&#39;checks title&#39;, () =&gt; {<br>    cy.visit(&#39;https://bonigarcia.dev/selenium-webdriver-java/&#39;);<br>    cy.title().should(&#39;include&#39;, &#39;Selenium WebDriver&#39;);<br>  });<br>});</pre><h3>Playwright</h3><p><strong>Playwright</strong> is a multilanguage <strong>end-to-end automated testing framework </strong>maintained by Microsoft since 2020. Playwright maintains patched versions of Chromium, Firefox, and WebKit (to enable automation and cross-browser consistency). Playwright uses an extended version of CDP to implement to control uniformly across these browsers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-_SFEWfr6xE32zsaMVcVyg.png" /></figure><p><strong>Playwright</strong> is working toward BiDi integration (see <a href="https://github.com/microsoft/playwright/issues/32577">issue</a>). Although support is still <strong>experimental</strong>, the transition to WebDriver BiDi will likely happen automatically in a future version of Playwright once the protocol is mature enough to support all of Playwright’s features.</p><h3>Conclusions</h3><ul><li>WebDriver BiDi is an in-progress W3C standard for the next generation of browser automation</li><li>It combines the stability of WebDriver with the power of CDP, offering a single, standard way to automate browsers</li><li>Major tools like Selenium, Puppeteer, Cypress, and Playwright are actively integrating WebDriver BiDi</li><li>Selenium: BiDi low-level features available in Selenium 4, high-level API is in development (planned for Selenium 5)</li><li>Puppeteer: BiDi support for Firefox since v23</li><li>Cypress: BiDi support for Firefox since v14.1.0</li><li>Playwright: BiDi support is still experimental</li></ul><h4>How to track the evolution of WebDriver BiDi?</h4><ul><li><a href="https://github.com/w3c/webdriver-bidi/issues">W3C WebDriver issues</a></li><li><a href="https://github.com/w3c/webdriver-bidi/blob/main/roadmap.md">W3C WebDriver BiDi roadmap</a></li><li><a href="https://docs.google.com/spreadsheets/d/1Cg3rifrBZClIitU3aFW_WDv64gY3ge8xPtN-HE1qzrY/">W3C WebDriver BiDi planning</a></li><li><a href="https://wpt.fyi/results/webdriver/tests/bidi?label=experimental&amp;label=master&amp;aligned">WPT dashboard</a></li><li><a href="https://github.com/GoogleChromeLabs/chromium-bidi">Implementation of WebDriver BiDi for Chromium</a></li><li><a href="https://developer.chrome.com/blog/webdriver-bidi">Chrome for developers blog about BiDi</a></li><li><a href="https://wiki.mozilla.org/WebDriver/RemoteProtocol">Communication with the Firefox team about WebDriver BiDi</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1ca0d5ee74dd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[JUnit vs. TestNG: Which Framework Fits Your Testing Strategy?]]></title>
            <link>https://medium.com/@boni.gg/junit-vs-testng-which-framework-fits-your-testing-strategy-9883debedf7e?source=rss-7118f7714c53------2</link>
            <guid isPermaLink="false">https://medium.com/p/9883debedf7e</guid>
            <dc:creator><![CDATA[Boni García]]></dc:creator>
            <pubDate>Tue, 14 Oct 2025 11:07:11 GMT</pubDate>
            <atom:updated>2025-10-14T11:07:11.904Z</atom:updated>
            <content:encoded><![CDATA[<p>This story summarizes a talk given by <a href="https://bonigarcia.dev/">Boni García</a> at the <a href="https://2025.javacro.hr/eng/">JavaCro’25</a> conference in Rovinj, Croatia, on October 14, 2025. You can find the original slides of this talk <a href="https://bonigarcia.dev/slides/2025-JavaCro25-JUnit_vs_TestNG_Which_Framework_Fits_Your_Testing_Strategy-v1.pdf">here</a>.</p><h3>Introduction</h3><p>A <strong>unit testing framework</strong> is a tool that provides structure and reusable components to write, organize, and run automated tests for given pieces of code, ensuring they behave as expected. This story reviews two of Java’s most popular unit testing frameworks: <a href="https://docs.junit.org/current/user-guide/">JUnit</a> and <a href="https://testng.org/">TestNG</a>.</p><h4>JUnit</h4><p>Created by Kent Beck and Erich Gamma in 1999, <strong>JUnit </strong>quickly became the de facto testing library for Java. Its evolution is as follows:</p><ul><li>JUnit 4: widespread adoption; annotation-based model (@Test, @Before, etc.)</li><li>JUnit 5 (2017): major redesign including the JUnit Platform, the foundation of the JUnit 5 testing framework, providing the launching infrastructure for running tests and defining the API for test engines (like JUnit Jupiter or Vintage) to discover and execute tests.</li><li>JUnit 6 (2025): the latest release (September 30, 2025), supporting Java 17 and cleaning up deprecated APIs</li></ul><p><strong>TestNG</strong></p><p>Created by Cédric Beust in 2004, <strong>TestNG </strong>was designed to fix some of JUnit’s early limitations. Its key features include:</p><ul><li>Grouping and filtering via annotations</li><li>Native parallel execution</li><li>Built-in data providers for parameterized tests</li></ul><p><strong>Comparison</strong></p><p>In this story, we’ll look at seven areas side-by-side:</p><ol><li>Test lifecycle (basics)</li><li>Parameterized tests</li><li>Categorizing &amp; filtering tests</li><li>Conditional test execution</li><li>Ordering tests</li><li>Parallel execution</li><li>Advanced test lifecycle</li></ol><p>As a real use case, this comparison will be done using <a href="https://selenium.dev/">Selenium</a> to illustrate the key differences between JUnit and TestNG. <strong>Selenium is a browser automation library</strong>, not a testing framework. However, the main use case of Selenium is end-to-end automated testing. For this reason, it is typically used in conjunction with a unit testing framework like JUnit or TestNG.</p><p>This story results from the work on developing examples of two books: <a href="https://www.amazon.com/Mastering-Software-Testing-JUnit-Comprehensive-ebook/dp/B076ZQCK5Q/">Mastering Software Testing with JUnit 5</a> (Packt Publishing, 2017) and <a href="https://oreil.ly/1E7CX">Hands-On Selenium WebDriver with Java</a> (O’Reilly Media, 2022). The test examples are presented here in the following open-source repositories: <a href="https://github.com/bonigarcia/mastering-junit5">mastering-junit5</a> and <a href="https://github.com/bonigarcia/selenium-webdriver-java">selenium-webdriver-java</a>.</p><h3>1. Test lifecycle (basics)</h3><p>The <strong>test lifecycle</strong> is the sequence of steps a testing framework follows to set up the test fixture (initial state), execute the test(s), and clean up afterward. The following picture shows that the basic test lifecycle is similar in JUnit and TestNG, but with changes in the annotations’ names.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/954/1*QcSfqG9oGFNMT9_yDWH_fQ.png" /></figure><p><em>Example #1.1: basic test with Selenium — JUnit</em></p><pre>import org.openqa.selenium.WebDriver;<br>import org.openqa.selenium.chrome.ChromeDriver;<br>import org.testng.annotations.AfterMethod;<br>import org.testng.annotations.BeforeMethod;<br>import org.testng.annotations.Test;<br><br>class HelloWorldSeleniumJupiterTest {<br>     WebDriver driver;<br>     @BeforeEach<br>    void setup() {<br>        driver = new ChromeDriver();<br>    }<br>     @Test<br>    void test() {<br>        // Test logic<br>    }<br><br>    @AfterEach<br>    void teardown() {<br>        driver.quit();<br>    }<br>}</pre><p><em>Example #1.2: basic test with Selenium — TestNG</em></p><pre>import org.openqa.selenium.WebDriver;<br>import org.openqa.selenium.chrome.ChromeDriver;<br>import org.testng.annotations.AfterMethod;<br>import org.testng.annotations.BeforeMethod;<br>import org.testng.annotations.Test;<br><br>public class HelloWorldSeleniumNGTest {<br>     WebDriver driver;<br>     @BeforeMethod<br>    public void setup() {<br>        driver = new ChromeDriver();<br>    }<br>     @Test<br>    public void test() {<br>        // Test logic<br>    }<br><br>    @AfterMethod<br>    public void teardown() {<br>        driver.quit();<br>    }<br>}</pre><h3>2. Parameterized tests</h3><p>A <strong>parameterized test</strong> is a test that runs multiple times with different input values, allowing us to reuse the same logic across varied datasets.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/952/1*uP0bkwCPCIH_7kXpEXXBgA.png" /></figure><p>To implement a parameterized test in JUnit, we need to:</p><ul><li>Use @ParameterizedTest (instead of @Test) or @ParameterizedClass (in addition to @Test)</li><li>Use an argument provider (to define the dataset)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rU_NC1OF1Fnr9cUrMqevwg.png" /></figure><p>There are two ways of implementing parameterized tests in TestNG:</p><ul><li>Using @DataProvider (most common and scalable)</li><li>Using @Parameters + testng.xml (dataset in &lt;test&gt;&lt;/test&gt;)</li></ul><p><em>Example #2.1: data-driven test case with Selenium — JUnit</em></p><pre>class LoginJupiterTest extends BrowserParent {<br>     static Stream&lt;Arguments&gt; loginData() {<br>        return Stream.of(Arguments.of(&quot;user&quot;, &quot;user&quot;, &quot;Login successful&quot;),<br>                Arguments.of(&quot;bad-user&quot;, &quot;bad-passwd&quot;, &quot;Invalid credentials&quot;));<br>    }<br><br>    @ParameterizedTest<br>    @MethodSource(&quot;loginData&quot;)<br>    void testLogin(String username, String password, String expectedText) {<br>        // Test logic<br>    }<br>}</pre><p><em>Example #2.2: data-driven test case with Selenium — TestNG</em></p><pre>public class LoginNGTest extends BrowserParent {<br>     @DataProvider(name = &quot;loginData&quot;)<br>    public static Object[][] data() {<br>        return new Object[][] { { &quot;user&quot;, &quot;user&quot;, &quot;Login successful&quot; },<br>                { &quot;bad-user&quot;, &quot;bad-passwd&quot;, &quot;Invalid credentials&quot; } };<br>    }<br>     @Test(dataProvider = &quot;loginData&quot;)<br>    public void testLogin(String username, String password, String expectedText) {<br>        // Test logic<br>    }<br> }</pre><p><em>Example #3.1: cross-browser testing with Selenium — JUnit</em></p><pre>@ParameterizedClass<br>@ArgumentsSource(CrossBrowserProvider.class)<br>class CrossBrowserParent {<br>     @Parameter<br>    WebDriver driver;<br>     @AfterEach<br>    void teardown() {<br>        driver.quit();<br>    }<br> }</pre><pre>class CrossBrowserProvider implements ArgumentsProvider {<br>     @Override<br>    public Stream&lt;? extends Arguments&gt; provideArguments(<br>            ExtensionContext context) {<br>        ChromeDriver chrome = new ChromeDriver();<br>        FirefoxDriver firefox = new FirefoxDriver();<br>         return Stream.of(Arguments.of(chrome), Arguments.of(firefox));<br>    }<br> }</pre><pre>class CrossBrowserJUnitTest extends CrossBrowserParent {<br>     @Test<br>    void test() {<br>        // Test logic<br>    }<br>}</pre><p><em>Example #3.2: cross-browser testing with Selenium — TestNG</em></p><pre>public class CrossBrowserNGTest extends CrossBrowserParent {<br>     @Test(dataProvider = &quot;browserProvider&quot;)<br>    public void test(WebDriver driver) {<br>        this.driver = driver;<br>         // Test logic<br>    }<br> }</pre><pre>public class CrossBrowserParent {<br>     WebDriver driver;<br>     @DataProvider(name = &quot;browserProvider&quot;)<br>    public static Object[][] data() {<br>        ChromeDriver chrome = new ChromeDriver();<br>        FirefoxDriver firefox = new FirefoxDriver();<br>         return new Object[][] { { chrome }, { firefox } };<br>    }<br>     @AfterMethod<br>    void teardown() {<br>        driver.quit();<br>    }<br> }</pre><h3>3. Categorizing &amp; filtering tests</h3><p><strong>Categorizing and filtering</strong> allows us to group tests into categories and run only the ones that match specific criteria.</p><p>In JUnit, test classes and methods can be tagged in JUnit using @Tag. In TestNG, test methods can be grouped using the attribute groups in @Test. Those categories (tags or groups) can later be used to filter test discovery and execution.</p><p><em>Example #5.1: </em>grouping Selenium tests<em> — JUnit</em></p><pre>class CategoriesJUnitTest extends BrowserParent {<br>     @Test<br>    @Tag(&quot;WebForm&quot;)<br>    void testCategoriesWebForm() {<br>        // Test logic<br>    }<br>     @Test<br>    @Tag(&quot;HomePage&quot;)<br>    void testCategoriesHomePage() {<br>        // Test logic<br>    }<br> }</pre><pre>mvn test -Dgroups=HomePage<br><br>gradle test -Pgroups=HomePage</pre><p><em>Example #5.2: </em>grouping Selenium tests<em> — TestNG</em></p><pre>public class CategoriesNGTest extends BrowserGroupsParent {<br>     @Test(groups = { &quot;WebForm&quot; })<br>    public void testCategoriesWebForm() {<br>        // Test logic<br>    }<br>     @Test(groups = { &quot;HomePage&quot; })<br>    public void tesCategoriestHomePage() {<br>        // Test logic<br>    }<br>}</pre><pre>mvn test -Dtest=CategoriesNGTest -DexcludedGroups=HomePage<br><br>gradle test --tests CategoriesNGTest -PexcludedGroups=HomePage</pre><h3>4. Conditional test execution</h3><p><strong>Conditional test execution </strong>allows us to enable or skip tests based on predefined conditions. JUnit provides a rich set of built-in annotations for skipping tests (<a href="http://twitter.com/Disabled">@Disabled</a> and others). Also, we can use Assumptions to disable tests in runtime. TestNG provides the annotation <a href="http://twitter.com/Ignore">@Ignore</a> and attributes in <a href="http://twitter.com/Test">@Test</a> (e.g., enabled=false) to run conditionally. Also, we can use SkipException to disable tests in runtime.</p><p>The JUnit annotations for disabling tests are the following:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WGpH1isAeg5-P1txLTgiQg.png" /></figure><p><em>Example #6.1: skipping tests (I) — JUnit</em></p><pre>class DisabledJupiterTest {<br>    @Disabled(&quot;Optional reason for disabling&quot;)<br>    @Test<br>    public void testDisabled1() {<br>        // Test logic<br>    }<br>    @DisabledOnJre(JAVA_17)<br>    @Test<br>    public void testDisabled2() {<br>        // Test logic<br>    }<br>    @EnabledOnOs(MAC)<br>    @Test<br>    public void testDisabled3() {<br>        // Test logic<br>    }<br><br>}</pre><p><em>Example #6.2: skipping tests (I) — TestNG</em></p><pre>public class DisabledNGTest {<br>     @Ignore(&quot;Optional reason for disabling&quot;)<br>    @Test<br>    public void testDisabled1() {<br>        // Test logic<br>    }<br>     @Test(enabled = false)<br>    public void testDisabled2() {<br>        // Test logic<br>    }<br> }</pre><p><em>Example #7.1: skipping tests (II) — JUnit</em></p><pre>class ConditionalJupiterTest {<br>     @Test<br>    public void testConditional() {<br>        boolean condition = false; // runtime condition<br>        Assumptions.assumeTrue(condition);<br>         // Test logic<br>    }<br>}</pre><p><em>Example #7.2: skipping tests (II) — TestNG</em></p><pre>public class ConditionalNGTest {<br>     @Test<br>    public void testConditional() {<br>        boolean condition = false; // runtime condition<br>        if (!condition) {<br>            throw new SkipException(&quot;Skipping test&quot;);<br>        }<br>         // Test logic<br>    }<br> }</pre><h3>5. Ordering tests</h3><p><strong>Ordering tests </strong>is used to control the sequence in which tests are executed. The default order for test execution are:</p><ul><li>In JUnit, tests are run in an unspecified order (not guaranteed, deterministic algorithm that is but intentionally nonobvious meant to be independent)</li><li>In TestNG, tests are run in alphabetical order by method name</li></ul><p>To change this behavior:</p><ul><li>In JUnit, we use @TestMethodOrder with @Order.</li><li>TestNG, we use priority and dependsOnMethods in @Test, or class order in testng.xml</li></ul><p><em>Example #8.1: reuse the same browser to run tests in a given order — JUnit</em></p><pre>@TestInstance(Lifecycle.PER_CLASS)<br>@TestMethodOrder(OrderAnnotation.class)<br>class OrderJunitTest {<br>    WebDriver driver;<br><br>    @BeforeAll<br>    void setup() {<br>        driver = new ChromeDriver();<br>    }<br><br>    @Test<br>    @Order(1)<br>    void testA() {<br>        // Test logic<br>    }<br><br>    @Test<br>    @Order(2)<br>    void testB() {<br>        // Test logic<br>    }<br><br>    @AfterAll<br>    void teardown() {<br>        driver.quit();<br>    }<br>}</pre><p><em>Example #8.2: reuse the same browser to run tests in a given order — TestNG</em></p><pre>public class OrderNGTest {<br>    WebDriver driver;<br><br>    @BeforeClass<br>    public void setup() {<br>        driver = new ChromeDriver();<br>    }<br><br>    @Test(priority = 1)<br>    public void testA() {<br>        // Test logic<br>    }<br><br>    @Test(priority = 2)<br>    public void testB() {<br>        // Test logic<br>    }<br><br>    @AfterClass<br>    public void teardown() {<br>        driver.quit();<br>    }<br> }</pre><h3>6. Parallel execution</h3><p><strong>Parallel test execution</strong> allows us to run multiple tests simultaneously to speed up execution. JUnit provides different configuration parameters to tests in parallel:</p><ul><li>junit.jupiter.execution.parallel.enabled (to enable test parallelism)</li><li>junit.jupiter.execution.parallel.mode.classes.default (to run test classes in parallel)</li><li>junit.jupiter.execution.parallel.mode.default (to run test methods in parallel)</li></ul><p>These parameters can be specified using a configuration file or in runtime trough annotations (<a href="http://twitter.com/Execution">@Execution</a>).</p><p>On the ther hand, TestNG enables parallelism using the testng.xml config file.</p><p><em>Example #9.1: run Selenium tests in parallel — JUnit</em></p><pre>junit.jupiter.execution.parallel.enabled = true<br>junit.jupiter.execution.parallel.mode.default = concurrent<br>junit.jupiter.execution.parallel.mode.classes.default = same_thread</pre><pre>@Execution(ExecutionMode.CONCURRENT)<br>class Parallel1JupiterTest extends BrowserParent {<br>    @Test<br>    void testParallel1() {<br>        // Test logic<br>    }<br> }</pre><pre>@Execution(ExecutionMode.CONCURRENT)<br>class Parallel2JupiterTest extends BrowserParent {<br>    @Test<br>    void testParallel2() {<br>        // Test logic<br>    }<br> }</pre><p><em>Example #9.2: run Selenium tests in parallel — TestNG</em></p><pre>&lt;!DOCTYPE suite SYSTEM &quot;http://testng.org/testng-1.0.dtd&quot; &gt;<br>&lt;suite name=&quot;parallel-suite&quot; parallel=&quot;classes&quot; thread-count=&quot;2&quot;&gt;<br>    &lt;test name=&quot;parallel-tests&quot;&gt;<br>        &lt;classes&gt;<br>            &lt;class name=&quot;io.github.bonigarcia.testng.selenium.HelloWorldSeleniumNGTest&quot; /&gt;<br>            &lt;class name=&quot;io.github.bonigarcia.testng.selenium.BasicSeleniumNGTest&quot; /&gt;<br>        &lt;/classes&gt;<br>    &lt;/test&gt;<br>&lt;/suite&gt;</pre><h3>7. Advanced test lifecycle</h3><p>In JUnit 5+, the <strong>extension model </strong>provides comprehensive capabilities to customize and hook into the test lifecycle at various points.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wbL_q8kzVbpX6YV093afCQ.png" /></figure><p>The following diagram shows the JUnit 5+ test execution lifecycle and the order in which user-defined annotations and extension callbacks run. Callbacks are extension hooks, and annotations are user code methods executed around each test lifecycle.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VZ1Vy5AS8djwOsg8n6pYSw.png" /></figure><p>On the other hand, TestNG provides a rich set of listeners to intercept lifecycle events for tests and suites:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a_qjuubCnV9lJptfVwWKcw.png" /></figure><p><em>Example #10.1: retrying Selenium tests (to detect flakiness) — JUnit</em></p><pre>@ExtendWith(RetryExtension.class)<br>class RandomCalculatorJupiterTest extends BrowserParent {<br>    @Test<br>    void testRandomCalculator() {<br>        // Test logic<br>    }<br> }</pre><pre>class RandomCalculatorJupiterTest extends BrowserParent {<br><br>    @RegisterExtension<br>    Extension failureWatcher = new RetryExtension(5);<br><br>    @Test<br>    void testRandomCalculator() {<br>        // Test logic<br>    }<br> }</pre><pre>public class RetryExtension implements TestExecutionExceptionHandler {<br>    static final int DEFAULT_MAX_RETRIES = 3;<br>    final AtomicInteger retryCount = new AtomicInteger(1);<br>    final AtomicInteger maxRetries = new AtomicInteger(DEFAULT_MAX_RETRIES);<br><br>    public RetryExtension() {<br>        // Default constructor<br>    }<br><br>    public RetryExtension(int maxRetries) {<br>        this.maxRetries.set(maxRetries);<br>    }<br><br>    @Override<br>    public void handleTestExecutionException(ExtensionContext extensionContext,<br>            Throwable throwable) throws Throwable {<br>        // Manage throwable depending on the retry count<br>    }<br> }</pre><p><em>Example #10.2: retrying Selenium tests (to detect flakiness) — TestNG</em></p><pre>public class RandomCalculatorNGTest extends BrowserParent {<br>    @Test(retryAnalyzer = RetryAnalyzer.class)<br>    @Retry(5)<br>    public void testRandomCalculator() {<br>        // Test logic<br>    }<br> }</pre><pre>@Retention(RetentionPolicy.RUNTIME)<br>public @interface Retry {<br>    int value();<br>}</pre><pre>public class RetryAnalyzer implements IRetryAnalyzer {<br>    static final int DEFAULT_MAX_RETRIES = 3;<br>    final AtomicInteger retryCount = new AtomicInteger(1);<br><br>    @Override<br>    public boolean retry(ITestResult result) {<br>        Method method = result.getMethod().getConstructorOrMethod().getMethod();<br>        int maxRetries = DEFAULT_MAX_RETRIES;<br>        if (method.isAnnotationPresent(Retry.class)) {<br>            Retry retry = method.getAnnotation(Retry.class);<br>            maxRetries = retry.value();<br>        }<br>        if (retryCount.get() &lt;= maxRetries) {<br>            logError(result.getThrowable());<br>            retryCount.incrementAndGet();<br>            return true;<br>        }<br>        return false;<br>    }<br><br>    private void logError(Throwable e) {<br>        System.err.println(&quot;Attempt test execution #&quot; + retryCount.get()<br>                + &quot; failed (&quot; + e.getClass().getName() + &quot;thrown):  &quot;<br>                + e.getMessage());<br>    }<br> }</pre><p><em>Example #11.1: gather data (e.g., browser screenshot) if test fails — Java</em></p><pre>@ExtendWith(FailureWatcher.class)<br>class FailureJupiterTest extends BrowserParent {<br>    @Test<br>    void testFailure() {<br>        // Test logic<br>        fail(&quot;Forced error&quot;);<br>    }<br> }</pre><pre>public class FailureWatcher implements TestExecutionExceptionHandler {<br>    @Override<br>    public void handleTestExecutionException(ExtensionContext context,<br>            Throwable throwable) throws Throwable {<br>         context.getTestInstance().ifPresent(testInstance -&gt; {<br>            WebDriver driver = (WebDriver) SeleniumUtils<br>                    .getFieldFromTestInstance(testInstance, &quot;driver&quot;);<br>            SeleniumUtils.getScreenshotAsFile(driver, context.getDisplayName());<br>        });<br>         throw throwable;<br>    }<br>}</pre><p><em>Example #11.2: gather data (e.g., browser screenshot) if test fails — TestNG</em></p><pre>public class FailureNGTest {<br>    WebDriver driver;<br><br>    @BeforeMethod<br>    public void setup() {<br>        driver = new ChromeDriver();<br>    }<br><br>    @AfterMethod<br>    public void teardown(ITestResult result) {<br>        if (result.getStatus() == ITestResult.FAILURE) {<br>            SeleniumUtils.getScreenshotAsFile(driver, result.getName());<br>        }<br>         driver.quit();<br>    }<br><br>    @Test<br>    public void testFailure() {<br>        // Test logic<br>        fail(&quot;Forced error&quot;);<br>    }<br> }</pre><p><em>Example #12.1: reporting test suite — Java</em></p><pre>@ExtendWith(Reporter.class)<br>class Report1JupiterTest extends BrowserParent {<br>    @Test<br>    void testReport1() {<br>        // Test logic<br>    }<br> }</pre><pre>@ExtendWith(Reporter.class)<br>class Report2JupiterTest extends BrowserParent {<br>    @Test<br>    void testReport2() {<br>        // Test logic<br>    }<br> }</pre><pre>public class Reporter implements BeforeAllCallback, BeforeEachCallback,<br>        AfterTestExecutionCallback {<br>    static final String REPORT_NAME = &quot;report-junit.html&quot;;<br>    ExtentReports report;<br>    ExtentTest test;<br><br>    @Override<br>    public void beforeAll(ExtensionContext context) throws Exception {<br>        Store store = context.getRoot()<br>                .getStore(ExtensionContext.Namespace.create(STORE_NAMESPACE));<br>        report = store.get(STORE_NAME, ExtentReports.class);<br>        if (report == null) {<br>            report = new ExtentReports();<br>            store.put(STORE_NAME, report);<br>             Runtime.getRuntime().addShutdownHook(new Thread(report::flush));<br>        }<br>        ExtentSparkReporter htmlReporter = new ExtentSparkReporter(REPORT_NAME);<br>        report.attachReporter(htmlReporter);<br>    }<br><br>    @Override<br>    public void beforeEach(ExtensionContext context) throws Exception {<br>        test = report.createTest(context.getDisplayName());<br>    }<br><br>    @Override<br>    public void afterTestExecution(ExtensionContext context) throws Exception {<br>        context.getTestInstance().ifPresent(testInstance -&gt; {<br>            // Take screenshot <br>            test.addScreenCaptureFromBase64String(screenshot);<br>        });<br>    }<br> }</pre><p><em>Example #12.2: reporting test suite — TestNG</em></p><pre>@Listeners(Reporter.class)<br>public class Report1NGTest extends BrowserParent {<br>    @Test<br>    public void testReport1() {<br>        // Test logic<br>    }<br> }</pre><pre>@Listeners(Reporter.class)<br>public class Report2NGTest extends BrowserParent {<br>    @Test<br>    public void testReport2() {<br>        // Test logic<br>    }<br> }</pre><pre>public class Reporter implements ITestListener {<br>     static final String REPORT_NAME = &quot;report-testng.html&quot;;<br>    ExtentReports report;<br>    ExtentTest test;<br><br>    @Override<br>    public void onStart(ITestContext context) {<br>        ITestListener.super.onStart(context);<br>        report = new ExtentReports();<br>        ExtentSparkReporter htmlReporter = new ExtentSparkReporter(REPORT_NAME);<br>        report.attachReporter(htmlReporter);<br>    }<br><br>    @Override<br>    public void onTestStart(ITestResult result) {<br>        ITestListener.super.onTestStart(result);<br>        test = report.createTest(result.getName());<br>    }<br><br>    @Override<br>    public void onTestSuccess(ITestResult result) {<br>        ITestListener.super.onTestSuccess(result);<br>        // Take screenshot <br>        test.addScreenCaptureFromBase64String(screenshot);<br>    }<br><br>    @Override<br>    public void onFinish(ITestContext context) {<br>        ITestListener.super.onFinish(context);<br>        report.flush();<br>    }<br> }</pre><h3>Conclusions</h3><p>Both JUnit and TestNG provide a comprehensive programming model for developing advanced tests in Java. The similar aspects in JUnit and TestNG are the following:</p><ul><li>Basic test lifecycle</li><li>Categorizing and filtering tests</li><li>Ordering tests</li><li>Parallel test execution</li></ul><p>The strong points of JUnit are:</p><ul><li>Parameterized tests</li><li>Conditional test execution</li><li>Extension model</li></ul><p>The strong points in TestNG is:</p><ul><li>Test listeners</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9883debedf7e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Browser Automation with Java]]></title>
            <link>https://medium.com/@boni.gg/browser-automation-with-java-794873cdc449?source=rss-7118f7714c53------2</link>
            <guid isPermaLink="false">https://medium.com/p/794873cdc449</guid>
            <category><![CDATA[browsers]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[selenium]]></category>
            <category><![CDATA[playwrights]]></category>
            <category><![CDATA[automation]]></category>
            <dc:creator><![CDATA[Boni García]]></dc:creator>
            <pubDate>Mon, 13 Oct 2025 18:48:41 GMT</pubDate>
            <atom:updated>2025-10-13T18:48:41.466Z</atom:updated>
            <content:encoded><![CDATA[<p>Browser automation is a key ingredient for end-to-end testing. At the <a href="https://2025.javacro.hr/eng">JavaCro’25 conference</a>, Boni García delivered a presentation offering a deep dive into two of the most popular browser automation tools available today: Selenium and Playwright. This article captures the essence of that talk, providing an overview for anyone looking to get started on browser automation in the Java ecosystem. You can get the original slides of this talk <a href="https://bonigarcia.dev/slides/2025-JavaCro25-Browser_Automation_with_Java-v1.pdf">here</a>.</p><h3>What is Browser Automation?</h3><p>Browser automation is the process of using software or scripts to control a web browser and perform tasks automatically, without manual human intervention. The primary use cases for browser automation include:</p><ul><li>Test automation: This is the most common application, encompassing end-to-end testing to verify web applications.</li><li>Web scraping: Automating the extraction of large amounts of data from websites.</li><li>Automating repetitive tasks for web pages: Automating mundane tasks like filling out forms or generating reports from web interfaces.</li></ul><p>The world of browser automation is rich with tools, each with its own philosophy and strengths. Some of the key players include Selenium, Playwright, Cypress, Puppeteer, TestCafe, and WebdriverIO. This story focuses on the most prominent choices for Java developers: Selenium and Playwright.</p><h3>Selenium</h3><p>Selenium is a <strong>browser automation library</strong>, and it has been considered the de facto standard for browser automation for many years. Selenium is:</p><ul><li>Multi-language: Officially supported in Java, JavaScript, Python, .NET, and Ruby.</li><li>Cross-browser: Compatible with all major browsers like Chrome, Firefox, Safari, and Edge.</li><li>Open-source and community-driven since 2004.</li></ul><p>Selenium’s architecture is based on the <a href="https://www.w3.org/TR/webdriver2/">W3C WebDriver</a> standard, which defines a protocol for browser communication using JSON over HTTP. This standards-based approach is a key strength, ensuring broad compatibility.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YZbxs_qfFUPa_HVCO0DGxw.png" /></figure><p>It’s important to understand that <strong>Selenium is not a testing framework </strong>because it doesn’t include a test runner, assertion library, or reporting features. For this, Selenium users relies on a rich ecosystem of tools like JUnit or TestNG (unit testing frameworks), AssertJ (fluent assertions), or Allure and ExtentReports (reporting), among others.</p><h4>Selenium Manager</h4><p>A significant recent improvement is <a href="https://www.selenium.dev/documentation/selenium_manager/">Selenium Manager</a>. Shipped with Selenium out of the box, it automatically discovers, downloads, and caches the browser drivers required for automation (e.g., chromedriver for Chrome, geckodriver for Firefox, etc.), greatly simplifying project setup. Consider the following <a href="https://github.com/bonigarcia/selenium-webdriver-java/blob/master/selenium-webdriver-junit5/src/test/java/io/github/bonigarcia/webdriver/jupiter/ch02/helloworld_selenium_manager/HelloWorldFirefoxJupiterTest.java">test example</a>. Internally, Selenium is using Selenium Manager to discover, download, and cache the needed driver, geckodriver, in this example:</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import org.openqa.selenium.WebDriver;<br>import org.openqa.selenium.firefox.FirefoxDriver;<br><br>class HelloWorldFirefoxJupiterTest {<br><br>    WebDriver driver;<br><br>    @BeforeEach<br>    void setup() {<br>        driver = new FirefoxDriver();<br>    }<br><br>    @AfterEach<br>    void teardown() {<br>        driver.quit();<br>    }<br><br>    @Test<br>    void test() {<br>        driver.get(&quot;https://bonigarcia.dev/selenium-webdriver-java/&quot;);<br>        assertThat(driver.getTitle()).contains(&quot;Selenium WebDriver&quot;);<br>    }<br><br>}</pre><p>Moreover, Selenium Manager also automatically discovers, downloads, and caches the browsers driven with Selenium. For example, if Firefox is unavailable in the previous test, Selenium Manager will manage (discover, download, and cache) the latest version of Firefox. And in addition, specific browser versions can be specified as follows (including “beta”, “dev”, or “nightly”), for <a href="https://github.com/bonigarcia/selenium-examples/blob/main/src/test/java/io/github/bonigarcia/selenium/version/ChromeVersionTest.java">example</a>, as follows:</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import org.openqa.selenium.WebDriver;<br>import org.openqa.selenium.chrome.ChromeDriver;<br>import org.openqa.selenium.chrome.ChromeOptions;<br><br>class ChromeVersionTest {<br><br>    WebDriver driver;<br><br>    @BeforeEach<br>    void setup() {<br>        ChromeOptions options = new ChromeOptions();<br>        options.setBrowserVersion(&quot;beta&quot;);<br>        driver = new ChromeDriver(options);<br>    }<br><br>    @Test<br>    void test() {<br>        driver.get(&quot;https://bonigarcia.dev/selenium-webdriver-java/&quot;);<br>        String title = driver.getTitle();<br>        assertThat(title).contains(&quot;Selenium WebDriver&quot;);<br>    }<br><br>    @AfterEach<br>    void teardown() {<br>        driver.quit();<br>    }<br><br>}</pre><h4>Selenium-Java Ecosystem</h4><p>A key strength for developing end-to-end tests with Selenium and Java is the richness of their respective ecosystem. The following examples demonstrate how. For instance, to implement <strong>cross-browser testing</strong> (i.e., reuse the same test logic for different browsers), we can use the parameterized test support by JUnit, as follows (see complete example <a href="https://github.com/bonigarcia/selenium-webdriver-java/tree/master/selenium-webdriver-junit5/src/test/java/io/github/bonigarcia/webdriver/jupiter/ch08/cross_browser">here</a>):</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import org.junit.jupiter.api.Test;<br><br>class CrossBrowserTest extends CrossBrowserParent {<br><br>    @Test<br>    void test() {<br>        driver.get(&quot;https://bonigarcia.dev/selenium-webdriver-java/&quot;);<br>        assertThat(driver.getTitle()).contains(&quot;Selenium WebDriver&quot;);<br>    }<br><br>}</pre><pre>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.params.Parameter;<br>import org.junit.jupiter.params.ParameterizedClass;<br>import org.junit.jupiter.params.provider.ArgumentsSource;<br>import org.openqa.selenium.WebDriver;<br><br>@ParameterizedClass<br>@ArgumentsSource(CrossBrowserProvider.class)<br>class CrossBrowserParent {<br><br>    @Parameter<br>    WebDriver driver;<br><br>    @AfterEach<br>    void teardown() {<br>        driver.quit();<br>    }<br><br>}</pre><pre>import java.util.stream.Stream;<br>import org.junit.jupiter.api.extension.ExtensionContext;<br>import org.junit.jupiter.params.provider.Arguments;<br>import org.junit.jupiter.params.provider.ArgumentsProvider;<br>import org.openqa.selenium.chrome.ChromeDriver;<br>import org.openqa.selenium.firefox.FirefoxDriver;<br><br>public class CrossBrowserProvider implements ArgumentsProvider {<br><br>    @Override<br>    public Stream&lt;? extends Arguments&gt; provideArguments(<br>            ExtensionContext context) {<br>        ChromeDriver chrome = new ChromeDriver();<br>        FirefoxDriver firefox = new FirefoxDriver();<br><br>        return Stream.of(Arguments.of(chrome), Arguments.of(firefox));<br>    }<br><br>}</pre><p>For video recording, you can use <a href="https://bonigarcia.dev/webdrivermanager/">WebDriverManager</a>, both using browsers in Docker containers (<a href="https://github.com/bonigarcia/selenium-examples/blob/main/src/test/java/io/github/bonigarcia/selenium/wdm/docker/DockerChromeRecordingTest.java">example</a>) or by using a web extension called <a href="https://bonigarcia.dev/browserwatcher/">BrowerWatcher</a> to record only the viewport (<a href="https://github.com/bonigarcia/selenium-examples/blob/main/src/test/java/io/github/bonigarcia/selenium/wdm/watch/RecordEdgeTest.java">example</a>).</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import java.time.Duration;<br>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import org.openqa.selenium.WebDriver;<br>import io.github.bonigarcia.wdm.WebDriverManager;<br><br>class DockerChromeRecordingTest {<br><br>    WebDriver driver;<br>    WebDriverManager wdm;<br><br>    @BeforeEach<br>    void setupTest() {<br>        wdm = WebDriverManager.chromedriver().browserInDocker()<br>                .enableRecording();<br>        driver = wdm.create();<br>    }<br><br>    @Test<br>    void test() {<br>        driver.get(&quot;https://bonigarcia.dev/selenium-webdriver-java/&quot;);<br>        assertThat(driver.getTitle()).contains(&quot;Selenium WebDriver&quot;);<br>    }<br><br>    @AfterEach<br>    void teardown() throws InterruptedException {<br>        // FIXME: pause for manual browser inspection<br>        Thread.sleep(Duration.ofSeconds(3).toMillis());<br>        wdm.quit();<br>    }<br><br>}</pre><pre>import java.nio.file.Path;<br>import java.time.Duration;<br>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import org.openqa.selenium.By;<br>import org.openqa.selenium.WebDriver;<br>import org.openqa.selenium.support.ui.ExpectedConditions;<br>import org.openqa.selenium.support.ui.WebDriverWait;<br>import org.slf4j.Logger;<br>import io.github.bonigarcia.wdm.WebDriverManager;<br><br>class RecordEdgeTest {<br><br>    WebDriver driver;<br>    WebDriverManager wdm;<br><br>    @BeforeEach<br>    void setup() {<br>        wdm = WebDriverManager.edgedriver().watch();<br>        driver = wdm.create();<br>    }<br><br>    @AfterEach<br>    void teardown() {<br>        driver.quit();<br>    }<br><br>    @Test<br>    void test() {<br>        driver.get(<br>                &quot;https://bonigarcia.dev/selenium-webdriver-java/slow-calculator.html&quot;);<br><br>        wdm.startRecording();<br><br>        // 1 + 3<br>        driver.findElement(By.xpath(&quot;//span[text()=&#39;1&#39;]&quot;)).click();<br>        driver.findElement(By.xpath(&quot;//span[text()=&#39;+&#39;]&quot;)).click();<br>        driver.findElement(By.xpath(&quot;//span[text()=&#39;3&#39;]&quot;)).click();<br>        driver.findElement(By.xpath(&quot;//span[text()=&#39;=&#39;]&quot;)).click();<br><br>        // ... should be 4, wait for it<br>        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));<br>        wait.until(ExpectedConditions.textToBe(By.className(&quot;screen&quot;), &quot;4&quot;));<br><br>        wdm.stopRecording();<br><br>        Path recordingPath = wdm.getRecordingPath();<br>        assertThat(recordingPath).exists();<br>    }<br><br>}</pre><h3>Playwright</h3><p>Playwright is a newer, modern alternative from Microsoft that has quickly gained popularity. Like Selenium, it is open-source, multi-language and cross-browser. We can define Playwright as follows:</p><ul><li>For Node.js, Playwright is a <strong>end-to-end testing framework</strong>.</li><li>For Java, Python, and .NET, Playwright is a <strong>browser automation library</strong>.</li></ul><p>The difference is caused by the Playwright Test Runner (@playwright/test), a full-featured testing framework bundled with Playwright in Node.js, so it is only available for JavaScript/TypeScript developers. The Playwright Test runner provides the following features:</p><ul><li>Test runner and assertions (similar to JUnit/TestNG)</li><li>Built-in fixtures (browser/page/context lifecycle)</li><li>Parallel test execution across multiple browsers/devices</li><li>Retries mechanism</li><li>HTML reporting</li><li>Video capture when failures</li><li>API testing (built-in request fixture)</li><li>Visual comparisons (expect(page).toHaveScreenshot())</li><li>Component testing (for React/Vue/Svelte/Angular)</li></ul><p>Regarding its architecture, Playwright takes a different architectural approach than Selenium. It maintains its own patched versions of Chromium, Firefox, and WebKit and it uses internally an extended version of the Chrome DevTools Protocol (CDP) and a custom WebSocket-based protocol to control these browsers uniformly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/935/1*gaVyHEBWW5Uf-VYHaJuzsw.png" /></figure><p>This way, for creating complete end-to-end tests with Playwright and Java, we typically use the Playwright API plus other tools, such as a unit testing framework (e.g., JUnit, TestNG), reporting tools, etc. For <a href="https://github.com/bonigarcia/browser-automation-apis/blob/main/playwright/java/src/test/java/io/github/bonigarcia/playwright/basic/HelloWorldPlaywrightTest.java">example</a>:</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import com.microsoft.playwright.Browser;<br>import com.microsoft.playwright.Page;<br>import com.microsoft.playwright.Playwright;<br><br>class HelloWorldPlaywrightTest {<br><br>    Browser browser;<br>    Page page;<br><br>    @BeforeEach<br>    void setup() {<br>        browser = Playwright.create().chromium().launch();<br>        page = browser.newContext().newPage();<br>    }<br><br>    @Test<br>    void test() {<br>        page.navigate(&quot;https://bonigarcia.dev/selenium-webdriver-java/&quot;);<br>        String title = page.title();<br>        assertThat(title).contains(&quot;Selenium WebDriver&quot;);<br>    }<br><br>    @AfterEach<br>    void teardown() {<br>        browser.close();<br>    }<br><br>}</pre><h4>Advanced features</h4><p>One of the most attractive features of Playwright compared to Selenium is its built-in <strong>automatic waiting </strong>mechanism. Playwright intelligently waits for elements to be ready before performing actions, such as clicking or typing, removing the need for explicit waits. The following <a href="https://github.com/bonigarcia/browser-automation-apis/blob/main/playwright/java/src/test/java/io/github/bonigarcia/playwright/basic/SlowLoginPlaywrightTest.java">test</a> illustrates this feature:</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import java.nio.file.Paths;<br>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import com.microsoft.playwright.Browser;<br>import com.microsoft.playwright.BrowserType;<br>import com.microsoft.playwright.Page;<br>import com.microsoft.playwright.Playwright;<br><br>class SlowLoginPlaywrightTest {<br><br>    Browser browser;<br>    Page page;<br><br>    @BeforeEach<br>    void setup() {<br>        browser = Playwright.create().chromium()<br>                .launch(new BrowserType.LaunchOptions().setHeadless(false));<br>        page = browser.newContext().newPage();<br>    }<br><br>    @Test<br>    void test() {<br>        // Open system under test (SUT)<br>        page.navigate(<br>                &quot;https://bonigarcia.dev/selenium-webdriver-java/login-slow.html&quot;);<br><br>        // Log in<br>        page.fill(&quot;#username&quot;, &quot;user&quot;);<br>        page.fill(&quot;#password&quot;, &quot;user&quot;);<br>        page.click(&quot;button[type=&#39;submit&#39;]&quot;);<br><br>        // Assert expected text<br>        String successText = page.textContent(&quot;#success&quot;);<br>        assertThat(successText).contains(&quot;Login successful&quot;);<br><br>        // Take screenshot<br>        page.screenshot(new Page.ScreenshotOptions()<br>                .setPath(Paths.get(&quot;slow-login-playwright.png&quot;)));<br>    }<br><br>    @AfterEach<br>    void teardown() {<br>        browser.close();<br>    }<br><br>}</pre><p>Another appealing feature of Playwright is the <strong>trace viewer</strong>, a powerful debugging tool that lets you replay your test execution step by step. It records snapshots, network activity, console logs, and DOM states during a test run, allowing you to visually inspect what happened at each point and easily identify the cause of failures. For <a href="https://github.com/bonigarcia/browser-automation-apis/blob/main/playwright/java/src/test/java/io/github/bonigarcia/playwright/basic/SlowLoginPlaywrightTest.java">example</a>:</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import java.nio.file.Paths;<br>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import com.microsoft.playwright.Browser;<br>import com.microsoft.playwright.BrowserType;<br>import com.microsoft.playwright.Page;<br>import com.microsoft.playwright.Playwright;<br><br>class SlowLoginPlaywrightTest {<br><br>    Browser browser;<br>    Page page;<br><br>    @BeforeEach<br>    void setup() {<br>        browser = Playwright.create().chromium()<br>                .launch(new BrowserType.LaunchOptions().setHeadless(false));<br>        page = browser.newContext().newPage();<br>    }<br><br>    @Test<br>    void test() {<br>        // Open system under test (SUT)<br>        page.navigate(<br>                &quot;https://bonigarcia.dev/selenium-webdriver-java/login-slow.html&quot;);<br><br>        // Log in<br>        page.fill(&quot;#username&quot;, &quot;user&quot;);<br>        page.fill(&quot;#password&quot;, &quot;user&quot;);<br>        page.click(&quot;button[type=&#39;submit&#39;]&quot;);<br><br>        // Assert expected text<br>        String successText = page.textContent(&quot;#success&quot;);<br>        assertThat(successText).contains(&quot;Login successful&quot;);<br><br>        // Take screenshot<br>        page.screenshot(new Page.ScreenshotOptions()<br>                .setPath(Paths.get(&quot;slow-login-playwright.png&quot;)));<br>    }<br><br>    @AfterEach<br>    void teardown() {<br>        browser.close();<br>    }<br><br>}</pre><p><strong>Video recording</strong> is another built-in feature in Playwright for Java, for <a href="https://github.com/bonigarcia/browser-automation-apis/blob/main/playwright/java/src/test/java/io/github/bonigarcia/playwright/recording/RecordingSlowLoginPlaywrightTest.java">example</a>, as follows:</p><pre>import static org.assertj.core.api.Assertions.assertThat;<br>import java.nio.file.Paths;<br>import org.junit.jupiter.api.AfterEach;<br>import org.junit.jupiter.api.BeforeEach;<br>import org.junit.jupiter.api.Test;<br>import com.microsoft.playwright.Browser;<br>import com.microsoft.playwright.BrowserType;<br>import com.microsoft.playwright.Page;<br>import com.microsoft.playwright.Playwright;<br><br>class RecordingSlowLoginPlaywrightTest {<br><br>    Browser browser;<br>    Page page;<br><br>    @BeforeEach<br>    void setup() {<br>        browser = Playwright.create().chromium()<br>                .launch(new BrowserType.LaunchOptions().setHeadless(false));<br>        Browser.NewContextOptions options = new Browser.NewContextOptions()<br>                .setRecordVideoDir(Paths.get(&quot;.&quot;));<br>        page = browser.newContext(options).newPage();<br>    }<br><br>    @Test<br>    void test() {<br>        // Open system under test (SUT)<br>        page.navigate(<br>                &quot;https://bonigarcia.dev/selenium-webdriver-java/login-slow.html&quot;);<br><br>        // Log in<br>        page.fill(&quot;#username&quot;, &quot;user&quot;);<br>        page.fill(&quot;#password&quot;, &quot;user&quot;);<br>        page.click(&quot;button[type=&#39;submit&#39;]&quot;);<br><br>        // Assert expected text<br>        String successText = page.textContent(&quot;#success&quot;);<br>        assertThat(successText).contains(&quot;Login successful&quot;);<br>    }<br><br>    @AfterEach<br>    void teardown() {<br>        browser.close();<br>    }<br><br>}</pre><h3>Conclusions</h3><p>Selenium and Playwright cannot be directly compared since they are naturally different. The following table summarizes their key differences:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MCGpAR6w_AiC4jt2ljXV5Q.png" /></figure><p>Finally, we can find both pros and cons in Selenium and Playwright in Java, namely:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gehuu0jZ8xuBVqcPCqYzVw.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=794873cdc449" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WebDriverManager 6: Automated driver management and other helper features for Selenium WebDriver in…]]></title>
            <link>https://medium.com/@boni.gg/webdrivermanager-6-automated-driver-management-and-other-helper-features-for-selenium-webdriver-in-842e6f1e76d8?source=rss-7118f7714c53------2</link>
            <guid isPermaLink="false">https://medium.com/p/842e6f1e76d8</guid>
            <dc:creator><![CDATA[Boni García]]></dc:creator>
            <pubDate>Wed, 19 Mar 2025 15:50:52 GMT</pubDate>
            <atom:updated>2025-03-19T15:50:52.054Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>WebDriverManager 6: Automated driver management and other helper features for Selenium WebDriver in Java</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*0Zr49-5u6Xcub1ZZAHWcEg.png" /></figure><p><a href="https://github.com/bonigarcia/webdrivermanager/">WebDriverManager</a> was first released on 19 March 2015. At that time, Selenium was in version 2 (i.e., the first release of “Selenium WebDriver”), and it was considered a “non-batteries included” library. That means that, to control a browser programmatically with Selenium (such as Chrome or Firefox), Selenium users first should manage (i.e., download, setup, and maintain) the required drivers by Selenium (e.g., chromedriver for Chrome or geckodriver for Firefox) manually.</p><p>I found that manual process suboptimal. I used to think that, in an ideal world for browser automation, the driver management process should be automated. I looked for a solution to that, but I did not find any tool doing this. At the same time, I was working as a first-year lecturer at the university. I was chosen to give a course on “web programming.” Since Selenium was an important part of my career (I have used Selenium RC in my PhD dissertation and Selenium WebDriver in my research activities), I prepared a lecture about automated web testing using JUnit 4 and Selenium 2. But I didn’t like the driver thing. I wanted my students to go directly to the point and start playing with Selenium without wasting their energy in the driver setup. So, I created WebDriverManager 1.0.0 and recommended my students to use it.</p><p>In the following years, and thanks to the power of open source, WebDriverManager started to be used by others. I felt that WebDriverManager was addressing a real need of Selenium developers since the project began to grow, and people contributed to the WebDriverManager repo with pull requests, bug fixes, comments, etc. Moreover, the WebDriverManager concept was migrated from Java to other languages like <a href="https://www.npmjs.com/package/webdriver-manager">webdriver-manager</a> for JavaScript, <a href="https://pypi.org/project/webdriver-manager">webdriver-manager</a> for Python, or <a href="https://github.com/rosolko/WebDriverManager.Net">WebDriverManager.Net</a> for C#. This way, I have continued maintaining and incorporating more and more features to WebDriverManager in my leisure time since then. Nowadays, WebDriverManager is a well-known Java library used by hundreds of thousands of projects, with around 3 million downloads monthly in Maven Central.</p><h3><strong>WebDriverManager 6</strong></h3><p>To celebrate the 10th birthday of the project, <a href="https://bonigarcia.dev/webdrivermanager">WebDriverManager 6</a> has been released on 19 March 2025. This new major feature ships some important novelties.</p><h4>Support to docker-selenium</h4><p>One of the most relevant features shipped in WebDriverManager 5 was the ability to create browsers in Docker containers out of the box. To support this feature, WebDriverManager used the Docker images by WebDriverManager created and maintained by <a href="https://github.com/aerokube/images">Aerokube</a>. Unfortunately, these images have not been available since December 2024. Luckily, we have <a href="https://github.com/SeleniumHQ/docker-selenium">docker-selenium</a>, a project maintained by Selenium that provides Docker images for running Selenium tests in containers. I have always wanted to support docker-selenium in WebDriverManager, but the lack of time stopped me. However, the sudden unavailability of Aerokube’s images forced me to migrate to docker-selenium quickly.</p><p>Luckily, the WebDriverManager API is the same after this significant change. Appealing features, such as browser recording, noVNC access, or unstable versions (i.e., beta and dev releases), are still supported. Even better, WebDriverManager supports ARM64 Docker images with docker-selenium thanks to the <em>seleniarm </em>images.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9504357ca68db27af8e10f8ca0cb426a/href">https://medium.com/media/9504357ca68db27af8e10f8ca0cb426a/href</a></iframe><h4>Improved browser version discovery</h4><p>Browser version discovery was a key feature first introduced in WebDriverManager 3. Knowing the local browser version is key to selecting the proper driver version. To that aim, WebDriverManager has an internal knowledge database to discover browser versions using shell commands (e.g., <em>google-chrome --version</em> for Chrome in Linux).</p><p>WebDriverManager used WMIC (Windows Management Instrumentation Command-line) in Windows for browser version discovery. However, Microsoft announced that <a href="https://techcommunity.microsoft.com/t5/windows-it-pro-blog/wmi-command-line-wmic-utility-deprecation-next-steps/ba-p/4039242">WMIC is deprecated</a> as of Windows 10, version 21H1, and will be removed in a future version. Therefore, WebDriverManger 6 started to use PowerShell commands to discover browser versions. This feature is transparent for end-users, and it will guarantee that automated driver management will continue adequately in the upcoming years.</p><p>Finally, WebDriverManager 6 included a new API method called <em>.browserBinary()</em> to provide a better configuration capability regarding browser version discovery. This method allows the specification of the browser binary path used for driver version discovery.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b0fe0cd821e26087a86c125481458015/href">https://medium.com/media/b0fe0cd821e26087a86c125481458015/href</a></iframe><p>As usual, this feature can also be configured using Java properties per the different browsers: <em>wdm.chromeBinary</em>, <em>wdm.operaBinary</em>, <em>wdm.edgeBinary</em>, <em>wdm.firefoxBinary</em>, and <em>wdm.chromiumBinary</em>.</p><h4>Support to snap browsers/drivers</h4><p>Some browsers, like Firefox and Chromium, are distributed through a packaging and deployment system called <a href="https://snapcraft.io/">snap</a>. Snap packages are self-contained and sandboxed, including all the dependencies required to run the application. For browsers, that means that both the browser and the driver are included in the snap package. As of release 6, WebDriverManager can detect if the local browser has been installed using snap, using the proper driver (which should be already locally installed). Again, this feature is transparent for WebDriverManager users. If the browser and driver are available, it can be used automatically in a Selenium session managed by WebDriverManager.</p><h3><strong>Selenium Manager</strong></h3><p>But wait. What about <a href="https://www.selenium.dev/documentation/selenium_manager/">Selenium Manager</a>? Is it not the same than WebDriverManager? Let me explain the little history of Selenium Manager.</p><p>In 2021, the Selenium project published the results of the first official <a href="https://www.selenium.dev/blog/2021/selenium-survey-results/">Selenium survey</a>. One of the key findings in this study was that Selenium users wanted “batteries included.” In other words, they want Selenium to manage browsers and drivers, similarly to the features provided by WebDriverManager. This way, I joined Sauce Labs as a Staff Software Engineer in the <a href="https://opensource.saucelabs.com/">Open Source Program Office</a> from 2022 to 2023 and contributed to the Selenium project, particularly developing Selenium Manager. Selenium Manager has been developed in Rust following the lessons learned from WebDriverManager, implementing the same driver resolution algorithm first designed in WebDriverManager. Selenium Manager is fully integrated into Selenium and is used by all the official binding languages: Java, JavaScript, Python, Ruby, and .Net. As shown in its usage statistics (publicly available through <a href="https://plausible.io/manager.selenium.dev">Plausible</a>), Selenium Manager is used daily by hundreds of thousands of unique users worldwide. So, below, you can find some differences between WebDriverManager and Selenium Manager.</p><p><em>Is Selenium Manager a replacement for WebDriverManager?</em> For the use case of automated driver management, yes. In other words, if you use WebDriverManager only for driver management, you can safely switch to Selenium Manager.</p><p><em>What are the key differences between WebDriverManager and Selenium Manager? </em>Both projects provide automated driver management (chromedriver, geckodriver, etc.). However, WebDriverManager ships several unavailable features in Selenium Manager (e.g., self-managed browsers in Docker containers or custom monitoring features). On the other side, Selenium Manager provides automated browser management using the browser binary distributions for Windows, macOS, and Linux (e.g., based on Chrome for Testing).</p><p><em>What are the reasons to continue using WebDriverManager?</em> There can be different reasons, such as:</p><ul><li>Advanced features. WebDriverManager provides self-managed browsers in Docker containers (now through docker-selenium). This feature allows you to delegate all the browser infrastructure management (including dev and beta browser releases) to WebDriverManager with Docker. Also, it enables the screencasting of Selenium sessions, which can be a game-changing characteristic for failure analysis (troubleshooting). Besides, WebDriverManager provides custom monitoring features (through <a href="https://bonigarcia.dev/browserwatcher/">BrowserWatcher</a>), such as seamless console log gathering, Content Security Policy (CSP) disabling, or viewport screencasting (i.e., not record the whole desktop but only the browser viewport).</li><li>Rich configuration. One of the key aspects of WebDriverManager is that all these features can be <a href="https://bonigarcia.dev/webdrivermanager/#advanced-configuration">configured</a> through its API, using Java system properties and even environmental variables. These capabilities are very convenient for fine-tuning every feature provided by WebDriverManager, such as automated driver management, docker management, or browser monitoring.</li><li>Legacy support. The minimum version for the latest releases of Selenium 4 (i.e., those that have Selenium Manager) is Java 11. If you cannot bump to Java 11 yet (you should, but sometimes it is impossible), you can continue using WebDriverManager since even release 6 is compiled using Java 8.</li></ul><h3><strong>What’s coming next</strong></h3><p>I’m devoted to maintaining Selenium Manager and WebDriverManager in the upcoming years. Now I am part of the <a href="https://www.selenium.dev/project/structure/#tlc">Selenium Technical Leadership Committee (TLC)</a>, so my commitment is to continue improving the browser automation experience for Selenium users. Selenium Manager is still in beta but has already proven stable at this writing so that it will be released as stable in the upcoming Selenium 5. Also, in light of usage numbers, WebDriverManager is still a popular and helpful tool, so I will continue maintaining the project for the Java community.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=842e6f1e76d8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WebDriverManager 5: Automated driver management and Docker builder for Selenium WebDriver]]></title>
            <link>https://medium.com/@boni.gg/webdrivermanager-5-automated-driver-management-and-docker-builder-for-selenium-webdriver-a0a0f747a35d?source=rss-7118f7714c53------2</link>
            <guid isPermaLink="false">https://medium.com/p/a0a0f747a35d</guid>
            <category><![CDATA[test-automation]]></category>
            <category><![CDATA[selenium]]></category>
            <category><![CDATA[software-testing]]></category>
            <category><![CDATA[selenium-webdriver]]></category>
            <category><![CDATA[java]]></category>
            <dc:creator><![CDATA[Boni García]]></dc:creator>
            <pubDate>Mon, 13 Sep 2021 12:37:48 GMT</pubDate>
            <atom:updated>2025-03-19T09:35:56.506Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*gB3qBn3NKYC47F-DON_kvw.png" /></figure><p><a href="https://bonigarcia.dev/webdrivermanager/">WebDriverManager</a> is an open-source Java library that carries out the management (i.e., download, setup, and maintenance) of the drivers required by <a href="https://www.selenium.dev/documentation/webdriver/">Selenium WebDriver</a> (e.g., chromedriver, geckodriver, msedgedriver, etc.) in a fully automated manner.</p><p>WebDriverManager enables the development of portable WebDriver tests while reducing the development and maintenance efforts. For instance, the skeleton of a <a href="https://junit.org/junit5/docs/current/user-guide/">JUnit 5</a> test using Selenium WebDriver and WebDriverManager is as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fbfaef4c2161a6e116954973ca66a4f9/href">https://medium.com/media/fbfaef4c2161a6e116954973ca66a4f9/href</a></iframe><p>WebDriverManager implements a resolution algorithm for automated driver management. The foundations of this algorithm are:</p><ol><li>Browser version discovery.</li><li>Driver version match.</li><li>Driver and resolution cache.</li><li>Export driver path as system property setup.</li></ol><p>You can find all the internal details of this resolution algorithm in the paper <a href="https://link.springer.com/article/10.1007/s10664-021-09975-3">Automated driver management for Selenium WebDriver</a>, published in the Springer Journal of Empirical Software Engineering in 2021.</p><h3><strong>WebDriverManager 5: the next generation</strong></h3><p>WebDriverManager was first released in 2015. Nowadays, it is a well-known helper library for Selenium WebDriver, used in thousands of projects. WebDriverManager 5 was released in 2021. As usual, this release allows the automated driver management for Selenium WebDriver. Moreover, this release provides other features aimed to ease the development of Selenium WebDriver tests.</p><h4><strong>New documentation</strong></h4><p>The documentation has been completely rewritten in WebDriverManager 5:</p><p><a href="https://bonigarcia.dev/webdrivermanager/">https://bonigarcia.dev/webdrivermanager/</a></p><p>Internally, this site is done in <a href="https://asciidoc.org/">AsciiDoc</a>, and it is generated to <a href="https://bonigarcia.dev/webdrivermanager/">HTML</a>, <a href="https://bonigarcia.dev/webdrivermanager/webdrivermanager.pdf">PDF</a>, and <a href="https://bonigarcia.dev/webdrivermanager/webdrivermanager.epub">EPUB</a>.</p><h4><strong>Browser finder</strong></h4><p>As of version 5, WebDriverManager allows detecting if a given browser is installed or not in the local system. To this aim, each manager provides the method <em>getBrowserPath()</em>. This method returns an <em>Optional&lt;Path&gt;</em>, which is empty if a given browser is not installed in the system or the browser path (within the optional object) when detected. You can use this feature to skip tests using assumptions, for instance, as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a22bbc69928204c80e09fbd1c44640ea/href">https://medium.com/media/a22bbc69928204c80e09fbd1c44640ea/href</a></iframe><h4><strong>WebDriver builder</strong></h4><p>WebDriverManager 5 also allows instantiating WebDriver objects (e.g., ChromeDriver, FirefoxDriver, etc.) using the WebDriverManager API. This feature is available using the method <em>create()</em> of each manager. For instance:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/beaa816114e2edb33a16ff1cd97ab787/href">https://medium.com/media/beaa816114e2edb33a16ff1cd97ab787/href</a></iframe><h4><strong>Browsers in Docker</strong></h4><p>Another relevant new feature available in WebDriverManager 5 is the ability to create browsers in Docker containers out of the box. To use it, we need to invoke the method <em>browserInDocker()</em> in conjunction with <em>create()</em> of a given manager. For instance:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7693a671b904b2126b5d7a647fdb8557/href">https://medium.com/media/7693a671b904b2126b5d7a647fdb8557/href</a></iframe><p>The usfed Docker images by WebDriverManager have been created and maintained by <a href="https://aerokube.com/images/latest/">Aerokube</a>. Therefore, Chrome (desktop and mobile), Firefox, Edge, Opera, and Safari (WebKit engine) are the available browsers to be executed as Docker containers in WebDriverManager. In addition, we can use the beta and development versions of Chrome and Firefox, thanks to a fork of the Aerokube images maintained by <a href="https://hub.docker.com/r/twilio/selenoid/">Twilio</a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bc6937a4f6ca3c91618b803b977abd83/href">https://medium.com/media/bc6937a4f6ca3c91618b803b977abd83/href</a></iframe><p>WebDriverManager allows connecting to the remote desktop session simply invoking the method <em>enableVnc()</em> of a dockerized browser. In addition, we can use the method <em>enableRecording()</em> to record the browser session.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/dd7aad46649200489218514539e0f2ef/href">https://medium.com/media/dd7aad46649200489218514539e0f2ef/href</a></iframe><h4><strong>Other usages</strong></h4><p>WebDriverManager can be used as a <a href="https://bonigarcia.dev/webdrivermanager/#webdrivermanager-cli">CLI tool</a>, <a href="https://bonigarcia.dev/webdrivermanager/#webdrivermanager-agent">Java agent</a>, or <a href="https://bonigarcia.dev/webdrivermanager/#webdrivermanager-server">Selenium Server</a>. Take a look at the documentation for further details:</p><p><a href="https://bonigarcia.dev/webdrivermanager/">https://bonigarcia.dev/webdrivermanager/</a></p><h3><strong>Selenium-Jupiter</strong></h3><p>WebDriverManager is the foundation tool of <a href="https://bonigarcia.dev/selenium-jupiter">Selenium-Jupiter</a>, an open-source <a href="https://junit.org/junit5/docs/current/user-guide/">JUnit 5</a> extension for developing Selenium WebDriver tests. Thanks to the parameter resolution provided by JUnit 5, the required boilerplate of a WebDriver test is reduced to the minimum.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9bbcd634ee25b1aa655b4c7efd58b495/href">https://medium.com/media/9bbcd634ee25b1aa655b4c7efd58b495/href</a></iframe><p>Selenium-Jupiter also provides seamless integration with Docker, recordings, VNC, and more. Check out the documentation for the complete reference and examples:</p><p><a href="https://bonigarcia.dev/selenium-jupiter">https://bonigarcia.dev/selenium-jupiter</a></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/983e6a1372a2657e5c8cbbc33a33bd4b/href">https://medium.com/media/983e6a1372a2657e5c8cbbc33a33bd4b/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a0a0f747a35d" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>