<?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>Forem: Jan Kliszcz</title>
    <description>The latest articles on Forem by Jan Kliszcz (@kliszczjan).</description>
    <link>https://forem.com/kliszczjan</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%2F1058477%2F27ff82d8-546d-4e63-8c4f-bdf2313c922d.png</url>
      <title>Forem: Jan Kliszcz</title>
      <link>https://forem.com/kliszczjan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kliszczjan"/>
    <language>en</language>
    <item>
      <title>Effortless Selenium Setups in Docker for .NET with Selenium Manager</title>
      <dc:creator>Jan Kliszcz</dc:creator>
      <pubDate>Sun, 29 Oct 2023 12:24:41 +0000</pubDate>
      <link>https://forem.com/kliszczjan/effortless-selenium-setups-in-docker-for-net-with-selenium-manager-13gl</link>
      <guid>https://forem.com/kliszczjan/effortless-selenium-setups-in-docker-for-net-with-selenium-manager-13gl</guid>
      <description>&lt;p&gt;I've been using Selenium in my projects for automation, web scraping and even for some minor workarounds not related to website navigation at all. However, because I've been usually deploying my .NET apps using Docker containers, I had to overcome some obstacles. &lt;/p&gt;

&lt;p&gt;In this article, I'll share my experiences, from handling dependencies and image sizes to navigating versioning challenges. We'll also explore Selenium Manager's latest features and its game-changing impact on Selenium + Docker + .NET integration. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Important note: This article assumes you are using Selenium with Chrome browser. Not every section of the article is applicable if you are using Firefox or Edge.&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies make the blue whale even bigger!
&lt;/h2&gt;

&lt;p&gt;When we want to build a Docker image running our .NET code, we typically create a Dockerfile that carries out the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy source files to an image with .NET SDK installed&lt;/li&gt;
&lt;li&gt;Restore NuGets and publish the app to a folder&lt;/li&gt;
&lt;li&gt;Copy the output files into an image with .NET runtime installed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we need to perform actions in a web browser through code, it's essential to make sure the browser, its necessary libraries, and the matching WebDriver are available. This can result in significantly larger image sizes in order to fulfil these requirements.&lt;/p&gt;

&lt;p&gt;To demonstrate this, I've prepared three Docker images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bare .NET runtime image tagged with version &lt;code&gt;6.0.22-bookworm-slim&lt;/code&gt; as a base line
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/runtime:6.0.22-bookworm-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;.NET runtime + required dependencies for launching Chrome browser inside container &lt;a href="https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-doesnt-launch-on-linux"&gt;from this list&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/runtime:6.0.22-bookworm-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ca-certificates fonts-liberation libasound2 &lt;span class="se"&gt;\
&lt;/span&gt;  libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 &lt;span class="se"&gt;\
&lt;/span&gt;  libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 &lt;span class="se"&gt;\
&lt;/span&gt;  libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 &lt;span class="se"&gt;\
&lt;/span&gt;  libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils curl &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;.NET runtime + required dependencies + latest stable version of Google Chrome installed
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/runtime:6.0.22-bookworm-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ca-certificates fonts-liberation libasound2 &lt;span class="se"&gt;\
&lt;/span&gt;  libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 &lt;span class="se"&gt;\
&lt;/span&gt;  libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 &lt;span class="se"&gt;\
&lt;/span&gt;  libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 &lt;span class="se"&gt;\
&lt;/span&gt;  libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils curl &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean

&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt;  https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb &lt;span class="se"&gt;\ &lt;/span&gt;
  &amp;amp;&amp;amp; apt-get install --no-install-recommends -y ./google-chrome-stable_current_amd64.deb \
  &amp;amp;&amp;amp; rm google-chrome-stable_current_amd64.deb \
  &amp;amp;&amp;amp; apt-get clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the Dockerfiles were written with size optimization in mind by chaining commands in a single &lt;code&gt;RUN&lt;/code&gt; command in order to reduce the number of layers, installing only the necessary dependencies without any additional recommended packages and cleaning up after install using &lt;code&gt;apt-get clean&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;After building the images, we are going to list them along with their size&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;REPOSITORY                         SIZE
runtime                            194MB
runtime-dependencies               310MB
runtime-dependencies-chrome        667MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the &lt;code&gt;runtime-dependencies&lt;/code&gt; and &lt;code&gt;runtime-dependencies-chrome&lt;/code&gt; images are 1.5x and 3x larger than the base &lt;code&gt;runtime&lt;/code&gt; image, respectively.&lt;/p&gt;

