<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rafał Garbowski</title>
    <description>The latest articles on DEV Community by Rafał Garbowski (@rg9).</description>
    <link>https://dev.to/rg9</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F939488%2Fee23ff9b-c636-4ba3-99cd-f8078548e08b.jpg</url>
      <title>DEV Community: Rafał Garbowski</title>
      <link>https://dev.to/rg9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rg9"/>
    <language>en</language>
    <item>
      <title>Automate copying text from web browser using Bookmarklet or Tampermonkey</title>
      <dc:creator>Rafał Garbowski</dc:creator>
      <pubDate>Sat, 11 Jul 2026 14:59:40 +0000</pubDate>
      <link>https://dev.to/rg9/automate-copying-text-from-web-browser-using-bookmarklet-or-tampermonkey-3l9f</link>
      <guid>https://dev.to/rg9/automate-copying-text-from-web-browser-using-bookmarklet-or-tampermonkey-3l9f</guid>
      <description>&lt;p&gt;We all have those tiny repetitive tasks that take only a few seconds but somehow add up over time.&lt;/p&gt;

&lt;p&gt;For me, one of them is &lt;strong&gt;copying GitHub issues into my Obsidian notes&lt;/strong&gt;. I usually want the following Markdown format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Issue title&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github.com/org/repo/issues/123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doing this manually means selecting the title, copying it, copying the URL, and formatting everything. Not difficult—just repetitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: Bookmarklet
&lt;/h2&gt;