&lt;p&gt;You've probably noticed that there is a step missing in all Dockerfile which would download a ChromeDriver matching the version of installed. I haven't include this step for two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The driver itself weighs about 15-17 MB, so it doesn't have that much impact on the image size&lt;/li&gt;
&lt;li&gt;There are packages providing a feature of automatic driver download during the runtime of application i.e. &lt;a href="https://github.com/rosolko/WebDriverManager.Net"&gt;WebDriverManager.Net&lt;/a&gt; for .NET applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Versioning nightmare
&lt;/h2&gt;

&lt;p&gt;For a long time, developers using Selenium with Chrome browser had no option to download a specific version of the browser from an official source. This could potentially cause some problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you couldn't perform a rollback to the previous Chrome version in case if a critical bug was introduced in a new version, unless you've built a runtime image when the previous version was considered the latest stable and you've applied a sensible image versioning strategy i.e. by not pushing images to the registry with a &lt;code&gt;latest&lt;/code&gt; tag only&lt;/li&gt;
&lt;li&gt;updating the version of .NET means that you are automatically going to install the new version of Chrome if a new version was released in the meantime, which is something that may not be desired&lt;/li&gt;
&lt;li&gt;it's hard to come up with a reasonable versioning format, because now not only you should specify the .NET version and OS used, but also a Chrome version installed. You could also use some custom versioning format, but it would be probably confusing for a typical developer that is used to the tags provided by Microsoft&lt;/li&gt;
&lt;li&gt;you needed also to make sure that the ChromeDriver version supports the currently installed version of Chrome&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the &lt;a href="https://www.selenium.dev/blog/2022/selenium-4-6-0-released/"&gt;release of Selenium &lt;code&gt;4.6.0&lt;/code&gt;&lt;/a&gt;, Selenium team introduced Selenium Manager, which in the beginning provided automated driver management. This feature simplified the process of manually installing and guarding the versions of drivers and browsers, but the Chrome browser itself still didn't provide any versioning.&lt;/p&gt;

&lt;p&gt;On June 12 2023, &lt;a href="https://developer.chrome.com/blog/chrome-for-testing/"&gt;Google announced the Chrome for Testing browser&lt;/a&gt;, a special flavor of Chrome dedicated for automation and testing. What's more important, the team had introduced the versioning of the browser binaries. Thanks to this change, developers could now install a specific version from &lt;code&gt;113.0.5672.0&lt;/code&gt; onward.&lt;/p&gt;

&lt;h2&gt;
  
  
  And then Selenium dropped the bombshell
&lt;/h2&gt;

&lt;p&gt;With the introduction of Selenium &lt;code&gt;4.11.0&lt;/code&gt;, Selenium team announced that &lt;a href="https://www.selenium.dev/blog/2023/whats-new-in-selenium-manager-with-selenium-4.11.0/"&gt;Selenium Manager now includes automated browser management&lt;/a&gt; based on Chrome for Testing. Thanks to this feature, if Selenium doesn't detect the version of the browser requested by the code installed on the machine, it automatically downloads the binaries. As of the time this article was written, version &lt;code&gt;4.12.0&lt;/code&gt; introduced support for Mozilla Firefox and Edge support is planned to be released with version &lt;code&gt;4.13.0&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current status of Selenium + Docker
&lt;/h2&gt;

&lt;p&gt;Let's analyze the benefits that all of these changes have brought us in the context of the issues mentioned earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduced base image size
&lt;/h3&gt;

&lt;p&gt;Since we no longer need to manually install the browser and its driver, we can remove this process from our Dockerfile and leave only the installation of required dependencies. Below is the Dockerfile for .NET runtime image tagged &lt;code&gt;6.0.22-bookworm-slim&lt;/code&gt; that can be safely used with Chrome for Testing and Selenium.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/runtime:6.0.22-bookworm-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ca-certificates fonts-liberation libasound2 &lt;span class="se"&gt;\
&lt;/span&gt;  libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 &lt;span class="se"&gt;\
&lt;/span&gt;  libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 &lt;span class="se"&gt;\
&lt;/span&gt;  libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 &lt;span class="se"&gt;\
&lt;/span&gt;  libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils curl &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Easier .NET version management
&lt;/h3&gt;

&lt;p&gt;We can go one step further and create a more general version of Dockerfile, where you can provide the base image version as a build argument and specify also the default version of the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; dotnet_tag=6.0.22-bookworm-slim&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/runtime:${dotnet_tag}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ca-certificates fonts-liberation libasound2 &lt;span class="se"&gt;\
&lt;/span&gt;  libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 &lt;span class="se"&gt;\
&lt;/span&gt;  libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 &lt;span class="se"&gt;\
&lt;/span&gt;  libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 &lt;span class="se"&gt;\
&lt;/span&gt;  libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils curl &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when building you can specify the runtime image version for building, but also at the same time you can use the same version to tag the new image&lt;br&gt;
&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;DOTNET_TAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;6.0
docker build &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="s2"&gt;"dotnet_tag=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOTNET_TAG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; runtime-chrome:&lt;span class="nv"&gt;$DOTNET_TAG&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Managing browser version from code
&lt;/h3&gt;

&lt;p&gt;Since our runtime image is no longer coupled with the specific version of the browser, we can utilize the new Selenium Manager feature to request any Chrome for Testing browser version from &lt;code&gt;113&lt;/code&gt; onward.&lt;/p&gt;

&lt;p&gt;I've created a simple program to demonstrate how you can request a specific browser version. Notice that we are not providing a specific version at compile time, which means that you can request any supported version of the browser during runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;OpenQA.Selenium&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;OpenQA.Selenium.Chrome&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chromeOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromeOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// remember about the necessary arguments when running inside container! &lt;/span&gt;
&lt;span class="n"&gt;chromeOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddArguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--headless"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"--disable-gpu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"--no-sandbox"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"--disable-dev-shm-usage"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&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="s"&gt;"Provide a version of Chrome browser you want to launch (type 'q' to quit):"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;chromeOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BrowserVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chromeService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChromeDriverService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;chromeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SuppressInitialDiagnosticInformation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChromeDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chromeService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;chromeOptions&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;Manage&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Timeouts&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;ImplicitWait&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&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;Navigate&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;GoToUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://www.whatsmybrowser.org/"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&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;FindElement&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="nf"&gt;TagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"h2"&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="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Quit&lt;/span&gt;&lt;span class="p"&gt;();&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="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&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;p&gt;Next, we are going to publish our program as a Docker image using the Dockerfile below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;#Change the default runtime to our custom base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runtime-chrome:7.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base &lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:7.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ["SeleniumChromeDocker.csproj", "./"]&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet restore &lt;span class="s2"&gt;"SeleniumChromeDocker.csproj"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; "/src/"&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet build &lt;span class="s2"&gt;"SeleniumChromeDocker.csproj"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app/build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;publish&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish &lt;span class="s2"&gt;"SeleniumChromeDocker.csproj"&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app/publish /p:UseAppHost&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;final&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=publish /app/publish .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "SeleniumChromeDocker.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After building, we can run this image in interactive mode and check if we can request any valid version of Chrome for Testing browser&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; selenium-chrome-docker &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; selenium-chrome-docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is an example output. When providing browser version, we can use various different formats such as whole version number, major number or even release channels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run --rm -it selenium-chrome-docker
Provide a version of Chrome browser you want to launch (type 'q' to quit):114
ChromeDriver was started successfully.
You’re using Headless Chrome 114.
Provide a version of Chrome browser you want to launch (type 'q' to quit):115
ChromeDriver was started successfully.
You’re using Headless Chrome 115.
Provide a version of Chrome browser you want to launch (type 'q' to quit):116.0.5800.0 
ChromeDriver was started successfully.
You’re using Headless Chrome 116.
Provide a version of Chrome browser you want to launch (type 'q' to quit):stable
ChromeDriver was started successfully.
You’re using Headless Chrome 118.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In my opinion, Selenium Manager is a great tool that will vastly improve the dev experience when working with Selenium. Thanks to this tool, working with web browsers has become less problematic and the application is easier to containerize. I'm also glad that Google has finally introduced more accessible version management with the release of their new Chrome flavor.&lt;/p&gt;

&lt;p&gt;If you want to test this integration on your own, checkout my &lt;a href="https://github.com/janex-PL/DotnetSeleniumDockerRuntimeExample/"&gt;GitHub example repository&lt;/a&gt; where I've gathered all source code used in this article.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>programming</category>
      <category>docker</category>
      <category>selenium</category>
    </item>
  </channel>
</rss>