&lt;p&gt;A bookmarklet is simply a browser bookmark that &lt;strong&gt;runs JavaScript instead of opening a webpage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Simply select from main menu: &lt;code&gt;Bookmarks -&amp;gt; Manage Bookmarks (Ctrl+Shit+O)&lt;/code&gt;. &lt;br&gt;
Then right click on &lt;code&gt;Bookmars Toolbar&lt;/code&gt; and add new bookmark titled, e.g. "Copy to Obsidian", in URL paste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bdi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
             &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/ · .*$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;](&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click the bookmark in toolbar while viewing a GitHub issue or pull request, and the formatted Markdown link is immediately copied to your clipboard.&lt;/p&gt;

&lt;p&gt;The nice thing is that it works even for private repositories because it operates on the page you've already opened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: Tampermonkey
&lt;/h2&gt;

&lt;p&gt;Using Tampermonkey, you can bind the action to a keyboard shortcut.&lt;/p&gt;

&lt;p&gt;Here is full script that works for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ==UserScript==&lt;/span&gt;
&lt;span class="c1"&gt;// @name         GitHub -&amp;gt; Obsidian Task&lt;/span&gt;
&lt;span class="c1"&gt;// @namespace    obsidian&lt;/span&gt;
&lt;span class="c1"&gt;// @version      1.0&lt;/span&gt;
&lt;span class="c1"&gt;// @match        https://github.com/*/*/issues/*&lt;/span&gt;
&lt;span class="c1"&gt;// @match        https://github.com/*/*/pull/*&lt;/span&gt;
&lt;span class="c1"&gt;// @grant        GM_setClipboard&lt;/span&gt;
&lt;span class="c1"&gt;// ==/UserScript==&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getTitle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bdi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;copyTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTitle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Couldn't find issue title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;GM_setClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;](&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctrlKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;altKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;copyTask&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;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now pressing &lt;code&gt;Ctrl+Alt+C&lt;/code&gt; copies exactly what I need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Prefer Tampermonkey
&lt;/h2&gt;

&lt;p&gt;I like bookmarklets because they're simple and require no extensions.&lt;/p&gt;

&lt;p&gt;However, I prefer Tampermonkey for small personal automations because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;scripts are easier to organize and maintain,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;they can be limited to specific websites,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;adding shortcuts or UI elements is straightforward,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;they can grow with your workflow without becoming browser bookmark clutter.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing I particularly appreciate is that &lt;strong&gt;Tampermonkey is a Recommended Extension in Firefox&lt;/strong&gt;. Mozilla reviews recommended extensions for security, code quality, and privacy, which gives me more confidence than installing a random browser add-on.&lt;/p&gt;

</description>
      <category>tampermonkey</category>
    </item>
    <item>
      <title>OCP 17 Exam passed!</title>
      <dc:creator>Rafał Garbowski</dc:creator>
      <pubDate>Sat, 11 Jul 2026 14:49:04 +0000</pubDate>
      <link>https://dev.to/rg9/ocp-17-exam-passed-53d2</link>
      <guid>https://dev.to/rg9/ocp-17-exam-passed-53d2</guid>
      <description>&lt;p&gt;I finally passed the OCP 17 exam! 🎉  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://catalog-education.oracle.com/ords/certview/sharebadge?id=6FE8423305029BEC4534F7D103A31CB36181C70513ABDDABE857C1F201900972" rel="noopener noreferrer"&gt;Link to badge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It took me about 1.5 years from start to finish, with plenty of shorter and longer breaks along the way. 😉  &lt;/p&gt;

&lt;p&gt;Looking back, I think I could have spent less time reading the OCP Study Guide and more time solving practice questions. However, one of my motivations for reading the book was to start an OCP book club at work.  &lt;/p&gt;

&lt;p&gt;I didn't complete all the practice questions, and my mock exam results weren't great. Still, it was enough to pass. In my opinion, the mock exams were more difficult than the real exam.  &lt;/p&gt;

&lt;p&gt;I also had two stressful moments on exam day.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Just before the exam, Windows decided to install three updates in a row, each requiring a restart. 😅 I was worried I'd be late.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;During the exam, my webcam suddenly stopped working. It was probably a glitch in the exam software because the camera shutter was open, but the indicator light was off. I lost about five minutes explaining the situation to the proctor and was afraid I might lose my exam attempt.  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, everything worked out, and I passed! 🚀&lt;/p&gt;

</description>
      <category>ocp17</category>
    </item>
    <item>
      <title>What Solving Hundreds of OCP Questions Taught Me</title>
      <dc:creator>Rafał Garbowski</dc:creator>
      <pubDate>Sat, 11 Jul 2026 14:48:40 +0000</pubDate>
      <link>https://dev.to/rg9/what-solving-hundreds-of-ocp-questions-taught-me-1691</link>
      <guid>https://dev.to/rg9/what-solving-hundreds-of-ocp-questions-taught-me-1691</guid>
      <description>&lt;h2&gt;
  
  
  🧠 1. Think Like the Compiler, Not Like a Developer
&lt;/h2&gt;

&lt;p&gt;As developers, we naturally focus on business logic.&lt;/p&gt;

&lt;p&gt;The compiler doesn't.&lt;/p&gt;

&lt;p&gt;It doesn't care that your algorithm is correct if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a variable is declared twice,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a local variable isn't initialized,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you're calling an instance method from a static context,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;an abstract class is instantiated,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;an overridden method throws a broader checked exception.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some examples that caught me during preparation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// duplicate local variable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;      &lt;span class="c1"&gt;// print() isn't static&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Animal&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;     &lt;span class="c1"&gt;// doesn't compile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One habit helped me a lot:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Before trying to predict the output, always ask: "Does it compile?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🚫 2. Compilation Always Comes Before Runtime
&lt;/h2&gt;

&lt;p&gt;This sounds obvious.&lt;/p&gt;

&lt;p&gt;Yet I repeatedly made the same mistake.&lt;/p&gt;

&lt;p&gt;I'd spend a minute calculating the output only to discover that the code didn't even compile.&lt;/p&gt;

&lt;p&gt;Sometimes I'd immediately think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This throws an exception."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Only to notice later that there was already a compilation error a few lines above.&lt;/p&gt;

&lt;p&gt;The OCP exam loves hiding compilation errors inside otherwise perfectly valid code.&lt;/p&gt;




&lt;h2&gt;
  
  
  👀 3. Read Every Single Word
&lt;/h2&gt;

&lt;p&gt;Some of my mistakes had nothing to do with Java.&lt;/p&gt;

&lt;p&gt;I simply didn't read the question carefully enough.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Choose &lt;strong&gt;two&lt;/strong&gt; answers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which statement is &lt;strong&gt;NOT&lt;/strong&gt; true?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which methods can be removed so the code &lt;strong&gt;compiles and runs&lt;/strong&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which answer is &lt;strong&gt;always&lt;/strong&gt; correct?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also remember selecting a perfectly valid answer...&lt;/p&gt;

&lt;p&gt;...only to notice afterwards that the question asked for &lt;strong&gt;"Compiles and runs"&lt;/strong&gt;, not &lt;strong&gt;"Produces the same output."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One word changed everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎭 4. The Exam Loves "Almost Correct" Code
&lt;/h2&gt;

&lt;p&gt;This is probably one of Oracle's favorite tricks.&lt;/p&gt;

&lt;p&gt;Everything looks correct except for one tiny detail.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;     &lt;span class="c1"&gt;// missing default&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// instance fields are not allowed&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// broader checked exception&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code looks almost perfect.&lt;/p&gt;

&lt;p&gt;That's exactly why it's easy to miss.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 5. Don't Trust Familiar APIs
&lt;/h2&gt;

&lt;p&gt;Many APIs on the exam look familiar.&lt;/p&gt;

&lt;p&gt;That's the trap.&lt;/p&gt;

&lt;p&gt;Examples I remember getting wrong:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;confusing &lt;code&gt;peek()&lt;/code&gt; with &lt;code&gt;poll()&lt;/code&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;using a class that doesn't actually exist in &lt;code&gt;java.time&lt;/code&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;forgetting that &lt;code&gt;Stream.min()&lt;/code&gt; returns &lt;code&gt;Optional&amp;lt;T&amp;gt;&lt;/code&gt;, while &lt;code&gt;IntStream.min()&lt;/code&gt; returns &lt;code&gt;OptionalInt&lt;/code&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;mixing up &lt;code&gt;Period&lt;/code&gt; and &lt;code&gt;Duration&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever I wasn't 100% sure an API existed, I stopped guessing.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ 6. Pay Attention to Words Like "Always", "Must", and "Never"
&lt;/h2&gt;

&lt;p&gt;These words completely change the meaning of a sentence.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;constructors &lt;strong&gt;must&lt;/strong&gt; start with &lt;code&gt;this()&lt;/code&gt; or &lt;code&gt;super()&lt;/code&gt;? (only if they're explicitly called)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;default&lt;/code&gt; is &lt;strong&gt;always&lt;/strong&gt; required? (not necessarily)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a method &lt;strong&gt;must never&lt;/strong&gt; throw an unchecked exception when overriding? (false)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One word often decides whether the answer is correct.&lt;/p&gt;




&lt;h2&gt;
  
  
  ❌ 7. Eliminate Wrong Answers First
&lt;/h2&gt;

&lt;p&gt;One of the biggest improvements in my exam strategy was learning to eliminate answers before searching for the correct one.&lt;/p&gt;

&lt;p&gt;Sometimes I couldn't immediately identify the right option.&lt;/p&gt;

&lt;p&gt;But I could confidently reject others because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;the code doesn't compile,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the API doesn't exist,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the modifier isn't legal,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the exception cannot occur.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was often enough to find the correct answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 8. Small Language Rules Matter More Than You Think
&lt;/h2&gt;

&lt;p&gt;The OCP exam rewards precision.&lt;/p&gt;

&lt;p&gt;Some examples that repeatedly appeared during my preparation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Duration&lt;/code&gt; vs &lt;code&gt;Period&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Month.MARCH&lt;/code&gt; vs &lt;code&gt;3&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;List.of()&lt;/code&gt; returns an immutable list&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;PriorityQueue&lt;/code&gt; always removes the smallest element&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;switch&lt;/code&gt; case labels must be compile-time constants&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;try-with-resources&lt;/code&gt; always closes resources before entering &lt;code&gt;catch&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these rules are difficult.&lt;/p&gt;

&lt;p&gt;The challenge is remembering them all under time pressure.&lt;/p&gt;




&lt;h2&gt;
  
  
  😴 9. Fatigue Creates More Mistakes Than Lack of Knowledge
&lt;/h2&gt;

&lt;p&gt;This was one of the biggest surprises.&lt;/p&gt;

&lt;p&gt;After solving many questions in a row, my error rate increased—not because the questions became harder, but because I became less careful.&lt;/p&gt;

&lt;p&gt;Typical mistakes included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;missing &lt;strong&gt;Choose three answers&lt;/strong&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;stopping halfway through the code,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;assuming indentation affected execution,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;overlooking a single &lt;code&gt;final&lt;/code&gt; keyword,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;forgetting to check every answer because I thought I had already found the correct one&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes taking a five-minute break was more valuable than solving another twenty questions.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 10. Every Question Exists to Test One Rule
&lt;/h2&gt;

&lt;p&gt;Eventually, I stopped asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What's the correct answer?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, I started asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What rule is Oracle trying to test?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That small shift completely changed the way I studied.&lt;/p&gt;

&lt;p&gt;Instead of memorizing questions, I focused on understanding the language rules behind them.&lt;/p&gt;

&lt;p&gt;Once I understood the rule, similar questions became much easier.&lt;/p&gt;




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

&lt;p&gt;Preparing for the OCP exam taught me something unexpected.&lt;/p&gt;

&lt;p&gt;The hardest part wasn't learning more Java.&lt;/p&gt;

&lt;p&gt;It was learning to slow down.&lt;/p&gt;

&lt;p&gt;To &lt;strong&gt;read carefully&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To think like the compiler before thinking like a developer.&lt;/p&gt;

&lt;p&gt;Ironically, the more experience you have, the easier it is to make assumptions because your brain automatically fills in missing details.&lt;/p&gt;

&lt;p&gt;If I could give one piece of advice to anyone preparing for the OCP exam, it would be this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Slow down. Read every line. Think like the compiler. Only then think like a programmer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ocp17</category>
    </item>
    <item>
      <title>Why I'm Taking the OCP Java 17 Exam in 2026</title>
      <dc:creator>Rafał Garbowski</dc:creator>
      <pubDate>Sat, 11 Jul 2026 14:44:39 +0000</pubDate>
      <link>https://dev.to/rg9/why-im-taking-the-ocp-java-17-exam-in-2026-1cbp</link>
      <guid>https://dev.to/rg9/why-im-taking-the-ocp-java-17-exam-in-2026-1cbp</guid>
      <description>&lt;p&gt;Last year I spent a lot of time reading the OCP study guide. I don't want to leave it unfinished, so my goal for 2026 is to finally pass the exam.&lt;/p&gt;

&lt;p&gt;These are my reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 I want to finish what I started
&lt;/h2&gt;

&lt;p&gt;I already spent many hours reading the book and solving questions. It would be nice to finish this journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧱 I want to organize my Java knowledge
&lt;/h2&gt;

&lt;p&gt;The exam covers many Java topics. Some of them I use every day, some I don't.&lt;/p&gt;

&lt;p&gt;Preparing for the exam helps me refresh what I already know and learn things I have forgotten.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 I want the satisfaction
&lt;/h2&gt;

&lt;p&gt;Passing the exam won't magically make me a better developer.&lt;/p&gt;

&lt;p&gt;But I know I'll feel proud after passing it. It is also a nice milestone in my Java journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  📈 It may help my career
&lt;/h2&gt;

&lt;p&gt;The job market is not as easy as it was a few years ago.&lt;/p&gt;

&lt;p&gt;Having an OCP certification won't guarantee a new job or a promotion, but it can help your profile stand out. In a competitive market, every advantage counts.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤯 It teaches you to read code carefully
&lt;/h2&gt;

&lt;p&gt;The part I like the least is remembering small details from the Java API.&lt;/p&gt;

&lt;p&gt;But after solving many practice questions, I think the exam is more about reading code carefully than memorizing everything.&lt;/p&gt;

&lt;p&gt;With AI writing more code every day, being able to understand and review code is still an important skill.&lt;/p&gt;

&lt;h2&gt;
  
  
  💪 It helps me practice focus
&lt;/h2&gt;

&lt;p&gt;After work, I often lose concentration after 4 or 5 PM.&lt;/p&gt;

&lt;p&gt;Preparing for the OCP is also a way to practice staying focused for an hour and carefully reading code.&lt;/p&gt;




&lt;p&gt;These are my reasons for taking the OCP in 2026.&lt;/p&gt;

&lt;p&gt;Maybe they will also convince you to give it a try.&lt;/p&gt;

</description>
      <category>ocp17</category>
    </item>
    <item>
      <title>AssertJ custom representation of asserted object</title>
      <dc:creator>Rafał Garbowski</dc:creator>
      <pubDate>Sat, 17 Jun 2023 10:13:38 +0000</pubDate>
      <link>https://dev.to/rg9/assertj-custom-representation-of-asserted-object-n2j</link>
      <guid>https://dev.to/rg9/assertj-custom-representation-of-asserted-object-n2j</guid>
      <description>&lt;p&gt;I really enjoy asserting collections with AssertJ.&lt;br&gt;
Usually it's safer and simpler to use &lt;code&gt;containsExactlyInAnyOrder&lt;/code&gt; rather than asserting individual elements (&lt;code&gt;collection.get(0)&lt;/code&gt;) - even if there is only one element, because it may change in the future.&lt;br&gt;
The challenge starts when asserting collection containing complex objects, because constructing an entire object as expected element can be tedious.&lt;br&gt;
To extract only certain properties, we can use &lt;code&gt;.extracting(Foo::field1, Foo::field2)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Player&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Michael Jordan"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Team&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bulls"&lt;/span&gt;&lt;span class="o"&gt;)),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Player&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Kobe Bryant"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Team&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Lakers"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;

        &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extracting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Player:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;team&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsExactly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Michael Jordan"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Bulls"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Kobe Bryant"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Lakers"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, I tend to concatenate properties to string because I wasn't fond of working with tuples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;        &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extracting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" | "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;team&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsExactly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Michael Jordan | Bulls"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Kobe Bryant | Lakers"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason is that, by default, AssertJ provides a generic "tuple" representation in the "actual" section.&lt;br&gt;
When copied, this has to be manually adapted to Java code, which can be inconvenient.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expecting actual:
  [("Michael Jordan", "Bulls"),
    ("Kobe Bryant" "Lakers")]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I want is an "easy-to-copy" representation of the asserted object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expecting actual:
  [tuple("Michael Jordan", "Bulls"),
    tuple("Kobe Bryant" "Lakers")]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fortunately, there's an easy way to globally fix this in 3 simple steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define a custom representation:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomAssertJRepresentation&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;StandardRepresentation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CustomAssertJRepresentation&lt;/span&gt; &lt;span class="no"&gt;INSTANCE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomAssertJRepresentation&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toStringOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tuple&lt;/span&gt; &lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"tuple"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toStringOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tuple&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Then add it to the global configuration:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomAssertJConfiguration&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Configuration&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Representation&lt;/span&gt; &lt;span class="nf"&gt;representation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;CustomAssertJRepresentation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"CustomAssertJConfiguration applied"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Lastly, register the global config in this file:
&lt;code&gt;/src/test/resources/META-INF/services/org.assertj.core.configuration.Configuration&lt;/code&gt;
which will contain:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my.package.CustomAssertJConfiguration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refer to the official documentation for more information: &lt;a href="https://assertj.github.io/doc/#assertj-core-representation" rel="noopener noreferrer"&gt;https://assertj.github.io/doc/#assertj-core-representation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>assertj</category>
      <category>java</category>
      <category>dx</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Introducing safe-eyes-cli: strict break reminder based on YAD</title>
      <dc:creator>Rafał Garbowski</dc:creator>
      <pubDate>Sat, 08 Apr 2023 16:33:07 +0000</pubDate>
      <link>https://dev.to/rg9/introducing-safe-eyes-cli-protect-your-eyes-while-coding-on-linux-40b6</link>
      <guid>https://dev.to/rg9/introducing-safe-eyes-cli-protect-your-eyes-while-coding-on-linux-40b6</guid>
      <description>&lt;p&gt;I'm excited to share with you a new tool that I've been using to help protect my eyes while working on my computer. It's called &lt;code&gt;safe-eyes-cli&lt;/code&gt; and it's inspired by the popular Safe Eyes application.&lt;/p&gt;

&lt;p&gt;It's a "strict" break reminder based on &lt;a href="https://github.com/v1cont/yad" rel="noopener noreferrer"&gt;YAD&lt;/a&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A "strict break reminder program" is a software application designed to remind users to take regular breaks from their computer work. It enforces strict rules for breaks, such as locking the screen, displaying full-screen reminders, or disabling input devices during the break period. This ensures that users take breaks and rest their eyes, helping to prevent eye strain, repetitive stress injuries, and promoting overall wellbeing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The tool is &lt;strong&gt;customizable&lt;/strong&gt;, allowing to set the frequency and duration of breaks.&lt;/p&gt;

&lt;p&gt;One of the great things about &lt;code&gt;safe-eyes-cli&lt;/code&gt;  is &lt;strong&gt;simplicity&lt;/strong&gt;. It's a shell script, that anyone can easily modify and adapt for special needs. Another killer feature is that it can &lt;strong&gt;automatically pause during calls&lt;/strong&gt;, so you don't have to worry about interruptions while you're on an important call.&lt;/p&gt;

&lt;p&gt;If you're interested in trying out &lt;code&gt;safe-eyes-cli&lt;/code&gt; for yourself, you can find it on GitHub at &lt;a href="https://github.com/RG9/safe-eyes-cli" rel="noopener noreferrer"&gt;https://github.com/RG9/safe-eyes-cli&lt;/a&gt;. The installation instructions are straightforward and easy to follow.&lt;/p&gt;

&lt;p&gt;Let me know if you have any questions or feedback!&lt;/p&gt;

</description>
      <category>breakreminder</category>
      <category>eyeshealth</category>
      <category>linux</category>
      <category>bash</category>
    </item>
    <item>
      <title>Three ways to configure HTTP Proxy in Playwright</title>
      <dc:creator>Rafał Garbowski</dc:creator>
      <pubDate>Thu, 23 Mar 2023 23:26:40 +0000</pubDate>
      <link>https://dev.to/rg9/three-ways-to-configure-http-proxy-in-playwright-23n</link>
      <guid>https://dev.to/rg9/three-ways-to-configure-http-proxy-in-playwright-23n</guid>
      <description>&lt;p&gt;TL;DR&lt;/p&gt;

&lt;p&gt;In fact, in Playwright &lt;code&gt;v.1.31.1&lt;/code&gt; there are &lt;strong&gt;three&lt;/strong&gt; ways to configure an HTTP proxy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chromium.launch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;browser.newContext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page.setExtraHTTPHeaders&lt;/code&gt;, but works only on Firefox&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Today I was struggling with proxy setup for Browserless ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.browserless.io/docs/using-a-proxy" rel="noopener noreferrer"&gt;Official docs&lt;/a&gt; says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Both browserless, and Chrome itself, support the usage of external proxies. In order to fully utilize a 3rd-party proxy you'll need to do two things:
&lt;span class="p"&gt;
-&lt;/span&gt;   Specify the address of where the proxy is with the &lt;span class="sb"&gt;`--proxy-server`&lt;/span&gt; switch.
&lt;span class="p"&gt;-&lt;/span&gt;   Optionally, you'll also need to send in your username and password if the proxy is authenticated.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, so I need to set &lt;code&gt;--proxy-server&lt;/code&gt;. I was using a Docker image to run Browserless locally. I discovered that there is an environment variable called &lt;code&gt;PROXY_URL&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;podman run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"PROXY_URL=my-proxy-url"&lt;/span&gt; browserless/chrome:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fine, but how do I pass on the username and password?&lt;br&gt;
Docs say there are two methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Using username and password&lt;/span&gt;
&lt;span class="gu"&gt;### Method 1: page.authenticate&lt;/span&gt;
&lt;span class="gu"&gt;### Method 2: page.setExtraHTTPHeaders&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm using &lt;code&gt;playwright-java&lt;/code&gt; and there is no method &lt;code&gt;page.authenticate&lt;/code&gt;, so &lt;code&gt;page.setExtraHTTPHeaders&lt;/code&gt; left, I thought.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setExtraHTTPHeaders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Proxy-Authorization"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Basic "&lt;/span&gt;  
   &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEncoder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"username:password"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;()))));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, it does not work. I received the following strange error: &lt;code&gt;net::ERR_INVALID_ARGUMENT&lt;/code&gt;. Besides there was no errors in container's logs. What's the hack?&lt;/p&gt;

&lt;p&gt;Google's top two results for &lt;code&gt;playwright Proxy-Authorization&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/67478486/how-do-i-authenticate-a-proxy-in-playwright" rel="noopener noreferrer"&gt;node.js - How do I authenticate a proxy in playwright - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/playwright-python/issues/443" rel="noopener noreferrer"&gt;Proxy-Authorization header not working in Chromium · Issue #443 · microsoft/playwright-python · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I quickly checked the first link. The answer says that there are &lt;strong&gt;two&lt;/strong&gt; ways. I didn't read further, because I thought that I know these ways - one is method I'm struggling with and second &lt;code&gt;chromium.launch&lt;/code&gt;, right? :) &lt;em&gt;Later it turned out that I was wrong!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The second link looks like what I need. However issue was closed without any conclusion. The alternative shown, was one that I already knew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;playwright&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost:8080&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="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;The case was that I didn't want to &lt;code&gt;lanuch&lt;/code&gt; browser, but rather &lt;code&gt;connect&lt;/code&gt; to one created inside container.&lt;/p&gt;

&lt;p&gt;I dug even deeper and found another similar issue in Playwright project:&lt;br&gt;
&lt;a href="https://github.com/microsoft/playwright/issues/11967" rel="noopener noreferrer"&gt;# [Feature] Directly set Proxy-Authorization in Chrome without configuring any other proxy settings&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The problem (in Chrome/Chromium anyway) is that &lt;code&gt;Proxy-Authorization&lt;/code&gt; may not be set explicitly via &lt;code&gt;extraHTTPHeaders&lt;/code&gt;. If you do this and then try to navigate to any page Chrome will give you the following error: &lt;code&gt;net::ERR_INVALID_ARGUMENT&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was lost, considering changing my approach...&lt;/p&gt;

&lt;p&gt;Fortunately, my teammate assisted me by showing some examples of where I found a missing piece:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NewContextOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NewContextOptions&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;  
   &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProxy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-proxy-url"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUsername&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;  
&lt;span class="nc"&gt;BrowserContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ta da! As simple as that! 😃&lt;/p&gt;

&lt;p&gt;I checked the &lt;a href="https://playwright.dev/docs/network#http-proxy" rel="noopener noreferrer"&gt;playwright docs&lt;/a&gt; and it appears to be possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Proxy can be either set globally for the entire browser, or for each browser context individually.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unluckily there is no example, so it's hard to spot.&lt;/p&gt;

&lt;p&gt;I also double-checked &lt;a href="https://stackoverflow.com/questions/67478486/how-do-i-authenticate-a-proxy-in-playwright" rel="noopener noreferrer"&gt;node.js - How do I authenticate a proxy in playwright - Stack Overflow&lt;/a&gt; and configuring proxy via &lt;code&gt;browser.newContext&lt;/code&gt; was actually there, but I missed it (confused by Browserless docs?):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;per-context&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://myproxy.com:3128&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Getting to the point: &lt;br&gt;
&lt;em&gt;Festina lente&lt;/em&gt; ("hurry slowly") and read comprehension!&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>browserless</category>
      <category>java</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>JUnit's @CsvSource.quoteCharacter</title>
      <dc:creator>Rafał Garbowski</dc:creator>
      <pubDate>Sat, 18 Feb 2023 16:40:14 +0000</pubDate>
      <link>https://dev.to/rg9/junits-csvsourcequotecharacter-4p0d</link>
      <guid>https://dev.to/rg9/junits-csvsourcequotecharacter-4p0d</guid>
      <description>&lt;p&gt;&lt;code&gt;@CsvSource&lt;/code&gt; used with &lt;strong&gt;text blocks&lt;/strong&gt; is really awesome! Similarly to &lt;a href="https://github.com/cronn/validation-file-assertions" rel="noopener noreferrer"&gt;validation files&lt;/a&gt;, it can be used to create easily readable tests with readily visible inputs and outputs placed side by side.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@ParameterizedTest&lt;/span&gt;
    &lt;span class="nd"&gt;@CsvSource&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delimiterString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-&amp;gt;"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""
        ABC -&amp;gt; abc
        Abc -&amp;gt; abc
        """&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;toLowercase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What recently confused me was the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org.junit.jupiter.api.extension.ParameterResolutionException:
No ParameterResolver registered for parameter [java.lang.String arg1] in method [void MyTest.test(java.lang.String,java.lang.String)].
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It came out that it was caused by a parameter value starting with a single quote &lt;code&gt;'&lt;/code&gt;, that wasn't closed by another &lt;code&gt;'&lt;/code&gt; at the end of value. In my case the culprit was &lt;code&gt;'S-GRAVENHAGE&lt;/code&gt;, which is Belgian street name.&lt;/p&gt;

&lt;p&gt;The solution is to set parameter &lt;code&gt;quoteCharacter&lt;/code&gt; to double quote &lt;code&gt;"&lt;/code&gt;, provided we are using &lt;em&gt;text block&lt;/em&gt;. This way we can test empty string with &lt;code&gt;""&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@ParameterizedTest&lt;/span&gt;
    &lt;span class="nd"&gt;@CsvSource&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quoteCharacter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'\"'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delimiterString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-&amp;gt;"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;textBlock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""
        'S-GRAVENHAGE -&amp;gt; 's-gravenhage
        "" -&amp;gt; ""
        """&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;toLowercase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope it was helpful, cheers!&lt;/p&gt;

</description>
      <category>junit</category>
      <category>java</category>
      <category>todayilearned</category>
    </item>
  </channel>
</rss>
